系统调用:sys_exit()、SYS_exit 和 exit() 之间的区别



SYS_exit、sys_exit() 和 exit() 有什么区别?

我的理解:

  • Linux 内核提供系统调用,这些调用在 man 2 syscalls 中列出。
  • 这些系统调用的包装器函数由glibc提供,这些系统调用的名称大多与系统调用相似。

我的问题:例如,在man 2 syscalls中,没有提到SYS_exit和sys_exit()。它们是什么?

注意:这里的系统调用exit只是一个示例。我的问题是:什么是SYS_xxx和sys_xxx()?

我将像您的示例中一样使用 exit(),尽管这适用于所有系统调用。

形式 sys_exit() 的函数是内核例程的实际入口点,该例程实现您认为为 exit() 的函数。这些符号甚至不适用于用户模式程序员。也就是说,除非您正在破解内核,否则您无法链接到这些函数,因为它们的符号在内核之外不可用。如果我写的libmsw.a有一个文件范围函数,比如

static int msw_func() {}

在其中定义,您将无法成功尝试链接到它,因为它未导出到 libmsw 符号表中;也就是说:

cc your_program.c libmsw.a

会产生如下错误:

ld: cannot resolve symbol msw_func

因为它没有导出;这同样适用于内核中包含的sys_exit()。

为了使用户程序进入内核例程, syscall(2) 接口需要用于实现从用户模式到内核模式的切换。当该模式切换(有时称为陷阱)发生时,将使用一个小整数在将整数映射到内核函数的内核表中查找正确的内核例程。表中的条目具有以下形式

{SYS_exit, sys_exit},

其中SYS_exit是一个预处理器宏,它是

#define SYS_exit (1)

并且从你出生之前就已经是 1,因为没有理由改变它。它也恰好是系统调用表中的第一个条目,这使得查找成为一个简单的数组索引。

正如您在问题中指出的,常规用户模式程序访问sys_exit的正确方法是通过 glibc(或类似核心库)中的精简包装器。你需要弄乱SYS_exit或sys_exit的唯一原因是你是否正在编写内核代码。

这现在在man syscall本身中得到了解决,

粗略地说,属于/usr/include/asm/unistd.h中定义编号__NR_xxx的系统调用的代码可以在例程sys_xxx()的Linux内核源代码中找到。 (i386 的调度表可在 /usr/src/linux/arch/i386/kernel/entry.S 中找到。 然而,也有许多例外,主要是因为较旧的系统调用被较新的系统调用所取代,并且这在某种程度上被系统地处理了。 在具有专有操作系统仿真的平台上,例如 parisc、sparc、sparc64 和 alpha,还有许多其他系统调用;MIPs64 还包含一整套 32 位系统调用。

至少现在/usr/include/asm/unistd.h是一个链接到其中任何一个的预处理器黑客,

  • /usr/include/asm/unistd_32.h
  • /usr/include/asm/unistd_x32.h
  • /usr/include/asm/unistd_64.h

C 函数exit()stdlib.h 中定义。可以将其视为一个高级事件驱动接口,它允许您向atexit()

/* Call all functions registered with `atexit' and `on_exit',
   in the reverse of the order in which they were registered,
   perform stdio cleanup, and terminate program execution with STATUS.  */
extern void exit (int __status) __THROW __attribute__ ((__noreturn__));

所以本质上内核提供了一个叫做__NR_xxx的接口(C符号)。传统上,人们想要sys_exit()它由预处理器宏SYS_exit定义。此宏创建sys_exit()函数。exit()函数是标准 C 库stdlib.h的一部分,并移植到完全缺乏 Linux 内核 ABI 的其他操作系统(可能没有__NR_xxx函数),甚至可能也没有可用的sys_*函数(您可以编写exit()来发送中断或在汇编中使用 VDSO)。

最新更新