一个进程中的多个线程共享一个进程的堆等内存空间,所以实现数据交互是很方便的;但多进程架构中,要想实现多进程间的数据交互相对就困难很多!

进程间通信(IPC,InterProcess Communication)是指在不同进程之间传递或交换信息。IPC常见的方式有:管道(无名管道、命名管道)、消息队列、信号量、共享内存、磁盘文件、Socket等,其中Socket网络方式可以实现不同主机上的多进程IPC

一个带坑的小例子

下面使用fork()、pipe()实现一个简单的Linux平台下的多进程、多进程通信的程序

#include

#include

#include

int main()

{

int pipefd[2]; //两个文件描述符

pid_t pid;

char buff[20];

int i;

//创建管道

if(0 > pipe(pipefd)){

printf("Create Pipe Error!\n");

}

//创建2个子进程

for(i=0; i<2; i++){

pid = fork();

if((pid_t)0 >= pid){

sleep(1);

break;

}

}

//如果fork返回值小于0,说明创建子进程失败

if((pid_t)0 > pid){

printf("Fork Error!\n");

return 3;

}

//如果fork返回值为0,表示进入子进程的逻辑

if((pid_t)0 == pid){

//发送格式化数据给主进程

FILE *f;

f = fdopen(pipefd[1], "w");

fprintf(f, "I am %d\n", getpid());

fclose(f);

sleep(1);

//接收父进程发过来的数据

read(pipefd[0], buff, 20);

printf("MyPid:%d, Message:%s", getpid(), buff);

}

//进入父进程的逻辑

else{

//循环接收所有子进程发过来的数据,并且返回数据给子进程

for(i=0; i<2; i++){

//接收子进程发来的数据

read(pipefd[0], buff, 20);

printf("MyPid:%d, Message:%s", getpid(), buff);

//发送数据给子进程

write(pipefd[1], "Hello My Son\n", 14);

}

//这里调用sleep(3)主要的作用是等待子进程运行结束

//当然这样并不是很规范!

sleep(3);

}

return 0;

}

编译程序gcc process.c -o process,然后执行./process输入信息如下

但我们可以发现输出的内容有一些异常,比如第二行最开始怎么有一个@字符、最后一行明显丢失了一些字符信息等

上面的程序还不止是输出不符合预期这个表面的问题,还存在诸多的坑,都是因为一开始对于多进程、管道的深入机制理解不正确造成的!

下面针对Linux的管道进行比较深入的挖掘,就可以发现上面的小程序中存在很多的坑

管道是阻塞的

管道读写是阻塞的,当管道中没有数据,但进程尝试去读的时候就会阻塞进程,比如

#include

#include

#include

int main()

{

int pipefd[2];

pid_t pid;

char buff[20];

int i;

if(0 > pipe(pipefd)){

printf("Create Pipe Error!\n");

}

pid = fork();

if((pid_t)0 > pid){

printf("Fork Error!\n");

return 3;

}

if((pid_t)0 == pid){

//write(pipefd[1], "Hello\n", 6);

}

else{

read(pipefd[0], buff, 20);

printf("MyPid:%d, Message:%s", getpid(), buff);

}

return 0;

}

其运行效果如下,可以看到主进程阻塞住了

可以修改让子进程往管道中写入数据,主进程再去读,这样就不会阻塞了

#include

#include

#include

int main()

{

int pipefd[2];

pid_t pid;

char buff[20];

int i;

if(0 > pipe(pipefd)){

printf("Create Pipe Error!\n");

}

pid = fork();

if((pid_t)0 > pid){

printf("Fork Error!\n");

return 3;

}

if((pid_t)0 == pid){

}

else{

read(pipefd[0], buff, 20);

printf("MyPid:%d, Message:%s", getpid(), buff);

}

return 0;

}

运行程序可以看到主进程没有阻塞

管道是半双工的

所谓半双工,意思就是只能在一个方向上传递数据,对于pipe,只能从pipe[1]写,从pipe[0]读,只能在一个方向传递数据;可以结合socket来理解,socket是全双工的,也就是针对一个socket既可以写又可以读

第一个例程中创建了一个管道,但却希望通过这个管道实现主进程传递数据给子进程、子进程传递数据给主进程,完全是想在两个方向传递数据,结果导致主进程和2个子进程同时既往管道里写,又从管道里读,所以出现了上述诡异的现象

比如下面这个例子,创建一个管道,但不创建子进程,可以在主进程既写又读管道!

#include

#include

#include

int main()

{

int pipefd[2];

pid_t pid;

char buff[20];

int i;

if(0 > pipe(pipefd)){

printf("Create Pipe Error!\n");

}

write(pipefd[1], "Hello\n", 6);

read(pipefd[0], buff, 20);

printf("MyPid:%d, Message:%s", getpid(), buff);

return 0;

}

因为管道是半双工的,所以要想在保证数据不乱的情况下,不能在多进程应用中只使用一个管道,需要一套管道,有的是数据从主进程到子进程,有的是数据从子进程到主进程

完善后的程序

#include

#include

#include

int main()

{

//管道1,用于子进程发送数据给主进程

int pipefd[2];

//管道数组2,用于主进程分别发数据给子进程

int pipearr[3][5];

pid_t pid;

char buff[20];

int i;

//创建管道

if(0 > pipe(pipefd)){

printf("Create Pipe Error!\n");

}

for(i=0; i<3; i++){

if(0 > pipe(pipearr[i])){

printf("Create Pipe Error!\n");

}

}

//创建3个子进程

for(i=0; i<3; i++){

pid = fork();

//创建子进程失败

if((pid_t)0 > pid){

printf("Fork Error!\n");

return 3;

}

//子进程逻辑

if((pid_t)0 == pid){

//发送格式化数据给主进程

FILE *f;

f = fdopen(pipefd[1], "w");

fprintf(f, "I am %d\n", getpid());

fclose(f);

//接收父进程发过来的数据

read(pipearr[i][0], buff, 20);

printf("MyPid:%d, Message:%s", getpid(), buff);

//完成后及时退出循环,继续循环会出大问题,和fork的运行逻辑有关!

break;

}

}

//主进程逻辑

if((pid_t)0 < pid){

//循环接收所有子进程发过来的数据,并且返回数据给子进程

for(i=0; i<3; i++){

//接收子进程发来的数据

read(pipefd[0], buff, 20);

printf("MyPid:%d, Message:%s", getpid(), buff);

//发送数据给子进程

write(pipearr[i][6], "Hello My Son\n", 14);

}

sleep(3);

}

return 0;

}

编译后的运行效果如下:

简单说一下上面的程序逻辑:

首先是有两种管道

第一种只有一个:创建的3个子进程都往这里面写,主进程从这里面读取数据

第二种有一组,每个子进程一个:主进程分别往3个管道中写,每个子进程对应从属于自己的管道中读

针对第二种,很明显一个写,一个读,可以保证并发安全。但第一种呢,多个子进程都往一个管道里面写,会不会有问题,这个需要特别注意:

当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性

当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性

上面多个子进程同时往pipefd中写入的数据小于PIPE_BUF,所以是原子性的,另外只有主进程一个进程在读,所以可以保证数据的完整性。在webbench中其实就是这样使用管道的

可以编译下面的程序,查看PIPE_BUF的值

#include

#include

int main()

{

printf("PIPE_BUF = %d\n", PIPE_BUF);

return 0;

}

编译后运行效果如下:

linux多进程能同时执行吗,Linux开发简单多进程应用相关推荐

  1. linux的保存命令执行结果,linux下把命令执行的结果输出

    我们知道在linux下当我们想把文字用命令输入到一个文本下时可以用echo命令 例:echo "nihao" > /z.txt 同样当我们想把命令执行的结果也输入到一个文本时 ...

  2. linux脚本命令顺序执行命令,linux – nohup多个顺序命令

    我需要一个接一个地发出两个命令.第二个命令必须在第一个命令之后执行.以下是命令: tar -zcf archive.tar.gz.tmp mydir mv archive.tar.gz.tmp arc ...

  3. Linux系统vi编辑器执行命令,linux下vi编辑器命令

    分类: 嵌入式 前言 在嵌入式linux开发中,进行需要修改一下配置文件之类的,必须使用vi,因此,熟悉 vi 的一些基本操作,有助于提高工作效率. 一,模式 vi编辑器有3种模式:命令模式.输入模式 ...

  4. linux设置用户的执行权限,Linux下ACL权限控制以及用sudo设置用户对命令的执行权限...

    ACL权限分配 1.setfacl命令设置文件权限 setfacl -m u:user1:rw root.txt setfacl -m u:user2:rwx root.txt 2.getfacl命令 ...

  5. linux用root权限执行命令,Linux root权限获取之sudo命令详解

    1. 了解sudo的作用 日常操作中为了避免一些误操作,更加安全的管理系统,我们一般使用普通用户登录系统,而非root.当需要执行一些管理员命令操作时,再切换成root用户身份去执行. 普通用户切换到 ...

  6. linux编程运行命令,如何运行linux程序

    原标题:如何运行linux程序 搞懂linux程序如何运行是一件重要的事情,这是为我们进一步发展打下了坚实基础的一步.本文将通过实例来详细地讲解如何运行linux程序.希望对于大家理解有帮助. 首先, ...

  7. Linux 高并发服务器实战 - 2 Linux多进程开发

    Linux 高并发服务器实战 - 2 Linux多进程开发 进程概述 概念1: 概念2: 微观而言,单CPU任意时刻只能运行一个程序 并发:两个队列交替使用一台咖啡机 并行:两个队列同时使用两台咖啡机 ...

  8. 【ketlle】本地开发kitlle job提交至linux生产环境上执行流程

    [ketlle]本地开发kitlle job提交至linux生产环境上执行 前言 操作步骤 后记 前言 因为kettle提供可视化界面,所以我们在本地开发的时候拖拽开发十分便捷,开发完成后,我们需要丢 ...

  9. 《嵌入式 Linux应用程序开发标准教程(第2版)》——第1章 Linux快速入门 1.1 嵌入式Linux基础...

    本节书摘来自异步社区<嵌入式 Linux应用程序开发标准教程(第2版)>一书中的第1章,第1.1节,作者 华清远见嵌入式培训中心,更多章节内容可以访问云栖社区"异步社区" ...

最新文章

  1. 轻松学Pytorch – 行人检测Mask-RCNN模型训练与使用
  2. C++函数指针解引用
  3. Ocelot(一)- .Net Core开源网关
  4. java 接口 泛型示例,java泛型接口实现示例
  5. 数据运营者的福音:海量数据处理利器Greenplum
  6. 未来计算机会有多发达,人工智能到底有多强?未来人工智能会主宰世界?
  7. Spring Cloud Gateway 原生支持接口限流该怎么玩
  8. 【经典回放】多种语言系列数据结构算法:数组
  9. socket timeout是什么引起的_MySQL C API 参数 MYSQL_OPT_READ_TIMEOUT 的一些行为分析
  10. 两台计算机怎样共享一台打印机共享文件夹,两台电脑怎么共享文件和打印机
  11. 分治-Strassen矩阵乘法
  12. ssh - 安全外壳协议的详解,为什么使用它
  13. android sdk build tools 版本,SDK Build Tools 版本说明
  14. 车牌号识别易语言代码
  15. 关于路由器认证校园网的可行方案.md
  16. I2C 总线协议初探 - STM32 I2C 接口外设学习笔记
  17. java简单的学生管理系统界面_java 学生信息管理系统(图形界面)
  18. 哔哩下载姬(downkyi)v1.4.0 B站视频下载工具 哔哩哔哩视频解析
  19. 河南省第十届ACM程序设计大赛参赛心得
  20. contour 函数详解

热门文章

  1. Python replace函数
  2. php+打开图片二进制文件,php图片文件、二进制流、base64格式相互转化
  3. 考博就业该如何选择呢
  4. LED灯带的制作过程
  5. 英语b级分数计算机,英语b级多少分算过?
  6. Fireworks制作经典的扫光字GIF动画
  7. python cx oracle 11g,怎么在python中使用cx_Oracle模块连接Oracle数据库
  8. win10中使用sqlserver2008r2 SQL Server 配置管理器
  9. 2012迅雷校园招聘笔试题——厦门大学站
  10. 利用前景和背景的mask图来得到边缘平滑的图像(graphcut实现)