【Linux学习笔记】Linux/UNIX系统调用详解(资料整理)

来源:互联网 发布:二手物品交易软件 编辑:程序博客网 时间:2024/04/30 19:08

 

当我们执行了一个函数,这个函数到底在系统里面完成了哪些工作呢?函数是按照怎样的路线执行呢??

 

不看不知道,一看吓一跳!这几天翻看一些书籍资料,整理了一下Linux系统调用方面的内容,以此与诸君共勉!


 

首先,说一下内核。系统内核是管理和分配计算机资源的的核心层软件。(ps: 一般情况下,Linux内核可执行文件存储在/boot/路径下,文件名一般以 “vmlinuz+内核版本号” 命名。末尾的z表示内核是经过压缩的可执行文件)


内核的主要任务有:任务调度;内存管理;文件系统管理;进程管理;设备管理;网络管理;提供系统调用应用编程接口(API)。


这里我们说一下最后一个功能---提供系统调用API,由于内核的管理功能,外围软件和标准库函数的作用,让我们很难接触到内核层。为我们提供了极大地方便,同时也造成我们可能对系统内核根本一无所知。


其实,当我们在执行prinf(“Hello World!\n”);的时候,系统就要由用户态切换到内核态工作了。


 

下面,是几点系统调用方面需要注意的地方:

1、系统调用将处理器从用户态切换到内核态,以便CPU访问到受保护的内核内存。

2、系统调用的组成是固定的,每个系统调用都由一个唯一的数字(以名称方式呈现给我们)来标识。

3、每个系统调用可辅之以一套参数,对用户空间(进程的虚拟地址空间)与内核空间之间传递的信息加以规范。

 

接下来,详细说明一下系统调用的详细步骤:

1、当我们在程序中调用某些函数时,应用程序会通过调用库函数中的外壳函数,发起系统调用。

2、对于系统调用中断处理来说,外壳函数必须保证所有的系统调用参数可用。通过堆栈,这些参数传入外壳函数,但内核却希望将这些参数置入特定寄存器。因此,外壳函数会将上述参数复制到寄存器。

3、由于所有系统调用进入内核的方式相同,内核需要设法区分每个系统调用。为此,外壳函数会将系统调用编号复制到一个特殊的CPU寄存器(%eax)中。

4、外壳函数执行一条中断机器指令(int 0x80,2.6.x内核之后都支持更快的sysenter指令了),让处理器从用户态切换到内核态,并执行系统中断0x80的中断矢量所指向的代码。

5、为了响应中断0x80,内核会调用system_call()例程(在内核汇编文件arch/i386/entry.S中)处理这次中断,具体步骤如下:

      a) 在内核栈中保存寄存器值;

      b) 审核系统调用编号的有效性;

      c) 以系统调用编号对存放所有系统调用服务例程的列表(内核变量sys_call_table)进行索引,发现并调用相应的系统调用服务例程。若系统调用服务例程带有参数,则首先检查参数的有效性。随后该服务例程会执行必要的任务,可能涉及到对特定参数中指定地址处的值得修改,以及在用户内存和内核内存之间传递数据。最后,该服务例程会将结果状态返回给system_call()例程。

      d) 从内核栈中恢复各寄存器值,并将系统调用返回值置于栈。

      e) 返回至外壳函数,同时将处理器切换回用户态。

6、若系统调用服务的返回值表明调用有误,外壳函数会使用该值来设置全局变量errno。然后,外壳函数会返回到调用函数,并同时返回一个整型值,以表明系统调用是否成功。(PS: 在linux中,系统调用服务成功返回非负值,失败返回相应errno的取反值。库函数会再次对errno的负值取反得到非负的errno,同时用-1作为外壳函数的返回值,表示调用程序出错。)

 

总算告一段落,下面以系统调用execve()为例,展示了系统调用的执行路径!

 


 

MISSION SUCCESS ......


0 0