Linux中进程之间的通信

IPC的概念

即进程间的通信

常用方式:

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

2,信号- 系统开销小

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

4,信号量-计数器

5,共享内存

6,内存映射

7,套接字

无名管道

管道的概念

本质:

        内核缓冲区

        伪文件--不占用磁盘空间

特点:

        分为两部分:

                        读端,写端,对应两个文件描述符

                        数据写端流入,读端流出

        操作管理的进程被销毁之后,管道自动被释放

适用于有血缘关系的进程(父子进程)

管道默认是阻塞的:

阻塞模式确保了在数据写入管道之前,读取端不会继续执行,从而避免了读取到不完整或错误的数据。这种同步机制保证了数据的完整性和准确性。

阻塞模式下的代码通常更加直观和易于理解,因为它遵循了“等待-处理”的简单模式。

在阻塞模式,如果管道中没有数据可读,进程会挂起,等待数据到来,从而节省CPU资源。

阻塞模式可以防止在数据未完全写入管道之前就被读取,从而避免了潜在的数据泄露风险。

管道的管理

内部实现方式:队列(先进先出),而且是环形队列,确保数据的顺序性和高效性。

缓冲区大小:默认4k,大小会根据实际情况做出适当调整

管道局限性:队列使数据只能读取一次,不能重复读取,而且管道使半双工工作模式,只能进行单项通信。

匿名管道的创建

函数原型

#include <unistd.h> //包含的头文件

int pipe(int pipefd[2]);

参数:

创造管道的函数:pipe()

pipefd()是一个整型数组,用来存放管道的两个文件描述符,pipefd(0)是读端,pipefd(1)是写端。

返回值:如果创建成功,pipe()返回0;如果失败,返回-1,并用exit退出。

这里打印的文件描述符是3和4,是因为在通常的UNIX和类UNIX系统(如Linux)中,标准文件描述符(file descriptors)012 分别被预留给标准输入(stdin)、标准输出(stdout)标准错误输出(stderr)。当你调用 pipe() 函数时,它会在系统中分配两个新的文件描述符用于管道的读端和写端,这两个是连续的,当前可用的最小文件描述符。如果系统中已经有其他文件描述符被打开(例如,通过打开文件、套接字等),那么 pipe() 返回的文件描述符可能会更高(那就可能文件描述不是3和4了)。

父子进程间使用管道通信

这里我们通过父子进程管道通信实现ps aux | grep "bash"这个功能

在父进程中,我们需要先关闭读端,在子进程关闭写端,这样就实现了数据在父进程到子进程的方向上流动。

在进程中,我们调用了dup2函数:

起到数据重定向功能通过将特定文件描述符复制到标准输入、输出或错误输出的文件描述符,可以实现I/O重定向。例如,将一个文件描述符复制到标准输出,可以使程序的输出结果保存到文件中而非显示在屏幕上

函数原型为:int dup2(int oldfd, int newfd)。

这里,oldfd表示要被复制的文件描述符,而newfd则表示目标文件描述符的新编号。

当dup2成功执行时,它会返回新的文件描述符编号,即newfd的值;如果出错,则返回-1。

值得注意的是,在调用dup2之前,若newfd已经打开,系统会先关闭newfd指向的文件,以确保不会出现悬挂指针的情况。

使用execlp让父子进程执行两个程序

成功实现功能,对于两个grep bash进程的编号不同,是因为在我们使用终端执行命令时所产生的进程,所以会略有不同(进程编号一般比较接近)

管道的读写功能

1. 写入管道
  • 写入函数ssize_t write(int fd, const void *buf, size_t count);
    • fd:管道写端的文件描述符(pipefd[1])。
    • buf:指向要写入数据的缓冲区的指针。
    • count:要写入的数据的字节数。
  • 写入行为
    • 如果管道未满,write函数将数据写入管道,并返回实际写入的字节数。
    • 如果管道已满且写端未被关闭,write函数将阻塞,直到有空间可用或管道被关闭。
    • 如果管道读端全部被关闭,且写端尝试写入数据,将导致进程异常终止(除非捕获SIGPIPE信号)。
2. 读取管道
  • 读取函数ssize_t read(int fd, void *buf, size_t count);
    • fd:管道读端的文件描述符(pipefd[0])。
    • buf:指向用于存储读取数据的缓冲区的指针。
    • count:请求读取的字节数。
  • 读取行为
    • 如果管道中有数据,read函数从管道中读取指定数量的数据(或管道中剩余的所有数据,如果剩余数据少于请求的数量),并返回实际读取的字节数。
    • 如果管道中没有数据且写端未被关闭,read函数将阻塞,直到有数据可读或管道被关闭。
    • 如果管道写端全部被关闭,且读端尝试读取数据,read函数将返回0,表示文件结束(EOF)。

学到消息队列时候会详细讲

这边简单的举个例子

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <string.h>  
#include <sys/wait.h>  
  
int main() {  
    int pipefd[2]; // 定义一个整型数组来存储管道的两个文件描述符  
    pid_t pid; // 定义一个变量来存储子进程的PID  
    char buf[100]; // 定义一个缓冲区来存储从管道中读取的数据  
  
    // 创建管道  
    if (pipe(pipefd) == -1) {  
        perror("pipe");  
        exit(EXIT_FAILURE);  
    }  
  
    // 创建子进程  
    pid = fork();  
  
    if (pid == -1) {  
        perror("fork");  
        exit(EXIT_FAILURE);  
    }  
  
    // 子进程代码  
    if (pid == 0) {  
        close(pipefd[1]); // 关闭子进程中的写端,因为我们只从管道读取数据  
  
        // 从管道中读取数据  
        ssize_t bytes_read = read(pipefd[0], buf, sizeof(buf) - 1);  
        if (bytes_read > 0) {  
            buf[bytes_read] = '\0'; // 确保字符串以null结尾  
            printf("Received from parent: %s\n", buf);  
        }  
  
        close(pipefd[0]); // 关闭读端  
        exit(EXIT_SUCCESS); // 子进程结束  
    }  
  
    // 父进程代码  
    close(pipefd[0]); // 关闭父进程中的读端,因为我们只向管道写入数据  
  
    // 向管道写入数据  
    const char *msg = "Hello from parent";  
    write(pipefd[1], msg, strlen(msg) + 1); // 包括字符串的null终止符  
  
    close(pipefd[1]); // 关闭写端  
  
    // 等待子进程结束  
    wait(NULL);  
  
    return 0;  
}

管道缓冲区大小

查看缓冲区大小命令:ulimit -a

可以看出pipe size(管道缓冲区大小时512bytes*8=4096bytes=4kb)

通过函数fpathcon也可查看,fpathcon是一个用于查询与文件描述符相关联的运行时配置信息的函数

函数原型:

#include <unistd.h>

long fpathconf(int fd, int name);

返回值时长整型,正确返回时,返回查询到的配置值,查询失败,返回-1,并设置error指示错误原因

有名管道

函数原型

头文件:#include<sys/types.h>     #include <sys/stat.h>

int mkfifo(const char \*filename,mode_t mode); 

功能:创建管道文件

参数:管道文件文件名,权限,创建的文件权限仍然和umask有关系。

返回值:创建成功返回0,创建失败返回-1。

1.特点

有名管道 在磁盘上有这样一个文件 ls -l ->p

也是一个伪文件,在磁盘大小永久为0

数据存在内核中有一个对应的缓冲区

半双工通信方式

2.使用场景

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

3.创建方式

命令:mkfifo 管道名

函数:mkfifo

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

   open/close

   read/write

   不能执行lseek操作

创建有名管道,即创建了一个节点,让这个节点指向我们的内核缓冲区

创建成功

我们可以在设置的路径中找到所创建的管道文件,并且文件权限第一个为p,表示这个文件是一个管道文件

有名管道的进程间通信

使用open函数读取管道文件中的数据

使用write写入数据到管道文件中

当我们分两个终端执行的时候,我们先执行read命令,但是管道文件里面没有,会等待管道里面写入东西,我们在另外一个终端执行write命令时,会把数据写入到管道文件中,同时read命令也会读取到这个数据并显示

  • 34
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值