热度 19
2012-3-14 08:55
1691 次阅读|
0 个评论
我们先说说函数原型: 头文件: #include 函数定义: int fork( void ); 返回值: 子进程中返回0,父进程中返回子进程ID,出错返回-1 函数说明:一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间,它们之间共享的存储空间只有代码段。 示例代码: #include #include int main(int argc, char ** argv ) { int pid = fork(); if(pid == -1 ) { // print("error!"); } else if( pid = =0 ) { // print("This is the child process!"); } else { // print("This is the parent process! child process id = %d", pid); } return 0; } Fork()系统在Linux中的返回值是没有NULL的. Error Codes 出错返回错误信息如下:EAGAIN 达到进程数上限.ENOMEM没有足够空间给一个新进程分配。 呵呵呵,要理解了,这些还远远不够的,你知道在Linux系统下,fork()是关键,下面多说一些: 第一点就是: 操作系统对进程的管理,是通过进程表完成的.进程表中的每一个表项,记录的是当前操作系统中一个进程的信息.进程在系统的唯一标识是PID,PID是一个从1到32768的正整数,其中1一般是特殊进程init,其它进程从2开始依次编号.当用完32768后,从2重新开始. .一个称为“程序计数器(program counter, pc)”的寄存器,指出当前占用 CPU的进程要执行的下一条指令的位置.当分给某个进程的 CPU时间已经用完,操作系统将该进程相关的寄存器的值,保存到该进程在进程表中对应的表项里面,把将要接替这个进程占用 CPU的那个进程的上下文,从进程表中读出,并更新相应的寄存器. 第二点就是:vfork() vfork与fork主要有三点区别: .fork():子进程拷贝父进程的数据段,堆栈段;vfork():子进程与父进程共享数据段.fork()父子进程的执行次序不确定vfork 保证子进程先运行,在调用 exec 或 exit 之前与父进程数据是共享的,在它调用 exec或 exit 之后父进程才可能被调度运行。 .vfork()保证子进程先运行,在它调用 exec 或 exit 之后父进程才可能被调度运行.如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。 1)先用fork()进行试验 #include #include int main(void) { pid_t pid; int count=0; pid=fork(); count++; printf("count= %d\n",count); return 0; } 分析: 通过上面fork()的说明,这个程序的输出应该是: ./test count= 1 count= 1 2)而将fork()换成vfork()呢,程序如下 #include #include int main(void) { pid_t pid; int count=0; pid=vfork(); count++; printf("count= %d\n",count); return 0; } 执行结果: ./test count= 1 count= 1 Segmentation fault (core dumped) 分析: 通过将fork()换成vfork(),由于vfork()是共享数据段,为什么结果不是2呢,答案是: vfork保证子进程先运行,在它调用 exec 或 exit 之后父进程才可能被调度运行.如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁. 3)做最后的修改,在子进程执行时,调用_exit(),程序如下: #include #include #include int main(void) { pid_t pid; int count=0; pid=vfork(); if(pid==0) { count++; _exit(0); } else { count++; } printf("count= %d\n",count); return 0; } 执行结果: ./test count= 2 分析:如果子进程中如果没有调用_exit(0),则父进程不可能被执行,在子进程调用exec(),exit()之后父进程才可能被调用. 所以加上_exit(0),使子进程退出,父进程执行. 这样 else 后的语句就会被父进程执行,又因在子进程调用 exec 或 exit 之前与父进程数据是共享的, 所以子进程退出后把父进程的数据段 count 改成1了,子进程退出后,父进程又执行,最终就将count 变成了 2. 简要的概括的说是: 1)fork()系统调用是创建一个新进程的首选方式,fork的返回值要么是0,要么是非0,父进程与子进程的根本区别在于fork函数的返回值. 2)vfork()系统调用除了能保证用户空间内存不会被复制之外,它与fork几乎是完全相同的.vfork存在的问题是它要求子进程立即调用exec, 而不用修改任何内存,这在真正实现的时候要困难的多,尤其是考虑到exec调用有可能失败. 3)vfork()的出现是为了解决当初fork()浪费用户空间内存的问题,因为在fork()后,很有可能去执行exec(),vfork()的思想就是取消这种复制. 4)现在的所有unix变量都使用一种写拷贝的技术(copy on write),它使得一个普通的fork调用非常类似于vfork.因此vfork变得没有必要.