fork() 函数,vfork()函数

1.fork()函数

头文件:#include <sys/types.h>  #include<unistd.h> 函数原型:pid_t    fork(void) fork函数它有两个返回值,调用一次返回两次,成功调用返回两次,成功调用后fork后,当前进程已经分裂为两个进程,一个是原来的父进程,另一个是刚刚创建的子进程,父进程fork后返回刚刚创建的子进程的ID,另一个是子进程中,fork的返回值,返回0, 如果fork失败返回-1;但是,fork()之后是先运行子进程还是父进程呢,这取决于内核的调度算法;要注意的是,操作系统一般会让所有进程都享有同等的执行权.除非某进程的优先级比其它高;来看个例子;

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
        pid_t pid;
        char *msg;
        int k;
        printf("Process Creation Study\n");
        pid=fork();                                                                                                                             
        switch(pid)
        {
                case 0:
                        msg="Child process is running";
                         k=3;
                        break;

                case -1:
                        perror("Process creation failed\n");
                        break;
                default:
                        msg="Parent process is  running";
                         k=5;
                        break;
        }
while(k>0)
{
        puts(msg);
        sleep(1);
        k--;
}
}

运行结果:

yang@liu:~/Linux C$ ./a.out
Process Creation Study
Parent process is  running
Child process is running
Parent process is  running
Child process is running
Parent process is  running
Child process is running
Parent process is  running
Parent process is  running

可以发现父进程和子进程交替执行,子进程输出3条消息,父进程输出5条消息,这是怎样交替执行的呢,当系统调用fork之后,创建了一个子进程,它复制了父进程的资源.然后执行父进程,当break后进入while循环输出"Parent process is running"然后sleep(1)k=4,这时父进程在等待中,把cup的控制权又交到子进程,子进程执行,当子进程输出"Child process is running",然后执行sleep(1),在子进程等待时,父进程等待结束,就又执行父进程,就这样一直交替执行,直到进程结束. fork创建进程失败返回-1;失败的原因一般是父进程拥有子进程的个数超过了规定的限制,此时errno值为EAGAIN,内存不足errno的值为ENOMEM; 子进程会继承父进程的很多属性,主要包括:用户ID,组ID,当前的工作目录,根目录,打开的文件,创建文件时使用的屏蔽字,信号屏蔽字,上下文环境,共享的存储段,资源限制等, 子进程和父进程也有一些不同: 1.子进程有它唯一的进程ID, 2.fork的返回值不同,父进程返回子进程的ID,子进程返回0. 3.父进程ID不同,子进程的父进程ID为创建它的父进程ID; 4.子进程共享父进程打开的文件描述符,但父进程队文件描述符的改变不会影响子进程中的文件描述符. 5.子进程不继承父进程设置的文件锁. 6.子进程不继承父进程设置的警告. 7.子进程的未决信号集被清空.

僵尸进程:一个子进程在其父进程还没有调用wait()或waitpid()的情况下退出。这个子进程就是僵尸进程。任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是 每个 子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统 所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init 进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init 进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init 进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害 。

2.vfork()函数

头文件:#include<sys/types.h>  #include <unistd.h>
原型:pid_t vfork();
fork和vfork()的异同:
  • vfork和frok都是调用一次,返回两次;
  • 使用fork创建一个子进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性,而使用vfork创建一个进程时,操作系统并不将父进程的地址空间完全复制到子进程, 用vfork创建的子进程共享父进程的地址空间,也就是说子进程完全运行在父进程的地址空间上,子进程对地址空间的任何数据的修改,同样为父进程所见.
  • 用fork创建的一个子进程,哪个进程先运行取决于系统的调度算法,而 vfork一个子进程,保证子进程先运行,当它调用exec或exit后才执行父进程.看一个例子:
#include <stdlib.h>
#include <sys/types.h>
int globVar = 5;
int main()
{
        pid_t pid;
        int var = 1,i;
        printf("fork is different with vfork\n");
//      pid = fork();
        pid = vfork();
        switch(pid)
        {
                case 0:
                        i=3;
                        while(i-->0)
                        {
                                printf("Child process is running\n");
                                globVar++;
                                var++;
                                sleep(1);
                        }   
                        printf("Child's globVar=%d,var=%d\n",globVar,var);
                        exit(0);
                case -1:                                                                                                                        
                        perror("Process creation failed\n");
                        exit(0);
                default:
                        i=5;
                        while(i-->0)
                        {   
                                printf("Parent process is running\n");
                                globVar++;
                                var++;
                                sleep(1);

                        }   
                        printf("Parent's globVar=%d,var=%d\n",globVar,var);
                        exit(0);
        }   
} 

执行结果:

yang@liu:~/Linux C$ gcc  diffork.c
yang@liu:~/Linux C$ ./a.out
fork is different with vfork
Child process is running
Child process is running
Child process is running
Child's globVar=8,var=4
Parent process is running
Parent process is running
Parent process is running
Parent process is running
Parent process is running
Parent's globVar=13,var=9

从结果可以看出,先执行了子进程,子进程退出后,父进程才执行,全局变量globVar=5;局部变量var=1;执行子进程i=3; 执行完后globVar=8;var=4;然后执行父进程,从结果GlobVar=13,var=9.可以看出,它们共享地址空间; 现在,去掉fork()的注释,注释掉vfork()执行结果:

yang@liu:~/Linux C$ gcc  diffork.c
yang@liu:~/Linux C$ ./a.out
fork is different with vfork
Parent process is running
Child process is running
Parent process is running
Child process is running
Parent process is running
Child process is running
Parent process is running
Child's globVar=8,var=4
Parent process is running
Parent's globVar=10,var=6

从执行结果可以看出,子进程完全复制了父进程的地址空间,子进程与父进程独立执行;

 

 

你可能感兴趣的:(fork() 函数,vfork()函数)