专栏首页编程珠玑如何创建多进程程序?(文末福利)

如何创建多进程程序?(文末福利)

来源:公众号【编程珠玑】

作者:守望先生

网站:https://www.yanbinghu.com

前言

在《对进程和线程的一些总结》已经介绍了进程和线程的区别,但是在C/C++中如何创建进程呢?或者说如何编写多进程的程序呢?

什么时候需要fork进程

一种可能见到的场景是在服务器程序中,一个请求到来后,为了避免服务器阻塞,fork出一个子进程处理请求,父进程仍然继续等待请求到来。但这种方式无疑开销会稍大。

另一种最常见的就是执行一个不同的程序,例如我们在shell终端执行一条命令,实际上就是bash(或者其他)调用fork之后,在执行exec族函数。

fork

一个现有的进程可以通过fork函数来创建一个新的进程,这个进程通常称为子进程。fork函数原型如下:

#include<unistd.h>
pid_t fork(void);

如果调用成功,它将返回两次,子进程返回值是0;父进程返回的是非0正值,表示子进程的进程id;如果调用失败将返回-1,并且置errno变量。

有的朋友可能常常会记不住返回0的时候到底是子进程还是父进程。这里教给大家一个方法。一个进程可以有多个子进程,但是一个子进程同一时刻最多只有一个父进程。子进程可以通过getppid获取父进程的进程id,但是父进程却没法获取,因此需要在fork后就得到子进程的进程id。

//公众号【编程珠玑】,博客 https://www.yanbinghu.com
#include<stdio.h>
#include<unistd.h>
int main(void)
{
    pid_t pid;
    char testVal[] = {};
    FILE *fp = fopen("test.txt","w");
    if(NULL == fp)
    {
        printf("open test.txt failed\n");
        return ;
    }
    if(-1 == (pid = fork()))//等于-1时表明fork出错
    {
        perror("fork error");
        return -1;
    }
    else if( == pid)//子进程
    {
        snprintf(testVal,,"I am child,father pid is %d\n",getppid());
        fprintf(fp,"%s",testVal);

    }
    else  //父进程
    {
        snprintf(testVal,,"I am parent,child pid is %d\n",pid);
        fprintf(fp,"%s",testVal);
    }
    printf("fork over,testVal is %s",testVal);
    //为了避免马上退出sleep一段时间
    sleep();
    fclose(fp);
    return ;
}

并在同目录下创建一个test.txt文件,运行结果:

fork over,testVal is I am parent,child pid is 
fork over,testVal is I am child,father pid is 

需要注意的是,不要对父进程先执行还是子进程先执行做任何假设,因为都有可能。所以,可能出现的运行结果并不一样。

fork到底做了什么

fork被调用后,子进程拥有父进程的副本,因此它拥有父进程的数据空间,堆栈等。但是由于fork之后通常会调用exec函数去执行与原进程不想关的程序,因此fork时直接拷贝父进程的副本显得没有必要。为了提高fork的效率,采用了一种写时复制的技术。即fork之后,子进程名义上拥有父进程的副本,但是实际上和父进程共用,只有当父子进程中有一个试图修改这些区域时,才会以页为单位创建一个真正的副本。

所以我们看到前面的示例程序中,父子进程都对testVal进程了修改,但是互不影响。因为它们修改了不同的区域。

子进程继承了父进程哪些属性?

由于子进程是父进程的一个副本,所以父进程有的属性,子进程也都有,这些属性包括

  • 打开的文件描述符
  • 会话ID
  • 根目录
  • 资源限制
  • 工作目录
  • 进程组ID
  • 控制终端
  • 环境

我们运行前面的示例程序之后,重新打开一个终端,找到打开test.txt文件的进程:

$ lsof test.txt
fork     root    r   REG  ,          test.txt
fork     root    r   REG  ,          test.txt

lsof命令的用法可以参考《如何查看linux中文件打开情况?

也可以观察进程打开的文件描述符:

$ ls -l /proc//fd
lrwx------  root root  Aug  :  -> /dev/pts/
lrwx------  root root  Aug  :  -> /dev/pts/
lrwx------  root root  Aug  :  -> /dev/pts/
lr-x------  root root  Aug  :  -> /data/workspaces/practices/c/test.txt

为什么这里要特别说明打开的文件描述符呢?试想以下两点:

  • 父子进程对同一个文件进行写,将共享文件偏移
  • 如果该描述符是一个socket描述符,父进程退出后,子进程仍然打开着,父进程再次启动,将会出现端口被占用的问题。

所以如果父子进程的其中一个使用了fclose关闭了文件描述符,实际上还有另外一个进程打开了test.txt文件。

与前面testVal不同的是,如果父子进程都对文件进行写,并不会产生两个不同的文件,而是会对同一个文件进行写,因此运行后会在同一个文件里出现父子进程写的内容:

$ cat test.txt
I am parent,child pid is 
I am child,father pid is 

父子进程有哪些不同?

  • fork之后的返回值不同,进程ID也不同
  • 子进程未处理信号设置为空
  • 子进程不继承父进程设置的文件锁
  • 一般子进程会执行与父进程不完全一样的代码流程

总结

fork用于创建进程,但是需要注意的是,子进程继承了很多父进程的东西,如果子进程不需要可以进行修改或“丢弃”,例如子进程关闭父进程打开的文件描述符等等。理解了fork的写时复制思想,也就会明白,实际上fork的速度是非常快的。本文总结点如下:

  • fork调用一次,返回两次
  • 一个进程可以有多个子进程,但同一时刻最多只有一个父进程
  • 子进程继承了父进程很多属性
  • 父子进程执行的先后顺序不一定

本文仅仅简单介绍了fork,实际上得到子进程之后,还需要对子进程的状态进行“监控”,否则会出现其他意想不到的问题。

本文分享自微信公众号 - 编程珠玑(shouwangxiansheng),作者:守望先生

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 父进程退出时如何确保子进程退出?

    子进程退出的时候,父进程能够收到子进程退出的信号,便于管理,但是有时候又需要在父进程退出的时候,子进程也退出,该怎么办呢?

    编程珠玑
  • 文件读写测试,磁盘读写测试全靠它

    有时候需要测试磁盘读写速度,或者临时读写文件,不想临时写代码?有没有测试使用的命令?当然有!

    编程珠玑
  • 进程间通信方式有哪些?

    进程能够单独运行并且完成一些任务,但是也经常免不了和其他进程传输数据或互相通知消息,即需要进行通信,本文将简单介绍一些进程之间相互通信的技术--进程间通信(In...

    编程珠玑
  • Linux的进程线程及调度

    操作系统中的经典定义: 进程:资源分配单位。 线程:调度单位。 操作系统中用PCB(Process Control Block, 进程控制块)来描述进程。...

    用户4940323
  • 三分钟基础:有哪些经典的进程调度算法?

    想当初,操作系统创造我时,只是打算让我用 FCFS 调度算法,简单维护下进程的秩序。但我后来的发展,远远超过了他的想象。

    帅地
  • 10-1 进程如何工作

    见贤思齊
  • 关于Android进程,你需要知道的

    导语 Android系统是怎样杀进程的,native进程是怎么管理的?本文为你解密 一、Android进程管理 Android是基于组件工作的,...

    MelonTeam
  • 进程 · 全家桶

    fork调用一次返回两次 父进程中返回子进程id (就是大于0的意思) 子进程返回0 读时共享写时复制,可保高效

    看、未来
  • 关于进程的分类

    默认情况下,进程是在前台运行的,这时就把shell给占据了,我们无法进行其它操作。对于那些没有交互的进程,很多时候,我们希望将其在后台启动,可以在启动参数的时候...

    shengjk1

扫码关注云+社区

领取腾讯云代金券