进程间的通信

发布于:2025-08-20 ⋅ 阅读:(18) ⋅ 点赞:(0)

进程间通信(IPC)概述

进程间通信(Inter-Process Communication, IPC)是Linux系统中多个进程共享数据或同步操作的机制。Linux提供了多种IPC方式,包括管道、消息队列、共享内存、信号量、套接字等。每种方式适用于不同场景,需根据需求选择。


管道(Pipe)

管道是半双工的通信方式,数据单向流动,通常用于父子进程或兄弟进程间的通信。

匿名管道

通过pipe()系统调用创建,返回两个文件描述符:fd[0](读端)和fd[1](写端)。

代码示例

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

int main() {
    int fd[2];
    pid_t pid;
    char buf[256];

    if (pipe(fd) == -1) {
        perror("pipe");
        return 1;
    }

    pid = fork();
    if (pid == 0) { // 子进程
        close(fd[1]); // 关闭写端
        read(fd[0], buf, sizeof(buf));
        printf("Child received: %s\n", buf);
        close(fd[0]);
    } else { // 父进程
        close(fd[0]); // 关闭读端
        write(fd[1], "Hello from parent", 18);
        close(fd[1]);
    }
    return 0;
}

命名管道(FIFO)

通过mkfifo命令或mkfifo()函数创建,允许无亲缘关系的进程通信。

代码示例

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    mkfifo("/tmp/myfifo", 0666);
    int fd = open("/tmp/myfifo", O_WRONLY);
    write(fd, "Hello FIFO", 11);
    close(fd);
    return 0;
}


消息队列(Message Queue)

消息队列是内核维护的链表,进程通过唯一标识符(key)访问队列,支持不同类型消息。

常用函数
  • msgget():创建或获取队列。
  • msgsnd():发送消息。
  • msgrcv():接收消息。
  • msgctl():控制队列(如删除)。

代码示例

#include <sys/msg.h>
#include <stdio.h>
#include <string.h>

struct msg_buffer {
    long msg_type;
    char msg_text[100];
};

int main() {
    key_t key = ftok("progfile", 65);
    int msgid = msgget(key, 0666 | IPC_CREAT);

    struct msg_buffer message;
    message.msg_type = 1;
    strcpy(message.msg_text, "Hello Message Queue");

    msgsnd(msgid, &message, sizeof(message), 0);
    printf("Sent: %s\n", message.msg_text);

    msgrcv(msgid, &message, sizeof(message), 1, 0);
    printf("Received: %s\n", message.msg_text);

    msgctl(msgid, IPC_RMID, NULL);
    return 0;
}


共享内存(Shared Memory)

共享内存是最快的IPC方式,允许多个进程访问同一块内存区域。

常用函数
  • shmget():创建或获取共享内存段。
  • shmat():附加到进程地址空间。
  • shmdt():分离共享内存。
  • shmctl():控制共享内存(如删除)。

代码示例

#include <sys/shm.h>
#include <stdio.h>
#include <string.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("Write data: ");
    fgets(str, 1024, stdin);
    printf("Data written: %s\n", str);

    shmdt(str);
    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}


信号量(Semaphore)

信号量用于同步进程对共享资源的访问,避免竞态条件。

常用函数
  • semget():创建或获取信号量集。
  • semop():执行原子操作(如P/V操作)。
  • semctl():控制信号量(如初始化或删除)。

代码示例

#include <sys/sem.h>
#include <stdio.h>
#include <unistd.h>

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

void p(int semid) {
    struct sembuf op = {0, -1, SEM_UNDO};
    semop(semid, &op, 1);
}

void v(int semid) {
    struct sembuf op = {0, 1, SEM_UNDO};
    semop(semid, &op, 1);
}

int main() {
    key_t key = ftok("semfile", 65);
    int semid = semget(key, 1, 0666 | IPC_CREAT);
    union semun arg;
    arg.val = 1;
    semctl(semid, 0, SETVAL, arg);

    pid_t pid = fork();
    if (pid == 0) {
        p(semid);
        printf("Child enters critical section\n");
        sleep(2);
        printf("Child leaves critical section\n");
        v(semid);
    } else {
        p(semid);
        printf("Parent enters critical section\n");
        sleep(2);
        printf("Parent leaves critical section\n");
        v(semid);
    }
    semctl(semid, 0, IPC_RMID);
    return 0;
}


套接字(Socket)

套接字支持跨网络通信,也可用于本地进程间通信(AF_UNIX域)。

本地套接字示例
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main() {
    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, "/tmp/socket");

    bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    listen(sockfd, 5);

    int client = accept(sockfd, NULL, NULL);
    char buf[256];
    read(client, buf, sizeof(buf));
    printf("Server received: %s\n", buf);
    write(client, "Hello from server", 17);

    close(client);
    close(sockfd);
    unlink("/tmp/socket");
    return 0;
}


信号(Signal)

信号是异步通知机制,用于进程间简单事件通知(如终止、中断)。

常用函数
  • kill():发送信号。
  • signal():注册信号处理函数。

代码示例

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

void handler(int sig) {
    printf("Received signal: %d\n", sig);
}

int main() {
    signal(SIGINT, handler);
    printf("Press Ctrl+C to trigger signal\n");
    pause(); // 等待信号
    return 0;
}


对比与选择建议

IPC方式 适用场景 特点
管道 父子进程简单通信 单向,容量有限
消息队列 无亲缘关系进程结构化通信 支持消息类型,内核持久化
共享内存 高频大数据量通信 无需拷贝,需同步机制
信号量 资源访问同步 计数器,避免竞态
套接字 跨网络或复杂通信 灵活,支持多种协议
信号 异步事件通知 简单,不可靠

根据需求选择:

  • 高性能:共享内存 + 信号量。
  • 结构化数据:消息队列。
  • 简单通知:信号或管道。
  • 跨机器通信:套接字。

注意事项

  1. 同步问题:共享内存和消息队列需配合信号量避免竞态。
  2. 资源释放:显式删除IPC对象(如msgctlshmctl)。
  3. 权限控制:设置正确的访问权限(如0666)。
  4. 错误处理:检查系统调用返回值(如-1表示失败)。


网站公告

今日签到

点亮在社区的每一天
去签到