linux——进程间的通信

1、IPC

什么是IPC?Inter Process Communication

2.进程间通信常用的几种方式

1,管道通信:有名管道,无名管道

2,信号- 系统开销小

3,消息队列-内核的链表

4,信号量-计数器

5,共享内存

6,内存映射

7,套接字

3、无名管道

  1. 管道(匿名管道 pipe)概念补充
  • 本质 :内核开辟的一段内存缓冲区 ,以伪文件形式存在。

  • 伪文件特点

    • 只存在内存中,不占磁盘空间

    • 只用于数据传输,不长期存储

  • 结构

    • 一对文件描述符:fd[0] 读端、fd[1] 写端

    • 数据只能从写端进,读端出,单向流动

  • 生命周期

    • 所有引用该管道的进程全部关闭文件描述符后,管道自动释放
  • 阻塞特性(默认)

    • 读空管道 → 读阻塞

    • 写满管道 → 写阻塞

  1. 管道原理补充
  • 内部是环形缓冲区(循环队列)

  • FIFO 先进先出,不能随机访问

  • 默认大小

    • 传统 Linux:4KB

    • 现代 Linux:默认 16KB,可调整到 64KB

  • 数据特点:

    • 字节流方式传输,无边界

    • 不保存数据,读走就没了

  1. 管道局限性补充(你写的很对,我补全)

  2. 数据只能读一次,不可重复读取读出去就从队列移除,其他进程读不到了。

  3. 半双工通信

    • 同一时刻只能单向传输

    • 想双向通信必须创建两个管道

  4. 只能用于有血缘关系的进程 父子、兄弟进程,不支持无亲缘进程通信 → 解决:用 命名管道 FIFO

  5. 不支持 lseek管道是流式队列,不能定位、不能回退。

  6. 数据无边界、无消息结构管道只认字节流,不认 "一条消息",需要自己处理分包。

  7. 容量有限写满就阻塞,不适合超大数据瞬时爆发。

  8. 额外重要知识点(面试常问)

  • 管道的引用计数所有进程都关了对应 fd,管道才真正销毁。

  • 管道破裂(SIGPIPE 信号) 读端全部关闭,写端还在写 → 内核发送 SIGPIPE 杀死写进程。

  • 原子性 写入小于 PIPE_BUF(通常 4096 字节) 时,写入是原子的,不会穿插。

4、创建匿名通道

复制代码
int pipe(int fd[2])
fd‐传出参数:
    fd[0]‐读端
    fd[1]‐写端
返回值:
    0:成功‐1:创建失败

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

int main()
{
	int ret;
	int fd[2];

	ret = pipe(fd);
	if(ret == -1)
	{
		printf("creat pipe failed\n");
		exit(1);
	}
	printf("pipe[0] is %d\n",fd[0]);
	printf("pipe[1] is %d\n",fd[1]);

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

	return 0;
}

因为标准IO里:

  • 0:标准输入
  • 1:标准输出
  • 2:标准错误
  • 管道从 3 开始分配

5、父子进程使用管道通信

  • 父进程先创建管道 pipe(fd)
  • 父进程 fork 子进程
  • 父子各自关闭不用的端口 (避免混乱)
    • 父要写 → 关闭 fd[0] 读端
    • 子要读 → 关闭 fd[1] 写端
  • 父进程通过 fd[1] 写数据
  • 子进程通过 fd[0] 读数据
  • 关闭文件描述符,结束

管道是半双工,同一时间只能单向传数据。

实现 ps aux| grep "bash" 数据重定向:dup2

复制代码
#include<stdio.h>
#include<stdlin.h>
#include<unsitd.h>

int main()
{
    int ret;
    int fd[2];
    
    ret = pipe(fd);
    if(ret == -1)
    {
        printf("pipe failed\n");
        exit(1);
    }
           
    pid_t pid;
    pid = fork();
    if(pid == -1)
    {
        printf("fork failed\n");
        exit(1);
    }

    //ps aux
    if(pid > 0)
    {
        close(fd[0]);
        dup2(fd[1],STDOUT_FILENO);
        execlp("ps","ps","aux",NULL);
        perror("execlp");
        exit(1);
    }
    //grep "bash"
    else if(pid == 0)
    {
        close(fd[1]);
        dup2(fd[0],STDIN_FILENO);
        execlp("grep","grep","bash",NULL);

    }
    printf("fd[0] is %d\n",fd[0]);
    printf("fd[1] is %d\n",fd[1]);

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

    return 0;
}

父进程执行 ps aux,输出重定向到管道写端,子进程执行 grep bash,输入从管道读端获取实现:ps aux | grep bash 的效果

先创建通道,再创建子进程

dup2(fd[1], STDOUT_FILENO)标准输出(屏幕) 重定向到 管道写端

意思:ps 输出的内容,不打印到屏幕,而是写入管道!

父进程:ps aux 输出 → 管道写端

管道:写端 → 内核缓冲区 → 读端

子进程:管道读端 → grep bash 读取

相关推荐
最好有梦想~2 小时前
嵌入式Linux Lua使用ZeroBrane远程调试
linux·嵌入式硬件·lua
我爱学习好爱好爱2 小时前
Ansible 详解:group模块、vars_files变量、user模块实战
linux·运维·ansible
独隅2 小时前
Linux 系统下 ADB 环境 的详细安装步骤和基础设置指南
linux·运维·adb
码农爱学习2 小时前
使用cJosn读写配置文件
java·linux·网络
自然常数e2 小时前
预处理讲解
java·linux·c语言·前端·visual studio
哼?~2 小时前
Linux线程同步
linux
tumeng07112 小时前
Linux(CentOS)安装 Nginx
linux·nginx·centos
cyber_两只龙宝2 小时前
【Docker】Docker的原生网络介绍
linux·运维·docker·云原生·容器
AzusaFighting3 小时前
Dify (Ubuntu 24.04 Noble x64)部署教程
linux·运维·ubuntu