您的位置:首页 > 运维架构 > Linux

Linux进程学习(二)之fork()和vfork()的学习

2012-07-04 15:53 831 查看
http://blog.csdn.net/tigerjb/article/details/6006884

 

fork()和vfork()的学习
通过 上一部分的学习,我们了解了进程的概念以及在Linux中进程的实现,此部分我们将具体学习如何在Linux中创建一个进程。
一前言:
 
通过原理知识的学习,我们知道每个进程由进程ID号标识。进程被创建时系统会为其分配一个唯一的进程ID号。当一个进程向其父进程(创建该进程的进程)传递其终止消息时,意味这个进程的整个生命周期结束。此时,该进程占用的所用资源包括进程ID被全部释放。
那么在Linux中如何创建一个进程呢?
创建进程有两种方式:一是由操作系统创建,二是由父进程创建的进程(通常为子进程)。
系统调用fork是创建一个新进程的唯一方式。vfork也可创建进程,但它实际上还是调用了fork函数。
Tiger-John说明:
1.由操作系统创建的进程它们之间是平等的不存在资源继承关系。
2.由父进程创建的进程通常为子进程它们之间有继承关系。
3. 在系统启动时,OS会创建一些进程,它们承担着管理和分配系统资源的任务,即系统进程。
如0号idle进程,它是从无到有诞生的第一个线程,主要用于节能 ;关于 idle进程 , 系统最初引导 0号进程,对应的 PCB为 init_task(),要说明下它是0号进程 PCB的头,并不是 1号 init进程,在引导结束后即成为 cpu 上的 idle进程。在每个 cpu上都有一个 idle进程,这些进程登记在 init_tasks[]数组中。 idle进程不进入就绪队列,系统稳定后,仅当就绪队列为空的时候 idle 进程才会被调度到,在没有其它进程运行的情况下,它大量时间占用 cpu 。
1号进程(init进程)它是一个由内核启动的用户级进程 ,它是所有用户进程的父进程。实际上,Linux2.6在初始化阶段首先把它建立为一个内核线程kernel_init:

kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

参数CLONE_FS | CLONE_FILES |CLONE_SIGHAND表示0号线程和1号线程分别共享文件系统(CLONE_FS)、打开的文件(CLONE_FILES)和信号处理程序(CLONE_SIGHAND)。当调度程序选择到kernel_init内核线程时,kernel_init就开始执行内核的一些初始化函数将系统初始化。

那么,kernel_init()内核线程是怎样变为用户进程的呢?
实际上,kernel_init()内核函数中调用了execve()系统调用,该系统调用装入用户态下的可执行程序init(/sbin/init)。注意,内核函数kernel_init()和用户态下的可执行文件init是不同的代码,处于不同的位置,也运行在不同的状态,因此,init是内核线程启动起来的一个普通的进程,这也是用户态下的第一个进程。init进程从不终止,因为它创建和监控操作系统外层所有进程的活动。
二fork()函数和vfork()函数的学习
1.fork()函数
调用fork函数后,当前进程分裂为两个进程,一个是原来的父进程,另一个是刚创建的子进程。父进程调用fork后返回值是子进程的ID,子进程中返回值是0,若进程创建失败,只返回-1。失败原因一般是父进程拥有的子进程个数超过了规定限制(返回EAGAIN)或者内存不足(返回ENOMEM)。我们可以依据返回值判断进程,一般情况下调用fork函数后父子进程谁先执行是未定的,取决于内核所使用的调度算法。一般情况下os让所有进程享有同等执行权,除非某些进程优先级高。若有一个孤儿进程,即父进程先于子进程死去,子进程将会由init进程收养。
函数实例:通过fork()函数创建一个进程:
 1#include<sys/types.h>

  2#include<unistd.h>

  3#include<stdio.h>

  4 

  5 main()

  6 {

 7        pid_t
pid;

 8        printf("PID
before fork() :%d/n",(int)getpid());

  9 

 10        pid
= fork();

 11        if(pid
< 0){

 12                printf("error
in fork!/n");

 13        }

 14        else
if(0 == pid){

 15                printf("I'm
the child process, CurpPID is %d,ParentPid is%d   /n",pid,(int)getppid());

 16                }

 17        else{

 18                printf("I'm
the parent process,child PID is %d,ParentPIDis    %d/n",pid,(int)getpid());

 19        }

 20 

程序经过调试后结果如下:
think@ubuntu:~/work/process_thread/fork$./fork

PID before fork() :4566

I'm the parent process,child PID is 4567,ParentPID is 4566

I'm the child process, CurpPID is 0,ParentPid is 4566

从程序执行结果可以看出: 调后fork()函数后返回两个值,子进程返回值为0,而父进程的返回值为创建的子进程的进程ID。
Tiger-John说明:
1> Linux进程一般包括代码段,数据段和堆栈段。代码段存放程序的可执行代码;数据段存放程序的全局变量、常量、静态变量;堆存放动态分配的内存变量,栈用于函数调用,存放函数参数、函数内部定义的局部变量。
2>有人说调用fork函数后,fork()函数返回了两个值,这是一中错误的说法。其实,当系统调用fork()函数后 fork()会将调用进程的所有内容原封不动的拷贝到新产生的子进程中去, 当前进程分裂成了两个进程分别在执行,互不干扰。
3>.看一个函数实例来仔细体会一下系统调用fork()函数后是如何执行的。
  1#include<stdio.h>

  2#include<unistd.h>

  3#include<sys/types.h>

  4 

  5 int main()

  6 {

 7        pid_t
pid;

 8        int
count = 0;

 9        pid
= fork();

 10 

 11        printf("This
is first time,pid = %d/n",pid);

 12        printf("This
is the second time,pid = %d/n",pid);

 13        count++;

 14        printf("count
= %d/n",count);

 15 

 16        if(pid
> 0){

 17                printf("This
is the parent process,the child has the pid:%d   /n",pid);       

 18        }

 19        else
if(!pid){

 20                printf("This
is the child process./n");

 21        } 
 22        else{

 23                printf("fork
failed./n");

 24        }

 25 

 26        printf("This
is third,pid = %d/n",pid);

 27        printf("This
is four time,pid = %d/n",pid);

 28        return
0;

 29 }

程序经过调试后结果
think@ubuntu:~/work/process_thread/fork1$./fork

This is first time,pid = 4614

This is the second time,pid = 4614

count = 1

This is the parent process,the child has the pid :4614

This is third,pid = 4614

This is four time,pid = 4614

This is first time,pid = 0

This is the second time,pid = 0

count = 1

This is the child process.

This is third,pid = 0

This is four time,pid = 0

think@ubuntu:~/work/process_thread/fork1$ 

Tiger-John说明:
从上面的程序的执行结果我们看到一个奇怪的现象:为什么printf的语句执行两次,

而那句“count++;”的语句却只执行了一次 ?
系统在调用fork()后分裂成了两个函数分别执行 ,互不干扰 。
 
2.Vfork和fork的区别:
1> vfork也可创建进程,但它实际上还是调用了fork函数。
2>在说明他们的区别之前我们先看两个程序和执行结果
函数实例1:用fork()函数创建进程
  1#include<stdio.h> 

  2#include<sys/types.h> 

  3#include<unistd.h> 

  4#include<stdlib.h> 

  5 int globVar = 5; 

  6 

  7 int main(void) 

  8 { 

 9        pid_t
pid; 

 10        int
var = 1; 

 11        int
i; 

 12        printf("fork
is different with vfork /n"); 

 13 

 14        pid
= fork(); 

 15        if(!pid){ 

 16                i=3; 

 17                while(i--
> 0){ 

 18                        printf("Child
process is running/n"); 

 19                        globVar++; 

 20                        var++; 

 21                        sleep(1); 

 22                              } 

3   
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息