linux下多进程开发详解

8 篇文章 0 订阅

全部代码:https://download.csdn.net/download/weixin_42295969/86504723

一、多进程经典开发模型

多进程的最经典的开发场景就是Nginx,这是因为多进程很适合做负载均衡,进程能够申请资源,而线程更多的则是资源共享。
Nginx模式

二、实践多进程程序

分为前台和后台
运行效果如下:

  • 先运行前台程序,前台程序会直接通过execl调用后台程序。
    在这里插入图片描述

整体结构

整体结构
在结构中对前台以及后台的划分:
在这里插入图片描述

如何形成一个逻辑的闭环

需要完成三个同步
在这里需要完成三个同步:1、开始进入消息队列发送接收之前的同步;2、对消息队列的发送与接收进行同步(这里系统调用的方法已经实现了同步);3、对消息队列接收之后的同步,这时候需要对命令处理之后信息进行输出,在后台处理,在前台输出;

消息队列前后的同步

如何在消息队列前后实现同步以及同步的意义

解释图中×的意义以及为何去除

因为之前想的是还要实现消息队列的同步,所以是想把发送以及接收放在同步之前,或者将接收的部分放在同步之后,其实这会导致下一个命令到来的问题,所以这才有图上的×,表示这个顺序是错误的。

现在的实现思路

但是由于消息队列已经是同步的了,所以只需要实现消息队列之前和之后的同步;
如何在消息队列前后实现同步以及同步的意义?

  • 首先确定的是消息队列已经实现同步功能,那么只需要保证两个进程的前后顺序就行
  • 所以:
    • 消息队列之前要保证同步;
    • 消息队列之后要保证同步;

消息队列之前的同步
通过建立有名管道实现同步,并管理内实现清零而已,保证之前是清零,因为这里实现了循环了,所以要保证每一个循环都是空的,然后再往里面写内容,同理消息队列之后的同步也是需要先做清零的
消息队列之后的同步:将结果输出,这里考虑到循环的问题,因为这也是共同的资源,所以也需要进行同步,所以同样通过有名管道将输出信息写进管道并进行读取;

前台分析

在这里实现两大组合:fork+execl+waitpid和fork+prctl(PR_SET_PDEATHSIG, SIGKILL)

  1. fork+execl+waitpid:waitpid让子进程不会进入僵尸状态,能够在父进程结束的时候,子进程也能够被结束
    • fork从父进程分支出一个新的进程,而execl将想要执行的进程覆盖掉fork的子进程,这样的好处就是,之后能够用父进程与子进程之间的管理方式,而waitpid就是指的管理方式,等待子进程结束,防止僵尸进程的出现。
  2. fork+prctl(PR_SET_PDEATHSIG, SIGKILL)在父进程退出来的时候,能够保证子进程也能够退出来;
    对孤儿进程和僵尸进程的分析:
  3. 孤儿进程——父进程退出了,但是子进程还在并没有结束,所以最终被迫被公共的init进程(进程号PID = 1)回收;
  4. 僵尸进程——子进程退出了,但是正常情况应该是要发送sigCHID信号,通知父进程进行回收资源,尤其是进程号,但是没有发送信号就会产生僵尸进程;

在waitpid的时候需要结合进程的三种状态进行判断:
进程的三种状态
进程的这三种状态也可以类比理发店排队的情景:
理发店类似的情况

如何同步

使用一个同步管道来维护前后台程序间的同步

  • 有名管道有个特性是没写完不能读,没读完不能写,所以管道的双方就能够保证是同步的了;
    那么如果是前后台的数据同步就可以利用两个命名管道来进行实现,如下所示:
  • 这里最终的需求是要消息队列的发送同步了
    前台程序的同步代码为:
writeSynPipe();                      //写同步管道
sendMessage(msg, queId);            //发送消息
readResPipe();                       //读结果管道并打印  

后台程序的同步代码为:

readSynPipe();                       //读同步管道
if(readMessage(queId, &m) == -1) {
      continue;                       //继续尝试
}
execCommand(m.text, resCommand);    //执行命令
writeResPipe(resCommand);            //将结果写入管道

其中synPipe是同步管道,resPipe是保存命令执行结果的管道。

后台分析

与前台一样,首先通过消息队列进行同步,然后通过有名管道在消息队列前后进行同步

//核心代码
writePipe();
// 6.对消息队列发送消息,将转换后的指令发到消息队列中去
sendMessage(msg, queId);
// 7.读结果管道并进行打印
readPipe();//这里又重新创建管道了

关键运行指令的函数以及发送信息的结构进行分析

convertCommand(pid, str, &msg);——前台将dir这样的windowns的指令转换linux下的指令ls
后台执行cmd指令并执行之后的结果void execCommand(const char *cmd, vector &res)
这里单独写了一个demo进行示例理解:

//执行shell指令,并将结果输出到字符串中
int myexec(const char *cmd, string &resvec)
{
    resvec.clear();
    FILE *pp = popen(cmd, "r"); //建立管道
    if (!pp)
    {
        return -1;
    }
    char tmp[1024]; //设置一个合适的长度,以存储每一行输出
    while (fgets(tmp, sizeof(tmp), pp) != NULL)
    {
        if (tmp[strlen(tmp) - 1] == '\n')
        {
            tmp[strlen(tmp) - 1] = '\0'; //去除换行符
        }
        resvec.append(tmp);
    }
    pclose(pp); //关闭管道
    return resvec.size();
}

将ls进行执行,第一次结果
第一次结果
第二次结果:
在这里插入图片描述
可以看出pp就是命令行执行的结果,并能够通过一步步遍历获得输出结果
因为这里open里面的属性是r,表示对应到表述命令行里的标准输出,输入到返回的文件指针指向中去。以下是popen的详细理解说明

FILE * popen ( const char * command , const char * type );
int pclose ( FILE * stream );
  1. type 参数: 是 “r” 则文件指针连接到 command 的标准输出;如果 type 是 “w” 则文件指针连接到 command 的标准输入。
  2. command 参数:是一个指向以 NULL 结束的 shell 命令字符串的指针。shell 将执行这个命令。
  3. 返回的是标准IO流

头文件对应作用

#include <sys/types.h>

  • 包含了一些基本的数据类型
  • key_t表示进程的关键字,能够区别同一程序的不同进程
    #include <sys/ipc.h>
  • sys/ipc.h是一种通讯格式,可在2个(多数情况下)或多个进程间传递信息
  • 匿名管道以及命名管道/消息队列都是需要这个头文件的
    #include <sys/msg.h>
  • 定义了消息队列相关
  • 消息队列中发送以及接收消息
    == #include<sys/wait.h>==
  • 定义了使用wait相关的定义
  • waitpid使指定进程堵塞
    #include <sys/prctl.h>
  • 给进程或者线程重命名或者其他操作,基本的进程管理函数
  • 实现在进程退出来的时候
  • 我有一个分叉到子进程的进程。如果父进程存在,子进程不应该存在。所以,我在子进程中调用:: prctl(PR_SET_PDEATHSIG,SIGKILL)来杀死父进程。最后发生的是父线程调用pthread_exit,并且该线程最终成为杀死子进程的催化剂。在父线程退出时调用prctl(PR_SET_PDEATHSIG,SIGNAL),而不是父进程退出
    #include <sys/stat.h>
  • 文件状态头文件还有一些常量
  • mkfifo 创建一个名为 PATH 的新 FIFO,具有权限位 MODE
  • umask(给文件管理权限赋予到最大值)
    #include <fcntl.h>
  • cntl.h,是unix标准中通用的头文件,其中包含的相关函数有 open,fcntl,shutdown,unlink,fclose等!
  • extern声明变量在其他地方,所以有时候比如unlink就在其他文件中,但是最终指向是
    #include <unistd.h>
  • unistd.h 中所定义的接口通常都是大量针对系统调用的封装(英语:wrapper functions),如 fork、pipe 以及各种 I/O 原语(read、write、close 等等)。
    #include <errno.h>
  • errno.h 是C语言C标准函式库里的标头档,定义了通过错误码来回报错误资讯的宏。
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值