unix xv6中系统调用的执行顺序

q3qa4bjr  于 2023-03-22  发布在  Unix
关注(0)|答案(2)|浏览(155)

我们在使用系统调用的时候(在用户级),从来不放sys_前缀,但是为什么我们在调用系统调用函数的时候,首先调用的是系统调用处理函数(前缀为sys_),这里我们调用的是sleep函数,但是它进入了sys_sleep()函数,然后进入了主sleep()函数呢:例如在用户级程序中:

int main(int argc,char* argv[])
{  
    sleep(2);
    return 0
}

这里我们调用了sleep函数,但是它先进入**sys_sleep()**函数,然后进入主sleep函数。

int
sys_sleep(void)
{
  int n;
  uint ticks0;

  if(argint(0, &n) < 0)
    return -1;
  acquire(&tickslock);
  ticks0 = ticks;
  while(ticks - ticks0 < n){
    if(myproc()->killed){
      release(&tickslock);
      return -1;
    }
    sleep(&ticks, &tickslock); //Here the main sleep() function is called.
  }
  release(&tickslock);
  return 0;
}

我们没有在*用户级程序*中编写callsys_sleep(),但是它首先进入那里,然后调用主**sleep()**函数。

b1uwtaje

b1uwtaje1#

sleep()函数是一个标准库函数,通常在unistd. h中找到。标准库中的大多数函数都是通过提供链接到可执行文件的共享库来实现的。此共享库中的函数将使用系统调用来请求操作系统内核提供与您调用的函数相关的服务。
函数本身的名字经常用宏来掩盖。

#define sleep sys_sleep

您正在从代码中调用sleep,但编译器会将每次出现的sleep替换为sys_sleep。共享库本身并不是系统调用。共享库是一个 Package 器,用于简化系统调用并模块化编译器。只要您不调用共享库并将其与代码链接,编译器就不会干涉系统调用。当您这样做时,共享库包含以下代码

mov rax, SYSCALL_NUMBER
syscall

第一条指令将syscall号放入一个寄存器中并执行syscall,这使得处理器在 Boot 时跳转到OS设置的syscall处理程序。处理程序会查看rax并打开值以确定它正在处理的syscall。为了帮助您理解,我做了一个小Linux示例
创建以下3个文件:
main.c

#include "shared.h"

int main(int argc, char* argv[]){
    print("hello\n");
}

shared.c

#include <stdio.h>
#include "shared.h"

void sys_print(char* str){
    printf(str);
}

shared.h

#ifndef SHARED
#define SHARED
#define print sys_print

void sys_print(char* str);

#endif

现在运行以下命令:

gcc -g -fPIC -shared -o libshared.so shared.c
gcc -g main.c -omain -I. -L. -lshared
export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH
gdb -ex start --args ./main

您应看到类似以下内容:

Reading symbols from ./main...
Temporary breakpoint 1 at 0x115c: file main.c, line 4.
Starting program: /home/user/main 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe0f8) at main.c:4
4       print("hello\n");
(gdb)

现在,您可以键入layout asm并按Enter键,以便在单步执行时查看程序集。键入si,然后重复按Enter键,以获得单步执行指令。您将看到,您获得的结果与睡眠时类似()系统调用。这正是标准库实现所做的事情,只是共享库将与小的程序集片段(如包含指令syscall

kcwpcxri

kcwpcxri2#

sleepuser.h中被声明
sleepusys.S定义
让我们看看:

SYSCALL(sleep)

将会在

.globl sleep; 
  sleep: 
    movl $SYS_ ## sleep, %eax; 
    int $T_SYSCALL; 
    ret

发生了什么事?
当调用(用户)函数sleep

  • 寄存器%eax设置为SYS_sleep(其值为13(参见syscall.h),SYS_sleepsysproc.c中定义
  • 中断T_SYSCALL(64参见traps.h)升高。
  • 系统调用返回后,sleep也返回。

sys_sleep调用的sleep定义位于proc.c中,只能从内核模式访问

相关问题