fork()和vfork()详解

fork():

1.功能:创建子进程

2.返回:失败返回-1, 成功,父进程返回子进程id,子进程返回0;

返回失败的原因:

1.进程数达到系统规定上限,errno被置为EAGAIN,

2. 系统内存不足,errno被置为ENOMEM

3.调用fork之后通常要用if进行分流

fork()一般用法:

1.父进程希望复制自己,是父子进程能同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。

2.一个进程要执行一个不同的程序。例如,子进程从fork()返回后,调用exec函数。

fork()注意:

1.一次调用两个返回值,父进程返回子进程pid,子进程返回0;

2.父进程和子进程都从fork()执行结束之后的位置继续执行;

3.子进程以父进程为模板(PCB,数据,代码),写时拷贝;

4.调用fork后, 父子进程运行顺序不定

5.如果父进程先死,子进程变为孤儿进程,,孤儿进程会被1号进程收养

6.如果子进程先死,就会变成僵尸进程。 处理僵尸进程-----结束父进

僵尸进程:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork");
        exit(1);
    }
    else if(0 == pid)        //子进程先运行结束,而父进程对此没有进行处理
    {
        printf("child:[%d]\n", getpid());
        sleep(5);
        exit(EXIT_SUCCESS);
    }
    else
    {
        printf("parent:[%d]\n", getpid());
        sleep(20);
    }

    return 0;
}

孤儿进程:

#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork");
        exit(1);
    }
    else if(0 == pid)
    {
        printf("child:[%d]\n", getpid());
        sleep(20);
        exit(EXIT_SUCCESS);
    }
    else                        //父进程先运行结束,子进程被1号进程收养
    {
        printf("parent:[%d]\n", getpid());
        sleep(5);
    }

    return 0;
}

进程创建:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int g_val = 10;

int main()
{
    pid_t pid = fork(); //创建子进程
    if(pid < 0)
    {
        perror("fork");
        return 0;
    }
    else if(pid > 0){   //父进程
        printf("parent: id = %d: %d : %p\n", getpid(), g_val,&g_val);
        }
    else{           //子进程
        printf("child: id = %d: %d : %p\n", getpid(), g_val,&g_val);
    }
    sleep(1);
    return 0;

}


子进程继承父进程的那些东西?

1.地址空间
2.进程上下文(除PCB外的)
3.进程堆栈 写时拷贝
4.内存信息
5.文件描述符 --- 父进程打开的文件也要继承
6.信号设置情况
7.父进程的调度优先级 缺省为0  , -20 到 19  nice  renice修正
8.当前路径
9.根路径
10.控制终端
11.进程组 --- 父进程和子进程缺省为同一进程组
12.资源限制
哪些不继承?
父进程的锁子进程不继承
父进程ID子进程不继承
父进程未决的信号,子进程清除掉

fork的写时拷贝

Linux的fork通过写时拷贝实现,即内核并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间,只是在需要写入的时候才会复制地址空间,从而使父子进程拥有各自独立的地址空间。(即资源的复制在需要写入时才发生,否则以只读的方式共享)。

父子进程的执行顺序:

一般来说,子进程和父进程到底谁先执行取决于内核所使用的调度算法


vfork():尽量不要使用
1、不进行空间拷贝(写时都不拷贝),子进程pcb直接指向父进程pcb指向的位置
2、父进程一定是等到子进程执行完毕以后在执行
3、必须调用exit 或exec系列的函数,否则就会出现段错误
4、任何一个系统上实现的vfork或多过少的都有问题,尽量 不要使用

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t pid = vfork();
    if(pid < 0)
    {
        perror("vfork");
        exit(1);
    }
    else if(pid  == 0 )
    {
        sleep(2);
        printf("i am child!\n");
        exit(0);
    }
    else
    {
        printf("i am father!\n");
    }

    return 0;
}


#include <stdio.h>
#include  <unistd.h>
#include <stdlib.h>

int g_val = 10;
int main()
{
    pid_t pid = vfork();

    if(pid < 0){
        perror("vfork");
        exit(1);
    }
    else if(pid == 0)
    {
       g_val = 20;
        printf("child[%d] : g_val = %d: %p \n", getpid(), g_val, &g_val);
        exit(0);
    }
    else{
        printf("parent[%d] : g_val = %d: %p \n", getpid(), g_val, &g_val);
    }
    sleep(1);
    return 0;
}

vfork()注意:

子进程先执行,不能return返回,return是在堆栈上进行操作,return返回相当于压栈一次,出栈两次,即子进程释放了main的栈,父进程再去释放就形成了段错误。

总结:fork和vfork的区别

1.调用fork后, 父子进程运行顺序不定,而vfork子进程一定先运行。

2.fork存在写时拷贝的机制,子进程拷贝父进程地址空间。 vfork子进程(在调用exec(进程替换)或者exit之前)共享父进程的地址空间(一改都改)。

3.vfork必须调用exit返回,否则会引发段错误。

fork()练习:

1.创建出三个进程,关系是父亲,兄弟进程。

2.创建出三个进程,关系是父亲,儿子,孙子

#include <stdio.h>
#include <unistd.h>


int main()
{
     pid_t pid;
     pid = fork();
    //if(pid > 0 ){           //创建三个进程, 一个父进程,两个兄弟进程
    // fork();
    // printf("mypid = %d, myppid = %d\n", getpid(), getppid());
    //}
  
  if(pid == 0 ){                //创建三个进程,一个父进程, 一个子进程,一个孙子进程
        fork();
        printf("mypid = %d, myppid = %d\n", getpid(), getppid());
     }
    return 0;
}
3.
#include <stdio.h>
#include <unistd.h>

int main()
{
//    printf("printf");       //没有刷新缓冲区会打印两次
//    fork();
    

//    printf("printf\n");     //刷新缓冲区,打印一次
//    fork();
        
    fork();
    fork();
    fork();
    printf("1");	//8个1,一生二,二生四, 四生八

    return 0;
}

4.

int main()
{
	int i = 0;
	for (; i < 2; i++)
	{
               fork();
               printf("$");               //这里打印8个
	        
               //flush(stdout);           //这里对缓冲区进行刷新,应该打印6个
	}
	return 0;
}



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值