01 管道

1. 管道简单理解
  • 管道实质上是在内存或文件系统中创建一个共享文件,通讯方共享这个文件,每一方只允许对文件进行读或者写,数据流是单向的。
  • 管道内是字节流传递,没有分界线,不能随机读取,只能顺序读写。
  • 管道分为匿名管道和命名管道
2. 匿名管道
  • 使用同一管道的进程间必须有亲属关系,对文件系统不可见,内核在内存中模拟管道
    管道应用:ls -al | grep xxx : ls -al 输出为管道的输入,管道的输出为grep的输入
  • 数据拷贝:
    写入:用户空间buf到内核,内核到内存
    读取:内存到内核,内核到用户空间buf
  • api
#include <unistd.h>
// fd为两个文件描述符,fd[0]为读,fd[1]为写
int pipe(int fd[2]);  

// 向管道中写数据
ssize_t write(int fd, const void *buf, size_t count);

// 从管道中读数据,返回的是读取的数据
ssize_t read(int fd, void *buf, size_t count);

// 关闭管道某一端
int close(int fd);

// 管道和shell command的交互
// type为r时,command的输出为管道的输入,type为w时,command的输入为shell的输出。
// popen返回的文件流中保存的是管道的输出。可以通过fgets获取,
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
3. 命名管道
  • FIFO ,进程间可以没有亲属关系,文件系统可见

  • 命名管道提供了一个路径名与之关联,以FIFO的文件形式存在于文件系统中,在文件系统中产生一个物理文件,其他进程只要访问该文件路径,就能彼此通过管道通信。

  • FIFO文件与普通文件的区别:
    普通文件无法实现字节流方式管理,而且多进程之间访问共享资源会造成意想不到的问题;
    FIFO文件采用字节流方式管理,遵循先入先出原则,不涉及共享资源访问。

  • api

#include <sys/types.h>
#include <sys/stat.h>
// 创建管道,pathname为文件系统中的路径名
int mkfifo(const char *pathname, mode_t mode);
// #define	FILE_MODE	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

// 打开管道
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode)

// 从文件系统中删除这个命名管道
int unlink(const char *pathname);

// 关闭管道,参数为open的返回值,为文件描述符
int close(int fd);
4. 管道和FIFO的额外参数 和 阻塞规则
  • 设置non_block标志

1)匿名管道只能通过fcntl设置, FIFO可以通过open指定

int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
	printf("fcntl get error \n");
}	
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0) {
	printf("fcntl set error\n");
}
int fd = open(FIFO_NAME, O_WRONLY|O_NONBLOCK, 0);

2)阻塞规则

对于open,有读无写,读阻塞直到有open写,设置O_NONBLOCK后成功返回
对于open,有写无读,阻塞直到有open读,设置O_NONBLOCK后返回ENXIO
对于read,有读无写,返回0,
对于read, 有读有写,如果写不及,阻塞知道有数据或者FIFO不在有写者,设置O_NONBLOCK后返回EAGAIN
对于write,有写无读,产生SIGPIPE信号,SIGPIPE信号默认终止程序,如果捕获或者显示忽略,则会返回错误EPIPE
5. 匿名管道和命名管道的区别:
1. 共享管道的进程间,匿名管道需要亲属关系,命名管道不需要
2. 匿名管道是通过内存文件实现,命名管道是通过文件系统中的文件实现。
6. 代码demo

6.1 pipe:创建管道后fork产生子进程,父子进程共享管道,子关闭写,父关闭读,实现父写子读。

#include <sys/wait.h>
#include <stdio.h>  
#include <stdlib.h>
#include <unistd.h>  
#include <string.h>

#define BUF_SIZE 100

int main(int argc, char *argv[]) 
{
	int fd[2];
	char buf[BUF_SIZE];
	if (argc != 2) {
		printf("input fail\n");
		return -1;
	}
	if (pipe(fd) == -1) {
		printf("pipe error\n");
		return -1;
	}
	
	switch(fork()) 
	{
		case 0:
			// son
			if (close(fd[1]) == -1) {
				printf("son close wirte fail\n");
				return -1;
			}
			while(1) {
				int num = read(fd[0], buf, BUF_SIZE);
				if (num == -1) {
					printf("read fail \n");
					return -1;
				}
				if (num == 0) {
					printf("end of file \n");
					break;
				}
				printf("son get msg: %s\n", buf);
			}
			printf("son read over\n");
			if (close(fd[0]) == -1) {
				printf("son close read fail\n");
				return -1;
			}
			return 0;
			break;
		case -1: 
			printf("fork error \n");
			break;
		default:
			// father
			if (close(fd[0]) == -1) {
				printf("father close read fail\n");
				return -1;
			}
			if (write(fd[1], argv[1], strlen(argv[1])) != strlen(argv[1])) {
				printf("father write fail\n");
				return -1;
			}
			
			if (close(fd[1]) == -1) {
				printf("father close fail\n");
				return -1;
			}
			wait(NULL); 
			printf("father gone\n");
			return 0;
	}
}

---
./app xxxxxxxxxd
son get msg: xxxxxxxxxdZ
end of file
son read over
father gone

6.2 popen,与shell的交互

#include <sys/wait.h>
#include <stdio.h>  
#include <stdlib.h>
#include <unistd.h>  
#include <string.h>

#define	MAXLINE		4096

int main(int argc, char **argv)
{
	size_t	n;
	char	buff[MAXLINE], command[MAXLINE];
	char	*rptr;
	if ( (rptr = fgets(buff, MAXLINE, stdin)) == NULL && ferror(stdin))
	{
		printf("fgets error\n"); return -1;
	}

	n = strlen(buff);		
	if (buff[n-1] == '\n') {
		n--;				
	}
	snprintf(command, sizeof(command), "cat %s", buff);
	FILE *fp = popen(command, "r"); // command执行后的结果作为管道的输入
	while (fgets(buff, MAXLINE, fp) != NULL) { // 获取管道内的数据,通过文件流fp
		fputs(buff, stdout);
	}

	pclose(fp);
	exit(0);
}
--
./app 
输入某个文件
输出为cat 文件后的内容

6.3 mkfifo: 客户端传递一个文件名,服务器获取文件内容返回后退出
testFifoServer.cpp

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

#define FIFO  "/tmp/testFifo1"
#define FIFO2  "/tmp/testFifo12"
#define	FILE_MODE	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define	MAXLINE		4096

int main()
{
	if (mkfifo(FIFO, FILE_MODE) < 0 && errno != EEXIST) {
		printf("mkfifo error1 \n"); return -1;
	}
	if (mkfifo(FIFO2, FILE_MODE) < 0 && errno != EEXIST) {
		unlink(FIFO);
		printf("mkfifo error2 \n"); return -1;
	}
	int readFd = open(FIFO, O_RDONLY, 0); // 打开管道1的读,阻塞
	int writeFd = open(FIFO2, O_WRONLY, 0); 
	
	int n;
	char	buff[MAXLINE+1];
	if((n = read(readFd, buff, MAXLINE)) == 0) { // 此时客户端open了写,服务端阻塞在此处等待客户端写数据
		printf("read size0 \n");
	}
	buff[n] = '\0';
	
	int	fd;
	if ((fd = open(buff, O_RDONLY)) < 0) { // 打开client传过来的管道名
		snprintf(buff + n, sizeof(buff) - n, ": can't open, %s\n", strerror(errno));
		n = strlen(buff);
		write(writeFd, buff, n);
	} else {
		while ((n = read(fd, buff, MAXLINE)) > 0)
			write(writeFd, buff, n);
		close(fd);  // 关闭文件
	}
	close(writeFd);
	printf(" server out\n");
	exit(0);
}

testFifoClient.cpp

// 头文件同server
int main()
{
	if (mkfifo(FIFO1, FILE_MODE) < 0 && errno != EEXIST) {
		printf("mkfifo error1 \n"); return -1;
	}
	if (mkfifo(FIFO2, FILE_MODE) < 0 && errno != EEXIST) {
		unlink(FIFO1);
		printf("mkfifo error2 \n"); return -1;
	}
	int writeFd = open(FIFO1, O_WRONLY, 0); // 客户端打开写,服务端的open读阻塞返回
	int readFd = open(FIFO2, O_RDONLY, 0);  
	
	char buff[MAXLINE];
	char *rptr;
	if ((rptr = fgets(buff, MAXLINE, stdin)) == NULL && ferror(stdin)) {
		printf("client get stdin data error \n"); return -1;
	}
	size_t len = strlen(buff);
	if (buff[len-1] == '\n') {
		len--;
	}
	if (write(writeFd, buff, len) != len) {
		printf("client write error\n"); return -1;
	}
	
	int n;
	while ((n = read(readFd, buff, MAXLINE)) > 0) { // 客户端读阻塞知道服务端写
		//printf("client read get n: %d\n", n);
		write(STDOUT_FILENO, buff, n);
		if (write(STDOUT_FILENO, buff, n) != n) {
			printf("write error \n"); return -1;
		}
	}
		
	close(readFd);
	close(writeFd);

	unlink(FIFO1); 
	unlink(FIFO2);
	printf("client end\n");
	return 0;
}

linux api查询 : https://www.die.net/
网络编程 卷2 进程间通信
linux编程手册

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值