Linux——进程通信(管道)笔记

进程通信方式

当今比较常用的有:

  1. 管道(使用简单)
  2. 信号(开销最小)
  3. 共享映射区(无血缘关系)
  4. 本地套接字(最稳定)

管道

管道又分为:无名管道和有名管道FIFO

无名管道:只能用于有亲属关系的进程间
有名管道:可以用在有亲属关系的进程间也可用在无亲属关系的进程间

无名管道

管道是基于文件描述符的通信方式,当一个管道建立时,它会创建两个文件描述符 fds[0]和 fds[1],其中 fds[0]固定用于读管道,而 fd[1]固定用于写管道,这样就构成了一个半双工的通道。

管道关闭时只需将这两个文件描述符关闭即可,可使用普通的 close 函数逐个关闭各个文件描述符。

管道其本质是一个伪文件(实为内核缓冲区)
管道的原理:管道实为内核使用环形队列的机制,借助内核缓冲区(4k)实现。

管道的局限性:

  1. 数据自己读不能自己写
  2. 数据一旦被读走,便不在管道中存在,不可反复读取
  3. 由于管道采用半双工通信方式,因此,数据只能在一个方向上流动
  4. 只能在有亲属关系的进程间使用管道

7种文件类型:

  • — 文件
  • d 目录
  • l 符号链接
  • s 套接字
  • b 块设备
  • c 字符设备
  • p 管道

前三个是占用内存空间的;后四个是不占用内存空间的,并且都是伪文件

管道创建 pipe( )

在这里插入图片描述

管道读写注意点

  • 只有在管道的读端存在时向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的 SIFPIPE 信号(通常 Broken pipe 错误)。
  • 向管道中写入数据时,linux 将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读取管道缓冲区中的数据,那么写操作将会一直阻塞。
  • 父子进程在运行时,它们的先后次序并不能保证,因此,在这里为了保证父进程已经关闭了读描述符,可在子进程中调用 sleep 函数。

代码示例:

/* pipe.c */

#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<sys/wait.h>
#define MAX_DATA_LEN	256
#define DELAY_TIME	1

int main()
{
	pid_t pid;
	int pipe_fd[2];
	char buf[MAX_DATA_LEN];
	const char data[] = "Pipe Test Program";
	int real_read, real_write;

	memset((void*)buf, 0, sizeof(buf));

	/* 创建管道 */
	if (pipe(pipe_fd) < 0)
	{
		printf("pipe create error\n");
		exit(1);
	}

	/* 创建一子进程 */
	if ((pid = fork()) == 0)
	{
		/* 子进程关闭写描述符,并通过使子进程暂停1秒等待父进程已关闭相应的读描述符 */
		close(pipe_fd[1]);
		sleep(DELAY_TIME * 3);

		/* 子进程读取管道内容 */
		if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
		{
			printf("%d bytes read from the pipe is '%s'\n", real_read, buf);
		}

		/* 关闭子进程读描述符 */
		close(pipe_fd[0]);
		exit(0);
	}
	else if (pid > 0)
	{
		/* 父进程关闭读描述符,并通过使父进程暂停1秒等待子进程已关闭相应的写描述符 */
		close(pipe_fd[0]);
		sleep(DELAY_TIME);

		/* 父进程向管道中写入字符串 */
		if((real_write = write(pipe_fd[1], data, strlen((const char*)data))) !=  -1)
		{
			printf("Parent wrote %d bytes : '%s'\n", real_write, data);
		}

		/*关闭父进程写描述符*/
		close(pipe_fd[1]);

		/*收集子进程退出信息*/
		waitpid(pid, NULL, 0);
		exit(0);
	}
}

结果:
在这里插入图片描述

有名管道FIFO

有名管道FIFO,它可以使互不相关的两个进程实现彼此通信。

对于读进程

  • 若该管道是阻塞打开,且当前 FIFO 内没有数据,则对读进程而言将一直阻塞直到有
    数据写入。
  • 若该管道是非阻塞打开,则不论 FIFO 内是否有数据,读进程都会立即执行读操作。

对于写进程

  • 若该管道是阻塞打开,则写进程而言将一直阻塞直到有读进程读出数据。
  • 若该管道是非阻塞打开,则当前 FIFO 内没有读操作,写进程都会立即执行读操作。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码演示:

读端:

/* fifo_read.c */

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>		

#define MYFIFO			"/tmp/myfifo"
#define MAX_BUFFER_SIZE		PIPE_BUF /*定义在于limits.h中*/

int main()
{
	char buff[MAX_BUFFER_SIZE];
	int  fd;
	int  nread;
	
	/* 判断有名管道是否已存在,若尚未创建,则以相应的权限创建*/
	if (access(MYFIFO, F_OK) == -1) 
	{
		if ((mkfifo(MYFIFO, 0666) < 0) && (errno != EEXIST))
		{
			printf("Cannot create fifo file\n");
			exit(1);
		}
	}
	
	/* 以只读阻塞方式打开有名管道 */
	fd = open(MYFIFO, O_RDONLY);
	if (fd == -1)
	{
		printf("Open fifo file error\n");
		exit(1);
	}
	
	while (1)
	{
		memset(buff, 0, sizeof(buff));
		if ((nread = read(fd, buff, MAX_BUFFER_SIZE)) > 0)
		{
			printf("Read '%s' from FIFO\n", buff);
		}		
	}	
	
	close(fd);	
	exit(0);
}	

写端:


/* fifo_write.c */

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>		

#define MYFIFO			"/tmp/myfifo"	/* 有名管道文件名*/
#define MAX_BUFFER_SIZE		PIPE_BUF 	/*定义在于limits.h中*/
	
int main(int argc, char * argv[]) /*参数为即将写入的字符串*/
{
	int fd;
	char buff[MAX_BUFFER_SIZE];
	int nwrite;
	
	if(argc <= 1)
	{
		printf("Usage: ./fifo_write string\n");
		exit(1);
	}
	sscanf(argv[1], "%s", buff);
	
	/* 以只写阻塞方式打开FIFO管道 */
	fd = open(MYFIFO, O_WRONLY);
	if (fd == -1)
	{
		printf("Open fifo file error\n");
		exit(1);
	}
	
	/*向管道中写入字符串*/
	if ((nwrite = write(fd, buff, MAX_BUFFER_SIZE)) > 0)
	{
		printf("Write '%s' to FIFO\n", buff);
	}
	
	close(fd);
	exit(0);
}

结果:
要先执行读端,因为管道是在读端简历的;如果先执行写端的话,会报错:管道文件打开失败

写端:
在这里插入图片描述
读端:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值