Linux0.11中的fork实现和一些注意事项


Linux0.11中有一个fork的系统调用一直没弄明白,自己添加一些自己的想法。下面是思路和提问。

内核是linux0.11版本,里面的fork()用于创建子进程。
但我现在在找这个函数的具体定义时遇到了一些困难。
先把我的查找过程说下:
1、init里的main.c中有static inline _syscall0 (int, fork);
2、在unistd.h中找到_syscall0是个宏,定义如下

Assembly code
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \                 // 调用系统中断0x80。
    : "=a" (__res) \                            // 返回值??eax(__res)。
    : "0" (__NR_##name)); \                     // 输入为系统中断调用号__NR_name。
if (__res >= 0) \                               //如果返回值>=0,则直接返回该值。
    return (type) __res; \                      // 否则置出错号,并返回-1。
errno = -__res; \
return -1; \
}
进行参数替换
感觉是int fork(void){....};
具体实现主要是:
__asm__ volatile ( "int $0x80" \     // 调用系统中断0x80。
:"=a" (__res) \                      // 返回值??eax(__res)。
:"" (__NR_##name)); 其中的__NR_##name是2,用来在一个指针函数的数组中作为索引号用的
这个数组定义如下:

C/C++ code
typedef int (*fn_ptr) ();

在sys.h中 fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read, sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link, sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod, sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount, sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm, sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access, sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir, sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid, sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys, sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit, sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid, sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask, sys_setreuid, sys_setregid };
而__NR_##name的值为2感觉正好对应着上面那个数组里的sys_call_table[2](也就是sys_fork)
感觉有点眉目了
3、继续解释这个指令

Assembly code
__asm__ volatile ( "int $0x80" \ // 调用系统中断0x80。

:"=a" (__res) \ // 返回值eax (__res)。

:"" (__NR_ ##name));

 

感觉主要是调用系统中断0x80
4、在汇编代码system_call.s中找到该中断的中断处理函数
大概如下
#### int 0x80 --linux 系统调用入口点(调用中断int 0x80,eax 中是调用号)。

Assembly code
.align 2
_system_call:
cmpl $nr_system_calls-1,%eax # 调用号如果超出范围的话就在eax 中置-1 并退出。
ja bad_sys_call
5.5 system_call.s 程序
push %ds # 保存原段寄存器值。
push %es
push %fs

pushl %edx # ebx,ecx,edx 中放着系统调用相应的C 语言函数的调用参数。

pushl %ecx # push %ebx,%ecx,%edx as parameters

pushl %ebx # to the system call
movl $0x10,%edx # set up ds,es to kernel space
mov %dx,%ds # ds,es 指向内核数据段(全局描述符表中数据段描述符)。
mov %dx,%es

movl $0x17,%edx # fs points to local data space
mov %dx,%fs # fs 指向局部数据段(局部描述符表中数据段描述符)。

# 下面这句操作数的含义是:调用地址 = _sys_call_table + %eax * 4。参见列表后的说明。
# 对应的C 程序中的sys_call_table 在include/linux/sys.h 中,其中定义了一个包括72 个
# 系统调用C 处理函数的地址数组表。
call _sys_call_table(,%eax,4)
如果上面的eax放的是__NR_##name,即2
那么call _sys_call_table(,%eax,4)
相当于call sys_fork()
在该文件的下面一点找到该函数的定义
#### sys_fork()调用,用于创建子进程,是system_call 功能2。原形在include/linux/sys.h 中。
# 首先调用C 函数find_empty_process(),取得一个进程号pid。若返回负数则说明目前任务数组
# 已满。然后调用copy_process()复制进程。

Assembly code
.align 2
_sys_fork:
call _find_empty_process # 调用find_empty_process()(kernel/fork.c,135)。

testl %eax,%eax
js 1f push %gs

pushl %esi

pushl %edi

pushl %ebp

pushl %eax
call _copy_process       # 调用C 函数copy_process()(kernel/fork.c,68)。
addl $20,%esp            # 丢弃这里所有压栈内容。
1: ret
里面的_find_empty_process和copy_process()就是fork的主要子函数了.

问题1:请问我这个思路是不是对的。
问题2:汇编调用C函数为什么名字不一样啊
汇编里是_find_empty_process 而在fork.c里该函数名字是find_empty_process()
汇编这样调用能找到这个函数么????
问题3:__asm__ volatile ( "int $0x80" \         // 调用系统中断0x80。
:"=a" (__res) \                                 // 返回值??eax(__res)。
:"" (__NR_##name));这个内嵌汇编里的__NR_##name值是怎么传给eax的?以让后面的call _sys_call_table(,%eax,4)正确调用sysfork;
希望能给我详细讲解这条指令:__asm__ volatile (仅这条)调用中断int $0x80的具体操作
问题1:
差不多,但是有一個地方需要強調一下。static inline _syscall0 (int, fork),這個實際上隱含指出了系統調用的參數個數,注意這裡是以0結尾的,也就是說這個系統調用不需要傳遞參數。你應該還能從代碼中看到 _syscall1,_syscall2這樣的定義。
问题2:
gcc的C編譯器compile的時候,會在函數名字前面加上一個下劃綫,所以在彙編裏面調用C的函數的時候,要手動的加上這個下劃綫。注意:這裡說的是C編譯器,不包括C++的,C++的編譯器和C的有很多的不同,所以才會有extern "C"的存在。
问题3:
开头你自己也说了NR_name 是定义的好的值。具体在 <>unistd.h>头文件里。怎么传给eax的?:"" (__NR_
##name) 这条语句就是传给eax的意思。引号中省略表示用和输出一样的寄存器,也就是输出中的a(eax)。gcc嵌入汇编的格式你应该了解吧。int $0x80就是调用系统中断了,设置系统中断的操作在sched.c 最后一行 set_system_gate(0x80,&system_call); set_system_gate是一个嵌入式宏汇编语句,设置中断描述符表表项。具体实现在system.h.这样调用0x80系统中断后,执行的是 system_call  eax中存放的是定义好的NR_name的值,以后处理过程你都说了。

  • 1
  • 2
  • 下一页

相关内容