关于linux系统调用的说明

<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

一、基本说明

系统调用在用户空间进程和硬件设备之间添加了一个中间层,在 linux 中,系统调用是用户空间访问内核的唯一手段;除异常和陷入外,它们是内核唯一的合法入口。系统调用通常通过函数来调用,并且最终有一种明确的操作。

系统调用有两个基本特点,首先,函数声明中有 asmlinkage 限定词,它用于通知编译器仅从栈中提取该函数的参数,所有的系统调用都需要这个限定词。

看一下 /usr/include/asm/linkage.h 里面的定义:
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
__attribute__
是关键字,是 gcc C 语言扩展, regparm(0) 表示不从寄存器传递参数

如果是 __attribute__((regparm(3))) ,那么调用函数的时候参数不是通过栈传递,而是直接放到寄存器里,被调用函数直接从寄存器取参数。

其次,系统调用 get_pid() 在内核中被定义为 sys_getpid() 。这是 linux 中所有系统调用都应该遵守的命名规则:系统调用 bar() 在内核中也实现为 sys_bar() 函数。


二、系统调用处理程序

用户空间的程序无法直接执行内核代码,它们不能直接调用内核空间中的函数,因为内核驻留在受保护的地址空间上。如果进程可以在内核的地址空间上读写的话,系统安全就会失去控制。

应用程序以某种方式通知系统,告诉内核自己需要执行一个系统调用,希望系统切换到内核态,这样内核就可以代表应用程序来执行系统调用了。

通知内核的机制靠软中断实现:通过引发一个异常来促使系统切换到内核态去执行异常处理程序。此时的异常处理程序实际上就是系统调用处理程序。 X86 系统上的软中断由 int $0x80 指令产生。这条指令出触发一个异常导致系统切换到内核态并执行第 128 号异常处理程序,而该程序正是系统调用处理程序,名字即为 system_call()


三、系统调用的实现

每个系统调用都应该有一个明确的用途, linux 系统中不提倡采用多用途的系统调用(一个系统调用通过传递不同的参数值完成不同的工作)。

编写一个系统调用的时候,要时刻注意可移植性和健壮性。基本的 Unix 系统调用,有很大一部分到现在都还和 30 年前一样适用和有效。

系统调用必须检查它们所有的参数是否合法有效,系统调用在内核空间执行,如果任由用户将不合法的输入传递给内核,可能会导致系统崩溃。除去一些基本的检查,最重要的一种检查是检查用户提供的指针是否有效。在接收一个用户空间的指针之前,内核必须保证

1. 指针指向的内存区域属于用户空间。进程不能欺骗内核去读内核空间数据。

2. 指针执行的内存区域在进程的地址空间。进程不能欺骗内核去读其它进程的数据。

3. 如果是读,该内存应被标记为可读,如果是写,该内存应被标记为可写。进程不能绕过内存访问控制。

内核提供了两个方法来完成必须的检查和内核空间与用户空间之间数据的来回拷贝。注意,内核无论何时都不能轻率的接受来自用户空间的指针,这两个方法必须有一个被调用。为了向用户空间写入数据,内核提供了 copy_to_user() ,三个参数依次为进程空间中的目的内存地址、内核空间内的源地址、拷贝数据长度。为了从用户空间读取数据,内核提供了 copy_from_user(), 三个参数依次为内核中的目的地址,进程中的源地址,拷贝数据长度。如果执行失败,这两个函数返回的都是没能完成拷贝的数据的字节数,如果成功,则返回 0

 

你可能感兴趣的:(linux,unix,user,gcc,System,编译器)