linux fork函数与vfork函数

man vfork:


NAME
vfork - create a child process and block parent

SYNOPSIS
#include <sys/types.h>
#include <unistd.h>

pid_t vfork(void);

DESCRIPTION
Standard description
(From POSIX.1) The vfork() function has the same effect as fork(2), except that the behav‐
ior is undefined if the process created by vfork() either modifies any data other than a
variable of type pid_t used to store the return value from vfork(), or returns from the
function in which vfork() was called, or calls any other function before successfully
calling _exit(2) or one of the exec(3) family of functions.

Linux description
vfork(), just like fork(2), creates a child process of the calling process. For details
and return value and errors, see fork(2).

vfork() is a special case of clone(2). It is used to create new processes without copying
the page tables of the parent process. It may be useful in performance-sensitive applica‐
tions where a child is created which then immediately issues an execve(2).

vfork() differs from fork(2) in that the calling thread is suspended until the child ter‐
minates (either normally, by calling _exit(2), or abnormally, after delivery of a fatal
signal), or it makes a call to execve(2). Until that point, the child shares all memory
with its parent, including the stack. The child must not return from the current function
or call exit(3), but may call _exit(2).

As with fork(2), the child process created by vfork() inherits copies of various of the
caller‘s process attributes (e.g., file descriptors, signal dispositions, and current
working directory); the vfork() call differs only in the treatment of the virtual address
space, as described above.

Signals sent to the parent arrive after the child releases the parent‘s memory (i.e.,
after the child terminates or calls execve(2)).

 创建一个新进程的方法只有由某个已存在的进程调用fork()或vfork(),当然某些进程如init等是作为系统启动的一部风而被内核创建的。

一、fork
1. 调用方法
#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);
正确返回:在父进程中返回子进程的进程号,在子进程中返回0
错误返回:-1

     子进程是父进程的一个拷贝。即,子进程从父进程得到了数据段和堆栈段的拷贝,这些需要分配新的内存;而对于只读的代码段,通常使用共享内存的方式访问。fork返回后,子进程和父进程都从调用fork函数返回处开始执行。
     父进程与子进程的不同之处在于:fork的返回值不同——父进程中的返回值为子进程的进程号,而子进程为0
2. fork函数调用的用途
⑴ 一个进程希望复制自身,从而父子进程能同时执行不同段的代码。
⑵ 进程想执行另外一个程序

 

二、vfork
1. 调用方法
与fork函数完全相同
#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);
正确返回:在父进程中返回子进程的进程号,在子进程中返回0
错误返回:-1

2. vfork函数调用的用途
       用vfork创建的进程主要目的是用exec函数执行另外的程序,与fork的第二个用途相同

三、fork与vfork的区别
1. fork要拷贝父进程的数据段;而vfork则不需要完全拷贝父进程的数据段,在子进程没有调用exec和exit之前,子进程与父进程共享数据段
2. fork不对父子进程的执行次序进行任何限制;而在vfork调用中,子进程先运行,父进程挂起,直到子进程调用了exec或exit之后,父子进程的执行次序才不再有限制

四、结束子进程
     结束子进程不用exit(0),而使用_exit(0)。这是因为_exit(0)在结束进程时,不对标准I/O流进行任何操作。而exit(0)则会关闭进程的所有标准I/O流。

 

由于子进程与父进程的运行是无关的,父进程可先于子进程运行,子进程也可先于父进程运行,所以下段程序可以有两种运行结果。

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int global=4;
int main()
{
    pid_t pid;
    int vari=5;
    printf("before fork\n");
    if((pid=fork())==0)
    {
        global++;
        vari--;
        printf("child changed\n");
    }
    else
        printf("parent did nog changed\n");
    
    printf("global=%d,vari=%d\n",global,vari);
    }

root@iZ23onhpqvwZ:~/ms/linux/unp/unpMy/tcpcliserv# ./fork1
before fork
parent did nog changed
global=4,vari=5
root@iZ23onhpqvwZ:~/ms/linux/unp/unpMy/tcpcliserv# child changed
global=5,vari=4

 

root@happy src]# ./a.out
before fork
Child changed
globa = 5 vari = 4
Parent did not changde
globa = 4 vari = 5

vfork创建新进程的主要目的在于用exec函数执行另外的程序,实际上,在没调用exec或exit之前子进程的运行中是与父进程共享数据段的。在vfork调用中,子进程先运行,父进程挂起,直到子进程调用exec或exit,在这以后,父子进程的执行顺序不再有限制。

 

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int global=4;
int main()
{
    pid_t pid;
    int vari=5;
    printf("before fork\n");
    if((pid=vfork())==0)
    {
        global++;
        vari--;
        printf("child changed\n");
        _exit(0);
    }
    else
        printf("parent did nog changed\n");
    
    printf("global=%d,vari=%d\n",global,vari);
    exit(0);
 }

 

root@iZ23onhpqvwZ:~/ms/linux/unp/unpMy/tcpcliserv# ./fork2
before fork
child changed
parent did nog changed
global=5,vari=4。

(从这里可以看出共享了数据段的。)

参考:http://blog.csdn.net/lingdxuyan/article/details/4996471

 

linux c 下exit(0);与_exit(0);的区别

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t pid;
if((pid=fork())==-1)
    {
    printf("error");
    }
else if(pid==0)
{
printf("ok\n");
exit(0);
}
else
{
printf("parent process,output begin\n");
printf("hello word");
_exit(0);
}
}

调试后你会发现没有

hello word

exit是退出去先把内存中的数据输出到文件中,而_exit 这个直接退出,消除内存中的数据;

printf是标准行输出,遇到“\n”或者是写入的内存满了才会标准输出;

我们可以尝试在hello word 中加入很多i,假设输入2000个吧(关于行输出自行google),再次调试发现,会有 hello 等字符,这就是溢出了。

回到前面,为什么开始没有hello,虽然hello word 在_exit前,但是你查看汇编会发现,他只是讲数据存在内存中。没有讲数据真正输出。当我们把_exit去掉 hello word就能显示了。为什么呢?这个就是编译器自己加的了。

                          

       从图中可以看出,_exit 函数的作用是:直接使进程停止运行,清除其使用的内存空间,并清除其在内核的各种数据结构;exit 函数则在这些基础上做了一些小动作,在执行退出之前还加了若干道工序。exit() 函数与 _exit() 函数的最大区别在于exit()函数在调用exit  系统调用前要检查文件的打开情况,把文件缓冲区中的内容写回文件。也就是图中的“清理I/O缓冲”。

 

所需头文件: exit:   #include<stdlib.h>

                       _exit: #include<unistd.h>

函数原型:exit:  void exit(int  status)

                _exit:  void  _exit(int  status)

函数传入值:status 是一个整型的参数,可以利用这个参数传递进程结束时的状态。一般来说,0表示正常结束;其他的数值表示出现了错误,进程非正常结束。在实际编程时,父进程可以利用wait 系统调用接收子进程的返回值,从而针对不同的情况进行不同的处理

参考:http://blog.csdn.net/lwj103862095/article/details/8640037

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。