085. 使用C语言实现简单的进程间通信

在C语言中,进程间通信(IPC)是多个进程之间交换数据的一种方式。Linux系统提供了多种IPC机制,包括管道(Pipes)、消息队列(Message Queues)、共享内存(Shared Memory)和信号(Signals)。以下将分别实现这些简单的IPC机制。

1. 管道(Pipes)

管道是一种简单的IPC机制,用于在父子进程之间单向通信。匿名管道只能用于具有亲缘关系的进程之间,而命名管道(FIFO)可以用于不相关的进程之间。

示例代码:匿名管道

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

int main() {
    int pipefds[2];
    pid_t pid;

    // 创建管道
    if (pipe(pipefds) == -1) {
        perror("pipe");
        exit(1);
    }

    pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) { // 子进程
        close(pipefds[1]); // 关闭写端
        char buffer[80];
        read(pipefds[0], buffer, sizeof(buffer)); // 从管道读取数据
        printf("子进程收到的消息:%s\n", buffer);
        close(pipefds[0]); // 关闭读端
    } else { // 父进程
        close(pipefds[0]); // 关闭读端
        char message[] = "Hello, World!";
        write(pipefds[1], message, sizeof(message)); // 向管道写入数据
        close(pipefds[1]); // 关闭写端
        wait(NULL); // 等待子进程结束
    }

    return 0;
}

示例运行

输入:

无输入

输出:

子进程收到的消息:Hello, World!

2. 消息队列(Message Queues)

消息队列是一种更高级的IPC机制,允许不相关的进程之间交换消息。消息队列可以存储多个消息,每个消息都有一个类型和数据。

示例代码:消息队列

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <unistd.h>

// 消息结构
struct message {
    long mtype;
    char mtext[80];
};

int main() {
    key_t key;
    int msgid;
    struct message msg;

    // 创建消息队列
    key = ftok("queuefile", 65);
    msgid = msgget(key, 0666 | IPC_CREAT);

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) { // 子进程
        msg.mtype = 1;
        msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0);
        printf("子进程收到的消息:%s\n", msg.mtext);
        msgctl(msgid, IPC_RMID, NULL); // 删除消息队列
    } else { // 父进程
        msg.mtype = 1;
        strcpy(msg.mtext, "Hello, World!");
        msgsnd(msgid, &msg, sizeof(msg.mtext), 0);
        wait(NULL); // 等待子进程结束
    }

    return 0;
}

示例运行

输入:

无输入

输出:

子进程收到的消息:Hello, World!

3. 共享内存(Shared Memory)

共享内存允许多个进程共享同一块内存区域,从而实现高效的数据交换。

示例代码:共享内存

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

int main() {
    key_t key;
    int shmid;
    char* data;

    // 创建共享内存
    key = ftok("shmfile", 65);
    shmid = shmget(key, 1024, 0666 | IPC_CREAT);

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) { // 子进程
        data = shmat(shmid, (void*)0, 0);
        printf("子进程收到的消息:%s\n", data);
        shmdt(data); // 分离共享内存
        shmctl(shmid, IPC_RMID, NULL); // 删除共享内存
    } else { // 父进程
        data = shmat(shmid, (void*)0, 0);
        strcpy(data, "Hello, World!");
        wait(NULL); // 等待子进程结束
    }

    return 0;
}

示例运行

输入:

无输入

输出:

子进程收到的消息:Hello, World!

4. 信号(Signals)

信号是一种软件中断,用于在进程之间传递异步事件。信号可以用于简单的进程间通信,但通常用于错误处理和中断处理。

示例代码:信号

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

void signal_handler(int sig) {
    printf("收到信号:%d\n", sig);
}

int main() {
    signal(SIGINT, signal_handler); // 设置信号处理函数

    printf("按Ctrl+C发送SIGINT信号...\n");
    while (1) {
        sleep(1); // 休眠1秒
    }

    return 0;
}

示例运行

输入:

按Ctrl+C发送SIGINT信号...

输出:

收到信号:2

5. 总结

通过实现简单的进程间通信机制,可以理解不同IPC方法的特点和适用场景。管道适用于父子进程之间的单向通信,消息队列和共享内存适用于不相关进程之间的通信,而信号则用于简单的异步事件处理。在实际开发中,可以根据具体需求选择合适的IPC机制。

C语言实现的简单进程间通信(IPC)在多个领域有广泛应用,尤其在需要高效、低延迟或跨平台交互的场景中。以下是常见应用领域及实现方式:

系统编程与操作系统

操作系统内核中频繁使用IPC机制协调进程。例如,Linux系统调用通过管道或消息队列传递控制信息。C语言直接调用系统API实现高效通信。

// 匿名管道示例
int fd[2];
pipe(fd);  // 创建管道
if (fork() == 0) {
    write(fd[1], "data", 5);  // 子进程写入
} else {
    char buf[5];
    read(fd[0], buf, 5);      // 父进程读取
}

嵌入式系统

资源受限的嵌入式设备常使用共享内存或信号量。C语言的轻量级特性适合直接操作硬件寄存器或内存映射。

// 共享内存示例(POSIX)
int shm_fd = shm_open("/mem_region", O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, SIZE);
void *ptr = mmap(NULL, SIZE, PROT_WRITE, MAP_SHARED, shm_fd, 0);
sprintf(ptr, "Shared data");  // 写入共享内存

网络服务器与客户端

Web服务器(如Nginx)使用IPC处理多进程请求。C语言通过套接字或Unix域套接字实现跨网络或本机通信。

// Unix域套接字示例
struct sockaddr_un addr;
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, "/tmp/socket");
connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));

数据库系统

数据库引擎(如MySQL)利用信号量或文件锁同步进程访问。C语言直接调用fcntlsem_init实现并发控制。

// 文件锁示例
int fd = open("db.lock", O_CREAT);
flock(fd, LOCK_EX);  // 排他锁
// 临界区操作
flock(fd, LOCK_UN);

科学计算与高性能计算

MPI(消息传递接口)库用C语言实现多节点进程通信。适用于分布式计算场景。

// MPI发送接收示例
int data;
MPI_Recv(&data, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
MPI_Send(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);

实时系统

航空或工业控制系统中,C语言通过优先级信号量确保实时进程优先执行。

// 实时信号量示例
sem_t sem;
sem_init(&sem, 1, 1);  // 初始化信号量
sem_wait(&sem);        // 进入临界区
sem_post(&sem);        // 离开临界区

选择IPC方法时需考虑性能、复杂度及平台兼容性。管道和信号适合简单场景,共享内存和套接字适用于高性能需求。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐