电子说
本文我将基于 ARM 体系结构角度,从 Linux 应用层例子到内核系统调用函数的整个过程来梳理一遍,讲清楚linux系统调用实现原理,这里我们以open系统调用为例来讲解。
在应用层调用 open 系统调用时,实际上调用的是 C 标准库函数,具体的代码如下:
其中,open 函数的第一个参数是要打开的文件路径,第二个参数则是打开方式(例如只读、读写等)。在这里我们使用了 O_RDONLY 参数表示只读。
在 C 标准库中,open 函数实际上是通过系统调用来完成文件的打开操作。接下来,我们来看一下系统调用的具体实现。
在 ARM 架构的 Linux 内核中,系统调用的处理流程分为以下几步:
1.应用程序通过 swi 汇编指令触发中断,将 CPU 切换到特权模式。
在 ARM 架构中,每一个系统调用都对应有一个系统调用号,比如open系统调用的号码就是5,应用程序通过 swi 指令从用户态切换到内核态,CPU进入特权模式,通过R7寄存器将中系统调用号传递给内核。下面是 open 系统调用的汇编代码示例:
2.中断处理程序根据传递的系统调用号找到对应的系统调用函数。
内核中的系统调用处理程序是通过一张系统调用表来实现的,该表包含了所有系统调用的函数指针。当中断处理程序接收到一个系统调用请求时,它会根据系统调用号查找该表,并跳转到相应的系统调用函数。在 ARM 架构中,系统调用表存储在地址为 0x9000 的内存位置上。
对于 open 系统调用,在内核中的实现代码为 sys_open() 函数,其定义在 fs/open.c 文件中。在 ARM 架构中,sys_open() 函数的函数指针存储在系统调用表的第 5 个位置上。
3.将用户空间的参数复制到内核空间,并在系统调用函数中进行相应的操作。
在 ARM 架构中,内核将用户空间和内核空间分开,以确保用户空间的数据不会被恶意程序修改。因此,在执行系统调用之前,内核需要将用户空间的数据复制到内核空间。对于 open 系统调用,它的参数包括文件名和标志,这些参数都需要从用户空间复制到内核空间。
在内核中,copy_from_user() 和 copy_to_user() 函数用于从用户空间复制数据到内核空间和从内核空间复制数据到用户空间。对于 open 系统调用,它需要从用户空间复制文件名和标志,并将它们传递给 sys_open() 函数进行处理。下面是 sys_open() 函数的代码示例:
4.将处理结果返回给用户空间,并将 CPU 切换回用户模式。
在 ARM 架构中,系统调用的返回值通过 r0 寄存器传递给应用程序。对于 open 系统调用,它的返回值为文件描述符,即打开文件的句柄。如果打开文件成功,则返回一个非负整数,表示新的文件描述符;否则,返回一个负数,表示错误代码。
在 sys_open() 函数中,如果成功打开文件,则将文件描述符安装到当前进程的文件描述符表中,并返回该文件描述符。否则,返回错误代码。下面是 open 系统调用的汇编代码示例:
最后,当处理完 open 系统调用后,中断处理程序将 CPU 切换回用户模式,将处理结果返回给应用程序。
全部0条评论
快来发表一下你的评论吧 !