fork()函数分析

1 C语言代码

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 
 4 int main()
 5 {
 6     if(!fork()){while(1)printf(“A”);}
 7     if(!fork()){while(1)printf(“B”);}
 8     wait();
 9     return 0;  
10 }

在Linux环境下,运行上述代码,屏幕上会交替显示A与B。

2 代码解析

进入fork()函数内部,如下所示。

main(){
mov __NR_fork, %eax
int 0x80
100: mov %eax, res
cmpl res,0
jne 208
200: printf(“A”)
jmp 200
208: ...
304: wait()
}

在实模式下:mov 目的操作数,源操作数

在保护模式下:mov 源操作数,目的操作数

而上面的一段代码是在保护模式下。

如下面代码所示,__NR_fork就是2。

 1 #define __NR_exit                 1
 2 #define __NR_fork                 2
 3 #define __NR_read                 3
 4 #define __NR_write                4
 5 #define __NR_open                 5
 6 #define __NR_close                6
 7 #define __NR_waitpid              7
 8 #define __NR_creat                8
 9 #define __NR_link                 9
10 #define __NR_unlink              10
11 #define __NR_execve              11
12 #define __NR_chdir               12
13 #define __NR_time                13
14 #define __NR_mknod               14
15 #define __NR_chmod               15
16 #define __NR_lchown              16

%eax

8086系列CPU16位寄存器如下所示,加上e表示扩展的意思,既寄存器是32位,前面加一个%是一个规则,就表示是寄存器的意思。

int 0x80

这是一个中断,只有通过中断,用户程序才能进入内核。

进入内核要调用两个关键函数

set_system_gate(0x80, &system_call);

set_system_gate把第0x80中断表的表项中中断处理程序入口地址设置为&system_call。并且把那一项IDT表中的DPL设置了为3, 方便用户程序可以去访问这个地址。

system_call:
    call sys_call_table(, %eax, 4)

这句汇编语句操作数的含义是间接调用地址在_sys_call_table + %eax * 4 处的函数。

由于sys_call_table[]指针每项4个字节,因此这里需要给系统调用功能号乘上4。

然后用所得到的值从表中获取被调用处理函数的地址。

接着执行sys_fork函数

sys_fork:
pushl ...
call copy_process
ret

内核通过调用函数copy_process()创建进程,copy_process()函数主要用来创建子进程的描述符以及与子进程相关数据结构。

通过执行ret指令,中断返回。

参考

int 80h系统调用方法

https://introspelliam.github.io/2017/08/07/int-80h%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E6%96%B9%E6%B3%95/

剖析Linux系统调用的执行路径

https://www.cnblogs.com/ronny/p/7789057.html

 

posted on 2020-07-12 21:45  辉哥54110  阅读(297)  评论(0编辑  收藏  举报