Linux系统编程之进程间通信(管道)


进程间通信的常用方式及特征:

管道:简单

信号:开销小

mmap映射:非血缘关系进程间

socket(本地套接字):稳定

一、匿名管道 pipe

匿名管道:

实现原理:内核借助环形队列机制,使用内核缓冲区实现。

特质:	1. 伪文件

		2. 管道中的数据只能一次读取。

		3. 数据在管道中,只能单向流动。

局限性:1. 自己写,不能自己读。

		2. 数据不可以反复读。

		3. 半双工通信。

		4. 血缘关系进程间可用。


pipe函数:创建,并打开管道。

int pipe(int fd[2]);

参数:	fd[0]: 读端。

		fd[1]: 写端。

返回值: 成功: 0

	 	失败: -1 errno

管道的读写行为:

读管道:
	1. 管道有数据,read返回实际读到的字节数。

	2. 管道无数据:1)无写端,read返回0 (类似读到文件尾)

				  2)有写端,read阻塞等待。

写管道:
	1. 无读端, 异常终止。 (SIGPIPE导致的)

	2. 有读端:	1) 管道已满, 阻塞等待

				2) 管道未满, 返回写出的字节个数。

1.1 一写一读

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

int main(int argc, char *argv[])
{
    int ret;
    int fd[2];
    pid_t pid;

    char *str = "hello pipe\n";
    char buf[1024];

    ret = pipe(fd);
    if (ret == -1)
        sys_err("pipe error");

    pid = fork();
    if (pid > 0) {
        close(fd[0]);       // 关闭读段
        sleep(3);
        write(fd[1], str, strlen(str));
        close(fd[1]);
    } else if (pid == 0) {
        close(fd[1]);       // 子进程关闭写段
        ret = read(fd[0], buf, sizeof(buf));
        printf("child read ret = %d\n", ret);
        write(STDOUT_FILENO, buf, ret);

        close(fd[0]);
    }
	return 0;
}

1.2 多写一读

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

int main(void)
{
	pid_t pid;
	int fd[2], i, n;
	char buf[1024];

	int ret = pipe(fd);
	if(ret == -1){
		perror("pipe error");
		exit(1);
	}

	for(i = 0; i < 2; i++){
		if((pid = fork()) == 0)
			break;
		else if(pid == -1){
			perror("pipe error");
			exit(1);
		}
	}

	if (i == 0) {			
		close(fd[0]);				
		write(fd[1], "1.hello\n", strlen("1.hello\n"));
	} else if(i == 1) {	
		close(fd[0]);				
		write(fd[1], "2.world\n", strlen("2.world\n"));
	} else {
		close(fd[1]);       //父进程关闭写端,留读端读取数据    
		sleep(1);
		n = read(fd[0], buf, 1024);     //从管道中读数据
		write(STDOUT_FILENO, buf, n);

		for(i = 0; i < 2; i++)		//两个儿子wait两次
			wait(NULL);
	}

	return 0;
}

1.3 兄弟进程实现 ls | wc -l

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

void sys_err(const char *str)
{
    perror(str);
    exit(1);
}
int main(int argc, char *argv[])
{
    int fd[2];
    int ret, i;
    pid_t pid;

    ret = pipe(fd);
    if (ret == -1) {
        sys_err("pipe error");
    }

    for(i = 0; i < 2; i++) {        // 表达式2 出口,仅限父进程使用
        pid = fork();
        if (pid == -1) {
            sys_err("fork error");
        } 
        if (pid == 0)               // 子进程,出口
            break;
    } 

   if (i == 2) {                    // 父进程 . 不参与管道使用. 
        close(fd[0]);               // 关闭管道的 读端/写端.
        close(fd[1]);

        wait(NULL);                 // 回收子进程
        wait(NULL);
    } else if (i == 0) {  // xiong
        close(fd[0]);
        dup2(fd[1], STDOUT_FILENO);     // 重定向stdout
        execlp("ls", "ls", NULL);
        sys_err("exclp ls error");
    } else if (i == 1) {            //弟弟
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);      // 重定向 stdin
        execlp("wc", "wc", "-l", NULL);
        sys_err("exclp wc error");
    }
    
    return 0;
}

二、命名管道 fifo

fifo管道:可以用于无血缘关系的进程间通信。

命名管道:  mkfifo 

无血缘关系进程间通信:

	读端,open fifo O_RDONLY

	写端,open fifo O_WRONLY

2.1 写端

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

void sys_err(char *str)
{
    perror(str);
    exit(-1);
}

int main(int argc, char *argv[])
{
    int fd, i;
    char buf[4096];

    if (argc < 2) {
        printf("Enter like this: ./a.out fifoname\n");
        return -1;
    }
    fd = open(argv[1], O_WRONLY);       //打开管道文件
    if (fd < 0) 
        sys_err("open");

    i = 0;
    while (1) {
        sprintf(buf, "hello itcast %d\n", i++);

        write(fd, buf, strlen(buf));    // 向管道写数据
        sleep(1);
    }
    close(fd);

    return 0;
}

2.2 读端

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

void sys_err(char *str)
{
    perror(str);
    exit(1);
}

int main(int argc, char *argv[])
{
    int fd, len;
    char buf[4096];

    if (argc < 2) {
        printf("./a.out fifoname\n");
        return -1;
    }
    //int fd = mkfifo("testfifo", 644);
    //open(fd, ...);
    fd = open(argv[1], O_RDONLY);   // 打开管道文件
    if (fd < 0) 
        sys_err("open");
    while (1) {
        len = read(fd, buf, sizeof(buf));   // 从管道的读端获取数据
        write(STDOUT_FILENO, buf, len);
        sleep(3);           //多個读端时应增加睡眠秒数,放大效果.
    }
    close(fd);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值