进程间的通信(IPC)--管道

1.进程间通信常用的方式

1 ,管道通信:有名管道,无名管道
2 ,信号 - 系统开销小
3 ,消息队列 - 内核的链表
4 ,信号量 - 计数器
5 ,共享内存
6 ,内存映射
7 ,套接字

2.管道的概念

2.1本质

*内核缓冲区
*伪文件 - 不占用磁盘空间

2.2特点

*两部分: 读端,写端,对应两个文件描述符 ,分别是数据写端流入,读端流出
* 操作管理的进程被销毁之后 管道自动被释放
*管道默认是阻塞的
* 管道数据只能被读取一次,再次读取数据需要再次写入数据

2.3管道的原理

*内部实现方式:队列 ,环形队列
*特点:先进先出
*缓冲区大小 :默认4K ,大小会根据实际情况做适当调整

2.4管道的局限性

*队列: 数据只能读取一次,不能重复读取
*单工:遥控器
*半双工:对讲机

2.5管道的读写行为

读操作:
        有数据 :read(fd[1]) 正常读,返回读出的字节数
        无数据:如果写端被全部关闭时,read 返回 0 ,相当于读文件到了尾部。
                       如果写端没有全部关闭时,read阻塞
写操作:
        读端全部关闭:
                管道不断的写入数据可能管道会破裂,进程被终止 ,
                就是内核给当前进程发送信号SIGPIPE- 13,默认处理动作
        读端没全部关闭:
                缓冲区写满了:write 阻塞
                缓冲区没满   :write 继续写,直到写满,阻塞

2.6查看管道缓冲区大小

命令 :ulimit -a

3.无名管道

3.1无名管道的创建

函数:int pipe(int fd[2])
           fd‐传出参数:
           fd[0]‐ 读端
           fd[1]‐ 写端
返回值:
          0 :成功
          ‐1 :创建失败
历程:
include <unistd.h>
#include <stdio.h>
#include <stdlib.h>


int main()
{

        int fd[2];
        int rnt;

        rnt = pipe(fd);
        if(rnt == -1)
        {
                printf("创建无名管道失败\n");
                perror("pipe error");
                exit(0);
        }

        else
        {
                printf("读端:%d\n",fd[0]);
                printf("写端:%d\n",fd[1]);
        }
        close(fd[0]);
	    close(fd[1]);

        return 0;
}

运行结果:

3.2父子进程使用无名管道通信

示例:实现 ps aux| grep "bash"
示例功能:父进程使用 ps aux命令进行查询,子进程使用grep "bash"进行读取
数据重定向: dup2
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>



int main()
{

	int fd[2];
	int rnt;

	rnt = pipe(fd);
	if(rnt == -1)
	{
		printf("创建无名管道失败\n");
		perror("pipe error");
		exit(0);
	}
	pid_t pid = fork();

	if(pid == -1)
	{
		printf("fork failed!\n");
		perror("fork");
		exit(0);
	}
	if(pid == 0)//子进程进行读取数据,grep "bash"
	{
		printf("这是一个子进程\n");
		close(fd[1]);
		dup2(fd[0],STDIN_FILENO);//将要在终端读取数据,重定向到管道去读取
		execlp("grep","grep","bash","--color=auto",NULL);
		exit(0);
	}
    if(pid > 0)//父进程将数据写到管道里去 ps aux
	{
		close(fd[0]);//写之前要把管道的读关闭
		printf("这是一个父进程\n");
		dup2(fd[1],STDOUT_FILENO);//将数据重定向到管道去 不然就显示到终端了
		execlp("ps","ps","aux",NULL);
		wait(NULL);//阻塞回收子进程
		exit(0);
	}
	else
	{
		printf("读端:%d\n",fd[0]);
		printf("写端:%d\n",fd[1]);
	}

	close(fd[0]);
	close(fd[1]);

	return 0;
}
运行结果:
PS:进行管道读写的时候,读的时候要把写关闭,写的时候要把读关闭

3.3使用场景

有血缘关系的进程间通信(如:父子进程之间的通信)

4.有名管道

4.1有名管道的创建

函数形式: int mkfifo(const char \*filename,mode_t mode);
功能:创建管道文件
参数:管道文件文件名,权限,创建的文件权限仍然和 umask 有关系。
返回值:创建成功返回 0 ,创建失败返回 -1
e--1
write --2
read --4
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

int main()
{
        int ret;

        ret = mkfifo("/home/czx/mkfifo",0777);
        if(ret == -1)
        {
                printf("有名管道创建失败\n");
                perror("myfifo");
                return -1;
        }
        else
                printf("有名管道创建成功\n");

        return 0;
}
~

运行结果:

PS :创建管道的时候,只会创建一个指向内核缓冲区的节点,调用open()的时候才会生成管道.

示例:
功能:一个进程利用管道进行读取数据,一个进程利用管道进行写数据
利用管道进行读数据:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
        int ret;
        char readBuff[20] = {0};
        int read_ret;
        int fd;
        ret = mkfifo("/home/czx/mkfifo",0775);
        if(ret == -1)
        {
                printf("有名管道创建失败\n");
                perror("myfifo");
                return -1;
        }
        else
        {
                printf("有名管道创建成功\n");
                fd = open("/home/czx/mkfifo",O_RDONLY);
                if(fd < 0)
                {
                        printf("打开管道错误\n");
                        return -2;
                }
                else
                {
                        while(1)
                        {
                                read_ret = read(fd,readBuff,20);
                                printf("读取到的字节数%d,内容为:%s\n",read_ret,readBuff);

                        }
                }
        }
        close(fd);

        return 0;
}

利用管道进行写数据:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
        char writeBuff[20];
        int fd;
        fd = open("/home/czx/mkfifo",O_WRONLY);
        if(fd < 0)
        {
                printf("打开管道错误\n");
                return -2;
        }
        else
        {
                while(1)
                {
                        gets(writeBuff);
                        write(fd,writeBuff,20);
                }
        }
        close(fd);

        return 0;
}
~       

运行结果:

4.2特点

有名管道
在磁盘上有这样一个文件 ls -l ->p
也是一个伪文件,在磁盘大小永久为 0
数据存在内核中有一个对应的缓冲区
半双工通信方式

4.3使用场景

没有血缘关系的进程间通信

4.4创建方式

命令: mkfifo 管道名
函数: mkfifo

4.5.fifo文件可以使用io函数进程操作

open/close
read/write
不能执行 lseek 操作
  • 19
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值