【Linux】进程间通信(一)——管道

目录

一、引言

1、进程间通信的概念

2、常见的进程间通信方式

3、注意事项

二、Linux管道

1、有名管道

单次管道通信

多次管道通信

2、 无名管道

无名管道的创建pipe

三、管道的面试题目

一、引言

1、进程间通信的概念

进程间通信(Inter-Process Communication,IPC)是指多个进程之间传递数据或信号的机制。由于进程拥有独立的地址空间,操作系统需要提供特定的方法以实现数据共享或同步。

2、常见的进程间通信方式

  • 管道(Pipe):管道是一种半双工的通信方式,数据只能单向流动。通常用于具有亲缘关系的进程间通信,例如父子进程。可分为:匿名管道(通过pipe()系统调用创建,仅限父子进程使用)和命名管道(FIFO)(通过mkfifo命令或系统调用创建,允许无亲缘关系的进程通信)。

  • 消息队列(Message Queue):消息队列是由内核维护的链表结构,允许进程发送或接收特定格式的消息。与管道不同,消息队列支持多对多通信,且消息可以指定类型(包括发送消息:msgsnd()和接收消息:msgrcv())。

  • 共享内存(Shared Memory):共享内存是最快的IPC方式,多个进程映射同一块物理内存到各自的地址空间,直接读写数据。创建共享内存:shmget();关联到进程空间:shmat();解除关联:shmdt()

  • 信号量(Semaphore):信号量用于进程间同步,避免资源竞争。它是一个计数器,控制对共享资源的访问。创建信号量:semget();操作信号量:semop()

  • 套接字(Socket):套接字可用于不同主机或同一主机的进程间通信,支持全双工通信。创建套接字:socket();绑定地址:bind();监听连接:listen()

3、注意事项

  • 共享内存需配合信号量或其他同步机制,避免数据竞争。
  • 管道和消息队列有大小限制,需考虑数据量。
  • 信号是异步的,处理逻辑应尽量简单以避免竞态条件。

二、Linux管道

管道可以用来在两个进程之间传递数据,如: ps -ef | grep “bash”, 其中‘|’就是管道,其作用就是将 ps 命令的结果写入管道文件,然后 grep 再从管道文件中读出该数据进行过滤。

1、有名管道

有名管道可以在任意两个进程之间通信,但需要在同一个管道内。

注:如果管道空,读阻塞;如果管道满,写阻塞。

单次管道通信

有名管道的创建:

#include <sys/types.h>
#include <sys/stat.h> 
//filename 是管道名 mode 是创建的文件访问权限
int mkfifo(const char *filename, mode_t mode);
命令创建: mkfifo FIFO;系统调用创建

  • a.c代码写入数据
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>

int main(){
    int fdw = open("fifo",O_WRONLY);//只写打开管道,可能阻塞
    if(-1==fdw){
        printf("open fifo failed\n");
        exit(1);
    }
    printf("fdw=%d\n",fdw);
    char buff[128] = {0};// 创建一个字符数组用于存储输入的数据
    fgets(buff, 128, stdin);// 从标准输入读取一行数据
    write(fdw, buff, strlen(buff) - 1);//向管道写数据
    close(fdw);//关闭管道
    exit(0);正常退出程序
}
  • b.c读程序代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>

int main(){
    int fdr = open("fifo",O_RDONLY);//只读打开管道,可能阻塞
    if(-1==fdr){
        exit(1);
    }
    printf("fdr=%d\n",fdr);
    char buff[128] = {0};
    int n= read(fdr,buff,127);
    if(n>0){
        printf("read:%s\n",buff);
    }
    close(fdr);//关闭管道
    exit(0);
}

运行结果:

注:

必须打开两个窗口,一个执行写程序,另一个执行读程序

写入的数据会通过命名管道传输到读端程序,并被打印出来。

多次管道通信

a.c代码写入数据

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>

int main(){
    int fdw = open("fifo",O_WRONLY);//只写打开管道,可能阻塞
    if(-1==fdw){
        printf("open fifo failed\n");
        exit(1);
    }
    printf("fdw=%d\n",fdw);
    while(1){
        char buff[128] = {0};
        fgets(buff, 128, stdin);
        if(strncmp(buff,"end",3)==0){
            break;
        }
        write(fdw, buff, strlen(buff) - 1);//向管道写数据
    }
    close(fdw);//关闭管道
    exit(0);
}

b.c读取程序代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>

int main(){
    int fdr = open("fifo",O_RDONLY);//只读打开管道,可能阻塞
    if(-1==fdr){
        exit(1);
    }
    printf("fdr=%d\n",fdr);
    while(1){
    char buff[128] = {0};
    int n= read(fdr,buff,127);
    if(n==0){
        break;
    }
        printf("read:%s\n",buff);
    }
    close(fdr);//关闭管道
    exit(0);
}

运行结果

通过while循环就会一直通信

  • 读端可以不断地从管道中读取数据,直到满足某个条件(如读取到特定结束标志)。

  • 写端可以不断地向管道中写入数据,直到满足某个条件(如用户输入结束标志)

直至在写端./a中写入end才会关闭,终止程序。

管道的写端关闭,读端read返回值为0;管道的读端关闭,写端写数据

2、 无名管道

无名管道主要应用于父子进程间的通信。

无名管道的创建pipe

#include <unistd.h>
/*
pipe()成功返回 0,失败返回-1
fds[0]是管道读端的描述符
fds[1]是管道写端的描述符
*/
int pipe(int fds[2]);

无名管道代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>

int main(){
    int fds[2];//fds[0]读端,fds[1]写端描述符
    if(pipe(fds)==-1){//创建无名管道并打开
        printf("创建管道失败\n");
        exit(1);
    }
    pid_t pid = fork();
    if(pid==-1){
        exit(1);
    }
    if( pid == 0 )  {
        close(fds[1]);
        char buff[128] = {0};
        read(fds[0], buff, 127);//子进程读
        printf("child read: %s\n", buff);
        close(fds[0]);
    }
    else {
        close(fds[0]);
        write(fds[1], "hello", 5);//父进程写
        close(fds[1]);
    }
    exit(0);
}

三、管道的面试题目

  • 无论有名还是无名,写入管道的数据都在内存中。
  • 管道是一种半双工通信方式(通信方式有单工、半双工、全双工)。
  • 有名和无名管道的区别:有名可以在任意进程间使用,而无名主要在父子进程间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易ლ拉罐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值