如下关于fork()的描述来自于:jason314linux
首先,在Linux环境下,一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。而后把原来的进程的全部值都复制到新的新进程中,只有少数值与原来的进程的值不一样。至关于克隆了一个本身。函数
fork调用的一个奇妙之处就是它仅仅被调用一次,却可以返回两次,它可能有三种不一样的返回值:
1)在父进程中,fork返回新建立子进程的进程ID;
2)在子进程中,fork返回0;
3)若是出现错误(如系统资源不足),fork返回一个负值。spa
在fork函数执行完毕后,若是建立新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新建立子进程的进程ID。咱们能够经过fork返回的值来判断当前进程是子进程仍是父进程。.net
如今,咱们来写一段程序,使用API调用fork:指针
#include <stdio.h> #include <unistd.h> int main() { pid_t fpid; int count = 0; fpid = fork(); printf("Now pid = %d\n", fpid); if(fpid < 0) printf("Error in fork!"); else if(fpid == 0){ printf("I am the child process, my process id is: %d\n", getpid()); count++; } else{ printf("I am the parent process, my process id is: %d\n", getpid()); count++; } printf("Now count = %d\n", count); return 0; }
在第八行执行完fork后,父进程中有count=0、fpid=子进程的pid;子进程中变量为count=0、 fpid=0,这两个进程的变量都是独立的,存在不一样的地址中,也不是共用的。能够说,咱们就是经过fpid来识别和操做父子进程的。orm
在x86的系统中,%eax寄存器在进行系统调用前储存系统调用号。另外,因为六个及以上参数的系统调用并很少见,所以通常使用%ebx、%ecx、%edx、%esi和%edi依次存放前五个参数。若是出现六个以上参数的状况,应该用一个单独的寄存器存放指向全部这些参数在用户空间地址的指针。当调用结束后,函数的返回值存放在%eax中。接口
下面,咱们将改写fork.c,直接嵌入汇编语言进行系统调用:进程
#include <stdio.h> #include <unistd.h> int main() { pid_t fpid; int count = 0; asm volatile( "mov $0, %%ebx\n\t" "mov $0x2, %%eax\n\t" "int $0x80\n\t" "mov %%eax, %0\n\t" : "=m"(fpid) ); printf("Now pid = %d\n", fpid); if(fpid < 0) printf("Error in fork!"); else if(fpid == 0){ printf("I am the child process, my process id is: %d\n", getpid()); count++; } else{ printf("I am the parent process, my process id is: %d\n", getpid()); count++; } printf("Now count = %d\n", count); return 0; }
程序的运行结果以下:资源
总结:API与系统调用并非一一对应的关系(Linux系统能够参考syscalls),它为程序提供了标准接口。而内核基本只与系统调用打交道;固然,咱们也能够直接使用系统调用写程序,但势必会下降程序的可移植性。至于APIs如何进行系统调用,那就是Glibc等标准制定者的事了。
陈政/arc001 原创做品转载请注明出处 《Linux内核分析》MOOC课程