Linux进程通信——IPC、管道、FIFO的引入

进程间的通信------IPC

进程间通信 (IPC,InterProcess Communication) 是指在不同进程之间传播或交换信息。

IPC的方式通常有管道 (包括无名管道和命名管道) 、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。

单机:若是在单一机器上,则为单机通信

半双工管道
全双工管道
消息队列
信号量
共享内存

多机:多台机器上,为网络通信

网络通信种类如下:

管道

管道,通常指无名管道(之所以叫无名管道是因为没有文件名),是 UNIX 系统IPC最古老的形式。

特点

(1)它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
(2)它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
(3)它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
(4)管道中不储存数据,数据写进后读取就会消失,类似于水流。

原型

cs 复制代码
#include <unistd.h>     //函数pipe包含的头文件
int pipe(int fd[2]);    // 返回值:若成功返回0,失败返回-1

当一个管道建立时,它会创建两个文件描述符: fd[0]为读而打开,fd[1]为写而打开。如下图:

要关闭管道只需将文件描述符关闭即可。

cs 复制代码
close(fd[0]);
close(fd[1]);

创建

**单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 IPC 半双工通道。**如下图所示:

左图为调用fork函数创建了IPC半双工管道,右图为父进程到子进程的管道。

代码示例

cs 复制代码
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{
        int pid=0;
        int fd[2];
        char buf[128];

        if(pipe(fd) == -1)//如果管道创建失败
	    {
                printf("creat pipe failed\n");
        }

        pid=fork();
        if(pid<0)//创建子进程失败
	    {
                printf("creat failed\n");
        }
        else if(pid >0)//进入父进程
	    {
                printf("this is father\n");
                close(fd[0]);//关闭读文件描述符
                write(fd[1],"read from father",strlen("read from father"));//将内容写入管道中
		        wait();//等待子进程
	    }
	    else//进入子进程
	    {
                printf("this is child\n");
                close(fd[1]);//关闭写文件描述符
                read(fd[0],buf,128);//将管道中内容读取到buf
                printf("read form father:%s\n",buf);
		        exit(0);//子进程退出
        }
        return 0;
}

以上代码实现了管道通信,但read在没有读取到内容时会阻塞,直到读取内容后才正常运行,可以做以下调试:

cs 复制代码
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{
        int pid=0;
        int fd[2];
        char buf[128];

        if(pipe(fd) == -1)//如果管道创建失败
	    {
                printf("creat pipe failed\n");
        }

        pid=fork();
        if(pid<0)//创建子进程失败
	    {
                printf("creat failed\n");
        }
        else if(pid >0)//进入父进程
	    {
                sleep(3);//进入父进程后睡眠3秒再运行
                printf("this is father\n");
                close(fd[0]);//关闭读文件描述符
                write(fd[1],"read from father",strlen("read from father"));//将内容写入管道中
		        wait();//等待子进程
	    }
	    else//进入子进程
	    {
                printf("this is child\n");
                close(fd[1]);//关闭写文件描述符
                read(fd[0],buf,128);//将管道中内容读取到buf
                printf("read form father:%s\n",buf);
		        exit(0);//子进程退出
        }
        return 0;
}

方案是创建父进程后让其睡眠3秒后再执行父进程中的代码,可见在睡眠时子进程先运行其代码,但并没有执行read函数,此时表现为堵塞状态,直到3秒后父进程正常运行并将内容写入管道中,子进程才读取管道中的内容并成功打印。

FIFO

FIFO,也称为命名管道,它是一种文件类型。

特点

1.FIFO可以在无关的进程之间交换数据,与无名管道不同。
2.FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

原型

cs 复制代码
#include <sys/stat.h>
int mkfifo (const char *pathname, mode t mode) ;// 返回值: 成功返回0,出错返回-1

第一部分参数是文件的路径,第二部分的 mode 参数与open函数中的 mode 相同 。一旦创建了一个 FIFO,就可以用一般的文件1/0函数操作它。如:open、read、write等函数。

当 open 一个FIFO时,是否设置非阻塞标志 (O_NONBLOCK) 的区别:

  • 若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
  • 若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 。如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。

创建

FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据,并且"先进先出"。

代码示例

read.c

cs 复制代码
#include <stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include <errno.h>
#include <fcntl.h>

int main()
{
	int fd = 0;
	int n_read = 0;
	char buf[128];
	
	if(mkfifo("./file",0600) == -1 && errno!=EEXIST)//判断管道出错原因是不是在于已经创建
	{
		printf("mkfifo failure\n");
        perror("why");
	}
	else
	{
		if(errno==EEXIST)//管道已经创建
		{
            printf("file eexist\n");
        }
		else//管道未创建
		{
            printf("mkfifo successed\n");
        }
    }
	fd = open("./file",O_RDONLY);//只写方式打开
	printf("open file succeed\n");
	n_read = read(fd,buf,128);//需要等待写入完毕才能读取,才能执行下列代码
	printf("read %d byte from file,context is %s\n",n_read,buf);
	close(fd);

	return 0;
}

write.c

cs 复制代码
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include <errno.h>
#include <fcntl.h>

int main()
{
        char *buf="hello word!!!!!!!!!!";
        int fd;

	    fd = open("./file",O_WRONLY);//只写方式打开
        printf("write file success\n");
        write(fd,buf,strlen(buf));//将字符串内容写入fd中,写完才可以读取
	    close(fd);
	
        return 0;
}

可见执行read文件时,显示管道已经存在后停止执行后续代码,当执行write文件后read文件继续执行后续代码,实现管道间的通信。

相关推荐
小糖学代码8 小时前
LLM系列:1.python入门:3.布尔型对象
linux·开发语言·python
shizhan_cloud8 小时前
Shell 函数的知识与实践
linux·运维
Deng8723473488 小时前
代码语法检查工具
linux·服务器·windows
霍夫曼10 小时前
UTC时间与本地时间转换问题
java·linux·服务器·前端·javascript
月熊11 小时前
在root无法通过登录界面进去时,通过原本的普通用户qiujian如何把它修改为自己指定的用户名
linux·运维·服务器
大江东去浪淘尽千古风流人物12 小时前
【DSP】向量化操作的误差来源分析及其经典解决方案
linux·运维·人工智能·算法·vr·dsp开发·mr
赖small强13 小时前
【Linux驱动开发】NOR Flash 技术原理与 Linux 系统应用全解析
linux·驱动开发·nor flash·芯片内执行
IT运维爱好者14 小时前
【Linux】LVM理论介绍、实战操作
linux·磁盘扩容·lvm
LEEE@FPGA14 小时前
ZYNQ MPSOC linux hello world
linux·运维·服务器
郝学胜-神的一滴14 小时前
Linux定时器编程:深入理解setitimer函数
linux·服务器·开发语言·c++·程序人生