进程间通信(IPC)

进程间通信(IPC)是操作系统提供的用于不同进程之间共享数据或协调工作的机制。

总结与选型建议

场景推荐 IPC 机制理由
父子进程简单通信匿名管道轻量、无需持久化
跨进程持久化通信具名管道(FIFO)文件系统可见,支持多进程
高性能数据共享共享内存 + 信号量零拷贝,适合频繁数据交换
进程同步(如互斥锁)POSIX 具名信号量接口简洁,跨进程兼容性好
跨网络通信套接字(TCP/UDP)支持远程通信,灵活可靠
异步事件通知信号(如 SIGUSR1)简单快捷,适合紧急操作

目录

1. 管道(Pipe)

匿名管道(Pipe)

特点:

示例:

应用场景:Shell命令中的管道符 |,如 ls | grep .txt。

具名管道(FIFO)

特点:

示例:

应用场景:不同进程间的持久化通信,如日志收集。

2. 套接字(Socket)

特点:

示例(Unix域套接字):

应用场景:分布式系统通信、Web服务器与浏览器交互。

3. 信号(Signal)

特点:

示例:

应用场景:进程终止(SIGTERM)、调试(SIGTRAP)、自定义事件处理。

4. System-V IPC 对象

共享内存(Shared Memory)

特点:

示例:

应用场景:高性能数据共享(如数据库缓存)。

消息队列(Message Queue)

特点:

示例:

应用场景:多进程协作的任务队列。

信号量组(Semaphore Set)

特点:

示例:

应用场景:多进程共享资源的互斥访问(如文件写入)。

5. POSIX 信号量

POSIX匿名信号量

特点:

示例:

POSIX具名信号量

特点:

示例:

对比与选型建议

总结



1. 管道(Pipe)

匿名管道(Pipe)

  • 特点

    • 仅适用于父子进程或有共同祖先的进程(如 fork() 创建的子进程)。

    • 单向通信(半双工),一端写,另一端读。

    • 基于内存缓冲区,无持久性,通信结束后自动销毁。

    • 容量有限(通常几KB到几十KB)。

  • 示例

int fd[2];
pipe(fd);  // 创建管道,fd[0]读端,fd[1]写端
if (fork() == 0) {
    close(fd[1]);  // 子进程关闭写端
    read(fd[0], buf, sizeof(buf));
} else {
    close(fd[0]);  // 父进程关闭读端
    write(fd[1], "Hello", 6);
}

具体例子:

 父进程向子进程发送字符串。

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

int main() {
    int fd[2];
    char buf[100];
    pipe(fd);  // 创建管道

    if (fork() == 0) {  // 子进程
        close(fd[1]);   // 关闭写端
        read(fd[0], buf, sizeof(buf));
        printf("Child received: %s\n", buf);
        close(fd[0]);
    } else {            // 父进程
        close(fd[0]);   // 关闭读端
        const char *msg = "Hello from parent!";
        write(fd[1], msg, strlen(msg) + 1);
        close(fd[1]);
        wait(NULL);     // 等待子进程结束
    }
    return 0;
}

运行与输出

gcc pipe_example.c -o pipe_example && ./pipe_example
# 输出:Child received: Hello from parent!
  • 应用场景:Shell命令中的管道符 |,如 ls | grep .txt


具名管道(FIFO)

  • 特点

    • 通过文件系统路径标识(如 /tmp/myfifo),允许任意进程访问。

    • 支持多对一通信(多个进程写入,一个进程读取)。

    • 需显式创建和删除(mkfifo 命令或 mkfifo() 函数)。

  • 示例

    mkfifo /tmp/myfifo    # 创建具名管道
    echo "Data" > /tmp/myfifo &  # 进程A写入
    cat /tmp/myfifo              # 进程B读取
  • 应用场景:不同进程间的持久化通信,如日志收集。

具体例子:

场景:两个独立进程通过 FIFO 文件通信。

  1. 创建 FIFO 文件

    mkfifo /tmp/myfifo
  2. 进程 A(写入)

    echo "Data from Process A" > /tmp/myfifo
  3. 进程 B(读取)

    cat /tmp/myfifo  # 输出:Data from Process A
    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    
    int main() {
        int fd = open("/tmp/myfifo", O_WRONLY);
        write(fd, "Hello via FIFO!", 15);
        close(fd);
        return 0;
    }


2. 套接字(Socket)

  • 特点

    • 支持跨网络通信(如 TCP/UDP)和本地通信(Unix域套接字)。

    • 全双工(双向通信)。

    • 需绑定地址和端口,支持多对多通信。

  • 示例(Unix域套接字):

    // 服务端
    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr = {.sun_family = AF_UNIX, .sun_path = "/tmp/sock"};
    bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    listen(sockfd, 5);
    accept(sockfd, ...);  // 等待连接
    
    // 客户端
    connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
  • 应用场景:分布式系统通信、Web服务器与浏览器交互。

具体例子:

 场景:本地进程间传输消息。

服务端代码

#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

int main() {
    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr = {.sun_family = AF_UNIX, .sun_path = "/tmp/mysock"};
    unlink("/tmp/mysock");  // 确保套接字文件不存在
    bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    listen(sockfd, 5);

    int clientfd = accept(sockfd, NULL, NULL);
    char buf[100];
    read(clientfd, buf, sizeof(buf));
    printf("Server received: %s\n", buf);
    close(clientfd);
    close(sockfd);
    return 0;
}

客户端代码

#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

int main() {
    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr = {.sun_family = AF_UNIX, .sun_path = "/tmp/mysock"};
    connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    write(sockfd, "Hello from client!", 18);
    close(sockfd);
    return 0;
}

运行步骤

  1. 编译并启动服务端:

    gcc server.c -o server && ./server
  2. 在另一个终端运行客户端:

    gcc client.c -o client && ./client
  3. 服务端输出:

    Server received: Hello from client!

3. 信号(Signal)

  • 特点

    • 异步通信:通过发送信号(如 SIGINTSIGKILL)通知进程事件。

    • 不传递数据,仅通知事件类型。

    • 需注册信号处理函数(signal() 或 sigaction())。

  • 示例

    void handler(int sig) { printf("Received SIGINT!\n"); }
    signal(SIGINT, handler);  // 注册SIGINT处理函数
  • 应用场景:进程终止(SIGTERM)、调试(SIGTRAP)、自定义事件处理。


4. System-V IPC 对象

共享内存(Shared Memory)

  • 特点

    • 效率最高(直接访问同一块内存)。

    • 需同步机制(如信号量)避免竞争。

    • 需显式创建和释放(shmget()shmat()shmdt())。

  • 示例

    key_t key = ftok("/tmp", 'A');
    int shmid = shmget(key, 1024, 0666 | IPC_CREAT);
    char *shm = shmat(shmid, NULL, 0);
    sprintf(shm, "Shared Data");  // 写入
    shmdt(shm);                   // 断开连接
  • 应用场景:高性能数据共享(如数据库缓存)。

具体例子:

场景:两个进程通过共享内存交换数据。
写入进程

#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>

int main() {
    key_t key = ftok("shmfile", 65);
    int shmid = shmget(key, 1024, 0666 | IPC_CREAT);
    char *str = (char*)shmat(shmid, NULL, 0);
    sprintf(str, "Shared Memory Data");
    printf("Data written: %s\n", str);
    shmdt(str);
    return 0;
}

读取进程

#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>

int main() {
    key_t key = ftok("shmfile", 65);
    int shmid = shmget(key, 1024, 0666 | IPC_CREAT);
    char *str = (char*)shmat(shmid, NULL, 0);
    printf("Data read: %s\n", str);
    shmdt(str);
    shmctl(shmid, IPC_RMID, NULL);  // 删除共享内存
    return 0;
}

运行步骤

  1. 编译并运行写入进程:

    gcc write_shm.c -o write_shm && ./write_shm
    # 输出:Data written: Shared Memory Data
  2. 运行读取进程:

    gcc read_shm.c -o read_shm && ./read_shm
    # 输出:Data read: Shared Memory Data

消息队列(Message Queue)

  • 特点

    • 类似管道,但支持消息类型标识(按类型读取)。

    • 消息持久化(即使进程退出仍存在)。

    • 容量较大(受系统配置限制)。

  • 示例

    struct msgbuf { long mtype; char mtext[100]; };
    key_t key = ftok("/tmp", 'B');
    int msgid = msgget(key, 0666 | IPC_CREAT);
    msgsnd(msgid, &msg, sizeof(msg), 0);  // 发送
    msgrcv(msgid, &msg, sizeof(msg), 1, 0); // 接收类型为1的消息
  • 应用场景:多进程协作的任务队列。


信号量组(Semaphore Set)

  • 特点

    • 用于进程同步(协调资源访问)。

    • 提供原子操作(semop())增减信号量值。

  • 示例

    key_t key = ftok("/tmp", 'C');
    int semid = semget(key, 1, 0666 | IPC_CREAT);
    semctl(semid, 0, SETVAL, 1);  // 初始化为1(互斥锁)
    struct sembuf op = {0, -1, 0}; // P操作(获取锁)
    semop(semid, &op, 1);
    // 临界区操作...
    op.sem_op = 1;                // V操作(释放锁)
    semop(semid, &op, 1);
  • 应用场景:多进程共享资源的互斥访问(如文件写入)。


5. POSIX 信号量

POSIX匿名信号量

  • 特点

    • 适用于多线程共享内存的多进程

    • 需在共享内存中初始化(sem_init())。

  • 示例

    sem_t sem;
    sem_init(&sem, 0, 1);  // 初始值1(跨线程共享)
    sem_wait(&sem);        // P操作
    // 临界区...
    sem_post(&sem);        // V操作

POSIX具名信号量

  • 特点

    • 通过名称标识(如 /mysem),适用于多进程

    • 需显式创建和删除(sem_open()sem_unlink())。

  • 示例

    sem_t *sem = sem_open("/mysem", O_CREAT, 0666, 1);
    sem_wait(sem);    // P操作
    // 临界区...
    sem_post(sem);    // V操作
    sem_close(sem);

具体例子:

场景:协调两个进程对共享资源的访问。
进程 A(获取信号量)

#include <fcntl.h>
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>

int main() {
    sem_t *sem = sem_open("/mysem", O_CREAT, 0666, 1);
    sem_wait(sem);
    printf("Process A entered critical section\n");
    sleep(2);  // 模拟操作
    sem_post(sem);
    sem_close(sem);
    return 0;
}

进程 B(等待信号量)

#include <fcntl.h>
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>

int main() {
    sem_t *sem = sem_open("/mysem", O_CREAT, 0666, 1);
    sem_wait(sem);
    printf("Process B entered critical section\n");
    sem_post(sem);
    sem_close(sem);
    sem_unlink("/mysem");  // 删除信号量
    return 0;
}

运行步骤

  1. 编译并运行进程 A:

    gcc sem_processA.c -o semA -lpthread && ./semA
    # 输出:Process A entered critical section
  2. 立即运行进程 B(在另一个终端):

    gcc sem_processB.c -o semB -lpthread && ./semB
    # 输出:Process B entered critical section(等待2秒后输出)


对比与选型建议

机制适用场景优点缺点
匿名管道父子进程简单通信轻量、简单仅单向、容量有限
具名管道任意进程间持久化通信支持多对一需要文件系统路径
套接字跨网络或本地进程通信灵活、跨平台协议复杂、开销较大
共享内存高性能数据共享零拷贝、速度快需同步机制
消息队列按类型处理任务支持消息类型、持久化系统资源占用较多
信号量(System-V/POSIX)进程/线程同步原子操作、可靠性高配置繁琐

总结

  • 简单通信:优先选管道或信号。

  • 高性能需求:共享内存 + 信号量。

  • 跨网络通信:套接字。

  • 同步协调:System-V/POSIX信号量。

  • 现代开发:优先使用 POSIX 接口(更简洁、跨平台)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值