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系统调用方法