linux-------------------------进程间通信(上)

发布于:2025-06-06 ⋅ 阅读:(16) ⋅ 点赞:(0)

1.进程间通信介绍

1-1 进程间通信目的

数据传输:一个进程需要把数据传输给另外一个进程

资源共享:多个进程可以共享资源

通知事件:一个进程需要通知另外多个或者一个进程比如子进程结束时要通知父进程进行回收

进程控制:有些进程希望完全控制另⼀个进程的执⾏(如Debug进程),此时控制进程希望能够
拦截另⼀个进程的所有陷⼊和异常,并能够及时知道它的状态改变。

1-2 进程间通信发展

1.管道

2.System V进程间通信

3.POSIX进程间通信

1-3 进程间通信分类

管道
        匿名管道pipe
        命名管道
System V
        消息队列
        共享内存
        信号量
POSIX
        消息队列
        共享内存
        信号量
        互斥量
        条件变量
        读写锁

2 管道

什么是管道
        
        管道是unix中最古老的进程间通信方式
        我们把从一个进程连接到另一个进程的一个数据流称为管道

3匿名管道

3-1 代码实例

#include <iostream>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <cstring>
using namespace std;

int main(void)
{
    int fds[2];
    char buf[100];
    int len;
    if (pipe(fds) == -1)
        perror("make pipe"), exit(1);
    // read from stdin
    while (fgets(buf, 100, stdin))
    {
        len = strlen(buf);
        // write into pipe
        if (write(fds[1], buf, len) != len)
        {
            perror("write to pipe");
            break;
        }
        memset(buf, 0x00, sizeof(buf));
        // read from pipe
        if ((len = read(fds[0], buf, 100)) == -1)
        {
            perror("read from pipe");
            break;
        }
        // write to stdout
        if (write(1, buf, len) != len)
        {
            perror("write to stdout");
            break;
        }
    }
}

3-2 用fork来共享管道原理

3-3 站在⽂件描述符⻆度-深度理解管道

3-4 站在内核⻆度-管道本质

3-5 管道样例

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)
int main(int argc, char *argv[])
{
    int pipefd[2];
    if (pipe(pipefd) == -1)
        ERR_EXIT("pipe error");
    pid_t pid;
    pid = fork();

    if (pid == -1)
        ERR_EXIT("fork error");
    if (pid == 0)
    {
        close(pipefd[0]);
        write(pipefd[1], "hello", 5);
        close(pipefd[1]);
        exit(EXIT_SUCCESS);
    }
    close(pipefd[1]);
    char buf[10] = {0};
    read(pipefd[0], buf, 10);
    printf("buf=%s\n", buf);
    return 0;
}

3-6 管道读写规则

1.读操作

  • 管道中有数据

    • read()立即返回可用数据(最多达到请求的大小)

    • 实际读取字节数 ≤ 请求字节数

  • 管道为空

    • 写端全部关闭:read()返回0(EOF)

    • 写端未关闭:read()阻塞等待数据

2.写操作

  • 管道有空间

    • write()立即执行,写入所有请求的数据

  • 管道空间不足

    • 读端全部关闭:write()会触发SIGPIPE信号

    • 读端未关闭:write()阻塞直到有足够空间

3-7 管道特点

只能⽤于具有共同祖先的进程(具有亲缘关系的进程)之间进⾏通信;通常,⼀个管道由⼀个进
程创建,然后该进程调⽤fork,此后⽗、⼦进程之间就可应⽤该管道。
管道提供流式服务
⼀般⽽⾔,进程退出,管道释放,所以管道的⽣命周期随进程
⼀般⽽⾔,内核会对管道操作进⾏同步与互斥
管道是半双⼯的,数据只能向⼀个⽅向流动;需要双⽅通信时,需要建⽴起两个管道

4 命名管道

核心特性

  1. 文件系统可见:在文件系统中有一个路径名(如 /tmp/myfifo

  2. 进程无关性:任何有权限的进程都可以访问

  3. 持久性:文件系统存在直到被显式删除

  4. 半双工通信:数据单向流动(需要双向通信时需创建两个FIFO)

  5. 阻塞特性:默认情况下,打开FIFO会阻塞直到另一端也被打开

创建与使用

创建方法

Shell命令创建

mkfifo /path/to/fifo

C语言创建

#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

代码演示

写入端:

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

#define FIFO_PATH "/tmp/myfifo"
#define BUFFER_SIZE 128

int main() {
    // 创建FIFO(如果已存在则忽略)
    if (mkfifo(FIFO_PATH, 0666) == -1 && errno != EEXIST) {
        perror("mkfifo failed");
        exit(EXIT_FAILURE);
    }

    printf("Writer: Opening FIFO...\n");
    int fd = open(FIFO_PATH, O_WRONLY);  // 阻塞直到有读取端打开
    if (fd == -1) {
        perror("open failed");
        exit(EXIT_FAILURE);
    }
    printf("Writer: FIFO opened\n");

    const char *message = "Hello from writer process!";
    printf("Writer: Sending message: %s\n", message);
    
    if (write(fd, message, strlen(message)) == -1) {
        perror("write failed");
        close(fd);
        exit(EXIT_FAILURE);
    }

    close(fd);
    printf("Writer: Closed FIFO\n");
    
    // 可选:删除FIFO文件
    // unlink(FIFO_PATH);
    
    return EXIT_SUCCESS;
}

读取端:

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

#define FIFO_PATH "/tmp/myfifo"
#define BUFFER_SIZE 128

int main() {
    printf("Reader: Opening FIFO...\n");
    int fd = open(FIFO_PATH, O_RDONLY);  // 阻塞直到有写入端打开
    if (fd == -1) {
        perror("open failed");
        exit(EXIT_FAILURE);
    }
    printf("Reader: FIFO opened\n");

    char buffer[BUFFER_SIZE];
    ssize_t bytes_read = read(fd, buffer, BUFFER_SIZE - 1);
    if (bytes_read == -1) {
        perror("read failed");
        close(fd);
        exit(EXIT_FAILURE);
    }

    buffer[bytes_read] = '\0';  // 添加字符串终止符
    printf("Reader: Received message: %s\n", buffer);

    close(fd);
    printf("Reader: Closed FIFO\n");
    
    // 删除FIFO文件
    if (unlink(FIFO_PATH) == -1) {
        perror("unlink failed");
        exit(EXIT_FAILURE);
    }
    printf("Reader: FIFO file deleted\n");
    
    return EXIT_SUCCESS;
}

与匿名管道的比较

特性 匿名管道 命名管道 (FIFO)
文件系统可见
进程关系要求 必须有亲缘关系 无要求
创建方式 pipe() 系统调用 mkfifo() 函数
生命周期 随进程结束 显式删除
访问控制 文件权限控制
双向通信 需要两个管道 需要两个FIFO
阻塞行为 内置 可配置