Linux进程间通信(IPC)机制深度解析与实践指南

Linux进程间通信(IPC)机制深度解析与实践指南

引言

在Linux系统中,进程间通信(Inter-Process Communication, IPC)是操作系统核心功能之一,它允许不同进程之间进行数据交换和同步操作。随着现代软件系统越来越复杂,多进程架构已成为常态,理解并掌握各种IPC机制对于开发高性能、高可靠性的系统至关重要。

Linux提供了丰富的IPC机制,每种机制都有其特定的应用场景和优缺点。从简单的管道通信到复杂的套接字编程,从同步原语到共享内存,不同的IPC方式满足了不同场景下的通信需求。本文将深入探讨Linux下的各种IPC机制,通过丰富的代码示例和详细的原理解析,帮助读者全面掌握这一关键技术。

进程间通信不仅是技术实现的工具,更是系统架构设计的重要组成部分。合理选择IPC机制可以显著提升系统性能、增强系统稳定性,并降低维护成本。接下来,让我们逐一深入分析各种IPC机制的原理、实现和应用。

1. 管道(Pipe)

1.1 匿名管道

匿名管道是Linux中最基本的IPC机制,它提供了一种单向通信的通道,通常用于具有亲缘关系的进程间通信。

基本原理

匿名管道通过pipe()系统调用创建,返回两个文件描述符:一个用于读取,一个用于写入。数据在管道中以先进先出(FIFO)的方式流动。

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

#define BUFFER_SIZE 1024

int main() {
    int pipefd[2];
    pid_t pid;
    char buffer[BUFFER_SIZE];
    
    // 创建管道
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return 1;
    }
    
    // 创建子进程
    pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }
    
    if (pid == 0) {
        // 子进程 - 读取数据
        close(pipefd[1]);  // 关闭写端
        
        ssize_t bytes_read;
        while ((bytes_read = read(pipefd[0], buffer, BUFFER_SIZE - 1)) > 0) {
            buffer[bytes_read] = '\0';
            printf("Child received: %s", buffer);
        }
        
        close(pipefd[0]);
        _exit(0);
    } else {
        // 父进程 - 写入数据
        close(pipefd[0]);  // 关闭读端
        
        const char *messages[] = {
            "Hello from parent!\n",
            "This is a test message.\n",
            "Pipe communication works!\n",
            NULL
        };
        
        for (int i = 0; messages[i] != NULL; i++) {
            write(pipefd[1], messages[i], strlen(messages[i]));
            sleep(1);  // 模拟处理延迟
        }
        
        close(pipefd[1]);  // 关闭写端,子进程read返回0
        wait(NULL);        // 等待子进程退出
    }
    
    return 0;
}
管道缓冲区管理

管道的性能很大程度上取决于缓冲区的大小和管理策略。我们可以通过fcntl函数来查询和设置管道的缓冲区大小。

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

void check_pipe_buffer() {
    int pipefd[2];
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return;
    }
    
    // 获取管道缓冲区大小
    long pipe_size = fpathconf(pipefd[0], _PC_PIPE_BUF);
    printf("Pipe buffer size: %ld bytes\n", pipe_size);
    
    // 设置非阻塞模式
    int flags = fcntl(pipefd[1], F_GETFL);
    fcntl(pipefd[1], F_SETFL, flags | O_NONBLOCK);
    
    // 测试写入大量数据
    char buffer[65536];
    ssize_t written;
    
    while ((written = write(pipefd[1], buffer, sizeof(buffer))) > 0) {
        printf("Written: %zd bytes\n", written);
    }
    
    if (written == -1 && errno == EAGAIN) {
        printf("Pipe buffer full, would block\n");
    }
    
    close(pipefd[0]);
    close(pipefd[1]);
}

int main() {
    check_pipe_buffer();
    return 0;
}

1.2 命名管道(FIFO)

命名管道(FIFO)与匿名管道的主要区别在于它有一个关联的文件名,可以在不相关的进程间使用。

创建和使用FIFO
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE 1024

// FIFO写入进程
int fifo_writer() {
    int fd;
    char buffer[BUFFER_SIZE];
    
    // 创建FIFO(如果不存在)
    if (mkfifo(FIFO_NAME, 0666) == -1 && errno != EEXIST) {
        perror("mkfifo");
        return 1;
    }
    
    printf("Writer: opening FIFO...\n");
    fd = open(FIFO_NAME, O_WRONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }
    
    printf("Writer: connected. Type messages (Ctrl+D to exit):\n");
    
    while (fgets(buffer, BUFFER_SIZE, stdin) != NULL) {
        ssize_t bytes_written = write(fd, buffer, strlen(buffer));
        if (bytes_written == -1) {
            perror("write");
            break;
        }
        printf("Writer: sent %zd bytes\n", bytes_written);
    }
    
    close(fd);
    return 0;
}

// FIFO读取进程
int fifo_reader() {
    int fd;
    char buffer[BUFFER_SIZE];
    
    printf("Reader: opening FIFO...\n");
    fd = open(FIFO_NAME, O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }
    
    printf("Reader: connected. Waiting for messages...\n");
    
    ssize_t bytes_read;
    while ((bytes_read = read(fd, buffer, BUFFER_SIZE - 1)) > 0) {
        buffer[bytes_read] = '\0';
        printf("Reader received: %s", buffer);
    }
    
    if (bytes_read == -1) {
        perror("read");
    }
    
    close(fd);
    // 清理FIFO文件
    unlink(FIFO_NAME);
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s [reader|writer]\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "writer") == 0) {
        return fifo_writer();
    } else if (strcmp(argv[1], "reader") == 0) {
        return fifo_reader();
    } else {
        printf("Invalid argument. Use 'reader' or 'writer'\n");
        return 1;
    }
}
使用select进行多路复用的FIFO
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/select.h>

#define FIFO_NAME "/tmp/select_fifo"
#define BUFFER_SIZE 1024

int main() {
    int fd;
    fd_set readfds;
    char buffer[BUFFER_SIZE];
    
    // 创建FIFO
    if (mkfifo(FIFO_NAME, 0666) == -1) {
        perror("mkfifo");
        return 1;
    }
    
    fd = open(FIFO_NAME, O_RDWR | O_NONBLOCK);
    if (fd == -1) {
        perror("open");
        return 1;
    }
    
    printf("FIFO server started. Waiting for data (Ctrl+C to exit)...\n");
    
    while (1) {
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);
        FD_SET(STDIN_FILENO, &readfds);
        
        // 设置超时
        struct timeval timeout;
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;
        
        int max_fd = (fd > STDIN_FILENO) ? fd : STDIN_FILENO;
        int ready = select(max_fd + 1, &readfds, NULL, NULL, &timeout);
        
        if (ready == -1) {
            perror("select");
            break;
        } else if (ready == 0) {
            printf("Timeout occurred, no data received.\n");
            continue;
        }
        
        if (FD_ISSET(STDIN_FILENO, &readfds)) {
            if (fgets(buffer, BUFFER_SIZE, stdin) != NULL) {
                printf("You typed: %s", buffer);
            }
        }
        
        if (FD_ISSET(fd, &readfds)) {
            ssize_t bytes_read;
            while ((bytes_read = read(fd, buffer, BUFFER_SIZE - 1)) > 0) {
                buffer[bytes_read] = '\0';
                printf("Received from FIFO: %s", buffer);
            }
            
            if (bytes_read == 0) {
                printf("Client disconnected\n");
            }
        }
    }
    
    close(fd);
    unlink(FIFO_NAME);
    return 0;
}

2. 消息队列(Message Queue)

2.1 System V消息队列

System V消息队列提供了一种结构化的消息传递机制,支持消息类型和优先级。

基本消息队列操作
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <errno.h>

#define MAX_MSG_SIZE 1024
#define MSG_TYPE_TEXT 1
#define MSG_TYPE_EXIT 2

struct message {
    long mtype;
    char mtext[MAX_MSG_SIZE];
};

// 创建或获取消息队列
int create_message_queue() {
    key_t key = ftok("/tmp", 'A');
    if (key == -1) {
        perror("ftok");
        return -1;
    }
    
    int msgid = msgget(key, 0666 | IPC_CREAT);
    if (msgid == -1) {
        perror("msgget");
        return -1;
    }
    
    printf("Message queue created with ID: %d\n", msgid);
    return msgid;
}

// 发送消息
int send_message(int msgid, const char *text, long mtype) {
    struct message msg;
    msg.mtype = mtype;
    strncpy(msg.mtext, text, MAX_MSG_SIZE - 1);
    msg.mtext[MAX_MSG_SIZE - 1] = '\0';
    
    if (msgsnd(msgid, &msg, strlen(msg.mtext) + 1, 0) == -1) {
        perror("msgsnd");
        return -1;
    }
    
    printf("Sent message (type %ld): %s\n", mtype, text);
    return 0;
}

// 接收消息
int receive_message(int msgid, long mtype, int flags) {
    struct message msg;
    
    ssize_t bytes = msgrcv(msgid, &msg, MAX_MSG_SIZE, mtype, flags);
    if (bytes == -1) {
        if (errno != ENOMSG) {
            perror("msgrcv");
        }
        return -1;
    }
    
    msg.mtext[bytes] = '\0';
    printf("Received message (type %ld): %s\n", msg.mtype, msg.mtext);
    return 0;
}

// 消息队列服务器
int message_queue_server() {
    int msgid = create_message_queue();
    if (msgid == -1) {
        return 1;
    }
    
    printf("Server started. Waiting for messages...\n");
    
    while (1) {
        struct message msg;
        ssize_t bytes = msgrcv(msgid, &msg, MAX_MSG_SIZE, 0, 0);
        if (bytes == -1) {
            perror("msgrcv");
            break;
        }
        
        msg.mtext[bytes] = '\0';
        printf("Server received (type %ld): %s\n", msg.mtype, msg.mtext);
        
        if (msg.mtype == MSG_TYPE_EXIT) {
            printf("Exit message received. Shutting down...\n");
            break;
        }
        
        // 回显消息
        char response[MAX_MSG_SIZE];
        snprintf(response, MAX_MSG_SIZE, "Echo: %s", msg.mtext);
        send_message(msgid, response, msg.mtype + 100);
    }
    
    // 清理消息队列
    if (msgctl(msgid, IPC_RMID, NULL) == -1) {
        perror("msgctl");
    }
    
    return 0;
}

// 消息队列客户端
int message_queue_client() {
    int msgid = create_message_queue();
    if (msgid == -1) {
        return 1;
    }
    
    // 发送测试消息
    send_message(msgid, "Hello from client!", MSG_TYPE_TEXT);
    send_message(msgid, "This is a test message.", MSG_TYPE_TEXT);
    
    // 接收回显
    printf("Waiting for echoes...\n");
    for (int i = 0; i < 2; i++) {
        receive_message(msgid, MSG_TYPE_TEXT + 100, 0);
    }
    
    // 发送退出消息
    send_message(msgid, "Exit", MSG_TYPE_EXIT);
    
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s [server|client]\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "server") == 0) {
        return message_queue_server();
    } else if (strcmp(argv[1], "client") == 0) {
        return message_queue_client();
    } else {
        printf("Invalid argument. Use 'server' or 'client'\n");
        return 1;
    }
}
带优先级的消息队列
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <time.h>

#define MAX_MSG_SIZE 256
#define MSG_TYPE_HIGH 1
#define MSG_TYPE_MEDIUM 2
#define MSG_TYPE_LOW 3

struct priority_message {
    long mtype;
    char mtext[MAX_MSG_SIZE];
    int priority;
};

void send_priority_message(int msgid, const char *text, int priority) {
    struct priority_message msg;
    msg.mtype = priority;  // 使用优先级作为消息类型
    msg.priority = priority;
    strncpy(msg.mtext, text, MAX_MSG_SIZE - 1);
    msg.mtext[MAX_MSG_SIZE - 1] = '\0';
    
    if (msgsnd(msgid, &msg, sizeof(msg) - sizeof(long), 0) == -1) {
        perror("msgsnd");
        return;
    }
    
    printf("Sent message with priority %d: %s\n", priority, text);
}

void receive_priority_messages(int msgid) {
    printf("Receiving messages in priority order...\n");
    
    // 按优先级接收消息
    struct priority_message msg;
    
    // 先接收高优先级消息
    while (msgrcv(msgid, &msg, sizeof(msg) - sizeof(long), MSG_TYPE_HIGH, IPC_NOWAIT) != -1) {
        printf("High priority: %s\n", msg.mtext);
    }
    
    // 然后接收中优先级消息
    while (msgrcv(msgid, &msg, sizeof(msg) - sizeof(long), MSG_TYPE_MEDIUM, IPC_NOWAIT) != -1) {
        printf("Medium priority: %s\n", msg.mtext);
    }
    
    // 最后接收低优先级消息
    while (msgrcv(msgid, &msg, sizeof(msg) - sizeof(long), MSG_TYPE_LOW, IPC_NOWAIT) != -1) {
        printf("Low priority: %s\n", msg.mtext);
    }
}

int main() {
    key_t key = ftok("/tmp", 'B');
    int msgid = msgget(key, 0666 | IPC_CREAT);
    
    if (msgid == -1) {
        perror("msgget");
        return 1;
    }
    
    // 发送不同优先级的消息
    send_priority_message(msgid, "Low priority task", MSG_TYPE_LOW);
    send_priority_message(msgid, "High priority task", MSG_TYPE_HIGH);
    send_priority_message(msgid, "Medium priority task", MSG_TYPE_MEDIUM);
    send_priority_message(msgid, "Another low priority", MSG_TYPE_LOW);
    send_priority_message(msgid, "Critical task", MSG_TYPE_HIGH);
    
    sleep(1);  // 确保所有消息都已发送
    
    receive_priority_messages(msgid);
    
    // 清理
    msgctl(msgid, IPC_RMID, NULL);
    return 0;
}

2.2 POSIX消息队列

POSIX消息队列提供了更现代和灵活的接口,支持异步通知和消息优先级。

基本POSIX消息队列操作
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <errno.h>

#define QUEUE_NAME "/my_posix_queue"
#define MAX_MSG_SIZE 1024
#define MAX_MSG_COUNT 10

void posix_mq_sender() {
    mqd_t mq;
    struct mq_attr attr;
    char buffer[MAX_MSG_SIZE];
    
    // 设置消息队列属性
    attr.mq_flags = 0;
    attr.mq_maxmsg = MAX_MSG_COUNT;
    attr.mq_msgsize = MAX_MSG_SIZE;
    attr.mq_curmsgs = 0;
    
    // 创建或打开消息队列
    mq = mq_open(QUEUE_NAME, O_CREAT | O_WRONLY, 0644, &attr);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        return;
    }
    
    printf("POSIX MQ Sender started. Type messages (empty line to exit):\n");
    
    while (fgets(buffer, MAX_MSG_SIZE, stdin) != NULL) {
        if (strlen(buffer) == 1 && buffer[0] == '\n') {
            break;
        }
        
        // 发送消息
        if (mq_send(mq, buffer, strlen(buffer), 0) == -1) {
            perror("mq_send");
            break;
        }
        
        printf("Sent: %s", buffer);
    }
    
    mq_close(mq);
}

void posix_mq_receiver() {
    mqd_t mq;
    struct mq_attr attr;
    char buffer[MAX_MSG_SIZE];
    unsigned int priority;
    
    mq = mq_open(QUEUE_NAME, O_RDONLY);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        return;
    }
    
    // 获取队列属性
    if (mq_getattr(mq, &attr) == -1) {
        perror("mq_getattr");
        mq_close(mq);
        return;
    }
    
    printf("POSIX MQ Receiver started. Waiting for messages...\n");
    printf("Queue stats: max messages=%ld, message size=%ld, current messages=%ld\n",
           attr.mq_maxmsg, attr.mq_msgsize, attr.mq_curmsgs);
    
    while (1) {
        ssize_t bytes_read = mq_receive(mq, buffer, MAX_MSG_SIZE, &priority);
        if (bytes_read == -1) {
            if (errno != EAGAIN) {
                perror("mq_receive");
            }
            break;
        }
        
        buffer[bytes_read] = '\0';
        printf("Received (priority %u): %s", priority, buffer);
        
        if (strcmp(buffer, "exit\n") == 0) {
            printf("Exit message received.\n");
            break;
        }
    }
    
    mq_close(mq);
    mq_unlink(QUEUE_NAME);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s [sender|receiver]\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "sender") == 0) {
        posix_mq_sender();
    } else if (strcmp(argv[1], "receiver") == 0) {
        posix_mq_receiver();
    } else {
        printf("Invalid argument. Use 'sender' or 'receiver'\n");
        return 1;
    }
    
    return 0;
}
使用信号通知的POSIX消息队列
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>

#define NOTIFY_QUEUE_NAME "/notify_queue"
#define NOTIFY_MAX_MSG_SIZE 256
#define NOTIFY_MAX_MSG_COUNT 10

static volatile int notification_received = 0;

// 通知处理函数
void notification_handler(union sigval sv) {
    mqd_t *mq = sv.sival_ptr;
    struct mq_attr attr;
    char buffer[NOTIFY_MAX_MSG_SIZE];
    ssize_t bytes_read;
    
    printf("Notification received! Reading messages...\n");
    
    // 获取队列属性
    if (mq_getattr(*mq, &attr) == -1) {
        perror("mq_getattr");
        return;
    }
    
    // 读取所有可用消息
    while ((bytes_read = mq_receive(*mq, buffer, NOTIFY_MAX_MSG_SIZE, NULL)) != -1) {
        buffer[bytes_read] = '\0';
        printf("Async received: %s", buffer);
    }
    
    if (errno != EAGAIN) {
        perror("mq_receive");
    }
    
    notification_received = 1;
}

void posix_mq_async_receiver() {
    mqd_t mq;
    struct mq_attr attr;
    struct sigevent sev;
    
    // 设置队列属性
    attr.mq_flags = 0;
    attr.mq_maxmsg = NOTIFY_MAX_MSG_COUNT;
    attr.mq_msgsize = NOTIFY_MAX_MSG_SIZE;
    attr.mq_curmsgs = 0;
    
    // 创建消息队列
    mq = mq_open(NOTIFY_QUEUE_NAME, O_CREAT | O_RDONLY | O_NONBLOCK, 0644, &attr);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        return;
    }
    
    // 设置异步通知
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = notification_handler;
    sev.sigev_notify_attributes = NULL;
    sev.sigev_value.sival_ptr = &mq;
    
    if (mq_notify(mq, &sev) == -1) {
        perror("mq_notify");
        mq_close(mq);
        mq_unlink(NOTIFY_QUEUE_NAME);
        return;
    }
    
    printf("Async receiver started. Waiting for notifications...\n");
    
    // 主循环
    while (1) {
        sleep(1);
        
        // 重新注册通知(每次通知后需要重新注册)
        if (notification_received) {
            printf("Re-registering notification...\n");
            if (mq_notify(mq, &sev) == -1) {
                perror("mq_notify re-register");
                break;
            }
            notification_received = 0;
        }
        
        printf("Main thread working...\n");
    }
    
    mq_close(mq);
    mq_unlink(NOTIFY_QUEUE_NAME);
}

void posix_mq_async_sender() {
    mqd_t mq = mq_open(NOTIFY_QUEUE_NAME, O_WRONLY);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        return;
    }
    
    const char *messages[] = {
        "First async message\n",
        "Second async message\n",
        "Third async message\n",
        "exit\n",
        NULL
    };
    
    for (int i = 0; messages[i] != NULL; i++) {
        printf("Sending: %s", messages[i]);
        if (mq_send(mq, messages[i], strlen(messages[i]), 0) == -1) {
            perror("mq_send");
            break;
        }
        sleep(2);  // 给接收者处理时间
    }
    
    mq_close(mq);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s [sender|receiver]\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "sender") == 0) {
        posix_mq_async_sender();
    } else if (strcmp(argv[1], "receiver") == 0) {
        posix_mq_async_receiver();
    } else {
        printf("Invalid argument. Use 'sender' or 'receiver'\n");
        return 1;
    }
    
    return 0;
}

3. 共享内存(Shared Memory)

3.1 System V共享内存

System V共享内存允许多个进程共享同一块内存区域,是实现高性能进程间通信的有效方式。

基本共享内存操作
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <unistd.h>
#include <errno.h>

#define SHM_SIZE 1024
#define SHM_KEY 0x1234
#define SEM_KEY 0x5678

// 简单的信号量操作
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

int create_semaphore() {
    int semid = semget(SEM_KEY, 1, 0666 | IPC_CREAT);
    if (semid == -1) {
        perror("semget");
        return -1;
    }
    
    // 初始化信号量值为1(二进制信号量)
    union semun arg;
    arg.val = 1;
    if (semctl(semid, 0, SETVAL, arg) == -1) {
        perror("semctl");
        return -1;
    }
    
    return semid;
}

void semaphore_wait(int semid) {
    struct sembuf sb = {0, -1, 0};
    if (semop(semid, &sb, 1) == -1) {
        perror("semop wait");
    }
}

void semaphore_signal(int semid) {
    struct sembuf sb = {0, 1, 0};
    if (semop(semid, &sb, 1) == -1) {
        perror("semop signal");
    }
}

// 共享内存写入进程
int shm_writer() {
    int shmid, semid;
    char *shm_ptr;
    
    // 创建共享内存段
    shmid = shmget(SHM_KEY, SHM_SIZE, 0666 | IPC_CREAT);
    if (shmid == -1) {
        perror("shmget");
        return 1;
    }
    
    // 附加共享内存
    shm_ptr = shmat(shmid, NULL, 0);
    if (shm_ptr == (char *)-1) {
        perror("shmat");
        return 1;
    }
    
    // 创建信号量
    semid = create_semaphore();
    if (semid == -1) {
        return 1;
    }
    
    printf("Writer attached to shared memory at %p\n", shm_ptr);
    
    const char *messages[] = {
        "Hello from writer!",
        "This is shared memory communication.",
        "Multiple processes can access this memory.",
        "Goodbye!",
        NULL
    };
    
    for (int i = 0; messages[i] != NULL; i++) {
        semaphore_wait(semid);  // 获取锁
        
        // 写入数据到共享内存
        snprintf(shm_ptr, SHM_SIZE, "%s", messages[i]);
        printf("Writer wrote: %s\n", messages[i]);
        
        semaphore_signal(semid);  // 释放锁
        
        sleep(2);  // 让读者有时间读取
    }
    
    // 标记结束
    semaphore_wait(semid);
    snprintf(shm_ptr, SHM_SIZE, "END");
    semaphore_signal(semid);
    
    // 分离共享内存
    if (shmdt(shm_ptr) == -1) {
        perror("shmdt");
    }
    
    printf("Writer finished.\n");
    return 0;
}

// 共享内存读取进程
int shm_reader() {
    int shmid, semid;
    char *shm_ptr;
    char buffer[SHM_SIZE];
    
    // 获取共享内存段
    shmid = shmget(SHM_KEY, SHM_SIZE, 0666);
    if (shmid == -1) {
        perror("shmget");
        return 1;
    }
    
    // 附加共享内存
    shm_ptr = shmat(shmid, NULL, 0);
    if (shm_ptr == (char *)-1) {
        perror("shmat");
        return 1;
    }
    
    // 获取信号量
    semid = semget(SEM_KEY, 1, 0666);
    if (semid == -1) {
        perror("semget");
        return 1;
    }
    
    printf("Reader attached to shared memory at %p\n", shm_ptr);
    printf("Reader started. Waiting for data...\n");
    
    while (1) {
        semaphore_wait(semid);  // 获取锁
        
        // 从共享内存读取数据
        strncpy(buffer, shm_ptr, SHM_SIZE - 1);
        buffer[SHM_SIZE - 1] = '\0';
        
        semaphore_signal(semid);  // 释放锁
        
        if (strcmp(buffer, "END") == 0) {
            printf("Reader received end signal.\n");
            break;
        }
        
        if (strlen(buffer) > 0) {
            printf("Reader read: %s\n", buffer);
        }
        
        sleep(1);  // 等待新数据
    }
    
    // 分离共享内存
    if (shmdt(shm_ptr) == -1) {
        perror("shmdt");
    }
    
    // 清理资源
    shmctl(shmid, IPC_RMID, NULL);
    semctl(semid, 0, IPC_RMID);
    
    printf("Reader finished.\n");
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s [writer|reader]\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "writer") == 0) {
        return shm_writer();
    } else if (strcmp(argv[1], "reader") == 0) {
        return shm_reader();
    } else {
        printf("Invalid argument. Use 'writer' or 'reader'\n");
        return 1;
    }
}
共享内存与进程间同步
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <unistd.h>
#include <sys/wait.h>

#define SHM_KEY 0x9999
#define SEM_KEY 0x8888
#define NUM_PROCESSES 3
#define NUM_ITERATIONS 5

struct shared_data {
    int counter;
    int process_completed[NUM_PROCESSES];
    char messages[NUM_PROCESSES][256];
};

int create_semaphore_set() {
    int semid = semget(SEM_KEY, 2, 0666 | IPC_CREAT);
    if (semid == -1) {
        perror("semget");
        return -1;
    }
    
    // 初始化信号量
    union semun arg;
    unsigned short values[2] = {1, 0};  // sem0: mutex, sem1: barrier
    arg.array = values;
    
    if (semctl(semid, 0, SETALL, arg) == -1) {
        perror("semctl SETALL");
        return -1;
    }
    
    return semid;
}

void semaphore_op(int semid, int sem_num, int op) {
    struct sembuf sb = {sem_num, op, 0};
    if (semop(semid, &sb, 1) == -1) {
        perror("semop");
    }
}

void worker_process(int process_id, int shmid, int semid) {
    struct shared_data *shm_ptr = shmat(shmid, NULL, 0);
    if (shm_ptr == (struct shared_data *)-1) {
        perror("shmat");
        exit(1);
    }
    
    printf("Process %d started\n", process_id);
    
    for (int i = 0; i < NUM_ITERATIONS; i++) {
        // 进入临界区
        semaphore_op(semid, 0, -1);
        
        // 修改共享数据
        shm_ptr->counter++;
        snprintf(shm_ptr->messages[process_id], 256, 
                "Process %d: iteration %d, counter=%d", 
                process_id, i, shm_ptr->counter);
        
        printf("Process %d: counter=%d\n", process_id, shm_ptr->counter);
        
        // 离开临界区
        semaphore_op(semid, 0, 1);
        
        sleep(1);  // 模拟工作
    }
    
    // 标记进程完成
    semaphore_op(semid, 0, -1);
    shm_ptr->process_completed[process_id] = 1;
    semaphore_op(semid, 0, 1);
    
    // 检查是否所有进程都完成
    int all_done;
    do {
        semaphore_op(semid, 0, -1);
        all_done = 1;
        for (int i = 0; i < NUM_PROCESSES; i++) {
            if (!shm_ptr->process_completed[i]) {
                all_done = 0;
                break;
            }
        }
        semaphore_op(semid, 0, 1);
        
        if (!all_done) {
            sleep(1);
        }
    } while (!all_done);
    
    printf("Process %d finished\n", process_id);
    shmdt(shm_ptr);
}

int main() {
    int shmid, semid;
    pid_t pids[NUM_PROCESSES];
    
    // 创建共享内存
    shmid = shmget(SHM_KEY, sizeof(struct shared_data), 0666 | IPC_CREAT);
    if (shmid == -1) {
        perror("shmget");
        return 1;
    }
    
    // 初始化共享内存
    struct shared_data *shm_ptr = shmat(shmid, NULL, 0);
    if (shm_ptr == (struct shared_data *)-1) {
        perror("shmat");
        return 1;
    }
    
    memset(shm_ptr, 0, sizeof(struct shared_data));
    shmdt(shm_ptr);
    
    // 创建信号量集
    semid = create_semaphore_set();
    if (semid == -1) {
        return 1;
    }
    
    // 创建子进程
    for (int i = 0; i < NUM_PROCESSES; i++) {
        pids[i] = fork();
        if (pids[i] == 0) {
            // 子进程
            worker_process(i, shmid, semid);
            exit(0);
        } else if (pids[i] < 0) {
            perror("fork");
            return 1;
        }
    }
    
    // 等待所有子进程完成
    for (int i = 0; i < NUM_PROCESSES; i++) {
        waitpid(pids[i], NULL, 0);
    }
    
    // 读取最终结果
    shm_ptr = shmat(shmid, NULL, 0);
    printf("\nFinal results:\n");
    printf("Final counter value: %d\n", shm_ptr->counter);
    for (int i = 0; i < NUM_PROCESSES; i++) {
        printf("Message from process %d: %s\n", i, shm_ptr->messages[i]);
    }
    
    // 清理资源
    shmdt(shm_ptr);
    shmctl(shmid, IPC_RMID, NULL);
    semctl(semid, 0, IPC_RMID);
    
    printf("All processes completed successfully.\n");
    return 0;
}

3.2 POSIX共享内存

POSIX共享内存提供了更简单、更符合现代编程习惯的接口。

基本POSIX共享内存操作
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <semaphore.h>
#include <errno.h>

#define SHM_NAME "/posix_shared_mem"
#define SEM_NAME "/posix_shared_sem"
#define SHM_SIZE 4096

struct shared_data {
    int counter;
    char message[256];
    int data_ready;
};

int posix_shm_writer() {
    int shm_fd;
    struct shared_data *shm_ptr;
    sem_t *semaphore;
    
    // 创建共享内存对象
    shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) {
        perror("shm_open");
        return 1;
    }
    
    // 设置共享内存大小
    if (ftruncate(shm_fd, SHM_SIZE) == -1) {
        perror("ftruncate");
        return 1;
    }
    
    // 内存映射
    shm_ptr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (shm_ptr == MAP_FAILED) {
        perror("mmap");
        return 1;
    }
    
    // 创建命名信号量
    semaphore = sem_open(SEM_NAME, O_CREAT, 0666, 1);
    if (semaphore == SEM_FAILED) {
        perror("sem_open");
        return 1;
    }
    
    printf("POSIX SHM Writer started.\n");
    
    const char *messages[] = {
        "Hello POSIX shared memory!",
        "This is more efficient than System V",
        "POSIX API is simpler to use",
        "Goodbye!",
        NULL
    };
    
    for (int i = 0; messages[i] != NULL; i++) {
        sem_wait(semaphore);  // 获取锁
        
        // 写入数据
        shm_ptr->counter = i + 1;
        strncpy(shm_ptr->message, messages[i], 255);
        shm_ptr->message[255] = '\0';
        shm_ptr->data_ready = 1;
        
        printf("Writer: counter=%d, message=%s\n", shm_ptr->counter, shm_ptr->message);
        
        sem_post(semaphore);  // 释放锁
        
        sleep(2);
    }
    
    // 标记结束
    sem_wait(semaphore);
    shm_ptr->data_ready = -1;
    sem_post(semaphore);
    
    // 清理
    munmap(shm_ptr, SHM_SIZE);
    close(shm_fd);
    sem_close(semaphore);
    
    printf("Writer finished.\n");
    return 0;
}

int posix_shm_reader() {
    int shm_fd;
    struct shared_data *shm_ptr;
    sem_t *semaphore;
    
    // 打开共享内存对象
    shm_fd = shm_open(SHM_NAME, O_RDWR, 0666);
    if (shm_fd == -1) {
        perror("shm_open");
        return 1;
    }
    
    // 内存映射
    shm_ptr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (shm_ptr == MAP_FAILED) {
        perror("mmap");
        return 1;
    }
    
    // 打开命名信号量
    semaphore = sem_open(SEM_NAME, 0);
    if (semaphore == SEM_FAILED) {
        perror("sem_open");
        return 1;
    }
    
    printf("POSIX SHM Reader started.\n");
    
    while (1) {
        sem_wait(semaphore);
        
        if (shm_ptr->data_ready == -1) {
            sem_post(semaphore);
            break;
        }
        
        if (shm_ptr->data_ready) {
            printf("Reader: counter=%d, message=%s\n", shm_ptr->counter, shm_ptr->message);
            shm_ptr->data_ready = 0;  // 标记为已读
        }
        
        sem_post(semaphore);
        sleep(1);
    }
    
    // 清理
    munmap(shm_ptr, SHM_SIZE);
    close(shm_fd);
    sem_close(semaphore);
    
    // 删除共享内存和信号量
    shm_unlink(SHM_NAME);
    sem_unlink(SEM_NAME);
    
    printf("Reader finished.\n");
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s [writer|reader]\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "writer") == 0) {
        return posix_shm_writer();
    } else if (strcmp(argv[1], "reader") == 0) {
        return posix_shm_reader();
    } else {
        printf("Invalid argument. Use 'writer' or 'reader'\n");
        return 1;
    }
}

4. 信号量(Semaphore)

4.1 System V信号量

System V信号量提供了复杂的信号量操作,支持信号量集和原子操作。

生产者-消费者问题
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <string.h>
#include <time.h>

#define BUFFER_SIZE 5
#define NUM_ITEMS 10

// 定义信号量索引
#define MUTEX 0    // 互斥信号量
#define EMPTY 1    // 空槽位信号量
#define FULL 2     // 满槽位信号量

struct buffer {
    int items[BUFFER_SIZE];
    int in;
    int out;
};

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

// 初始化信号量
int init_semaphore(int semid, int semnum, int value) {
    union semun arg;
    arg.val = value;
    return semctl(semid, semnum, SETVAL, arg);
}

// P操作(等待)
void sem_wait(int semid, int semnum) {
    struct sembuf sb = {semnum, -1, 0};
    semop(semid, &sb, 1);
}

// V操作(信号)
void sem_signal(int semid, int semnum) {
    struct sembuf sb = {semnum, 1, 0};
    semop(semid, &sb, 1);
}

// 生产者进程
void producer(int semid, struct buffer *buf) {
    printf("Producer started (PID: %d)\n", getpid());
    
    for (int i = 1; i <= NUM_ITEMS; i++) {
        // 生产一个项目
        sleep(rand() % 3 + 1);  // 模拟生产时间
        
        sem_wait(semid, EMPTY);  // 等待空槽位
        sem_wait(semid, MUTEX);  // 进入临界区
        
        // 将项目放入缓冲区
        buf->items[buf->in] = i;
        printf("Producer produced: %d at position %d\n", i, buf->in);
        buf->in = (buf->in + 1) % BUFFER_SIZE;
        
        sem_signal(semid, MUTEX);  // 离开临界区
        sem_signal(semid, FULL);   // 增加满槽位计数
    }
    
    printf("Producer finished\n");
}

// 消费者进程
void consumer(int semid, struct buffer *buf) {
    printf("Consumer started (PID: %d)\n", getpid());
    
    for (int i = 0; i < NUM_ITEMS; i++) {
        sem_wait(semid, FULL);    // 等待满槽位
        sem_wait(semid, MUTEX);   // 进入临界区
        
        // 从缓冲区取出项目
        int item = buf->items[buf->out];
        printf("Consumer consumed: %d from position %d\n", item, buf->out);
        buf->out = (buf->out + 1) % BUFFER_SIZE;
        
        sem_signal(semid, MUTEX);  // 离开临界区
        sem_signal(semid, EMPTY);  // 增加空槽位计数
        
        // 消费项目
        sleep(rand() % 3 + 1);  // 模拟消费时间
    }
    
    printf("Consumer finished\n");
}

int main() {
    srand(time(NULL));
    
    key_t key = ftok("/tmp", 'S');
    int shmid, semid;
    struct buffer *buf;
    
    // 创建共享内存
    shmid = shmget(key, sizeof(struct buffer), 0666 | IPC_CREAT);
    if (shmid == -1) {
        perror("shmget");
        return 1;
    }
    
    // 附加共享内存
    buf = shmat(shmid, NULL, 0);
    if (buf == (struct buffer *)-1) {
        perror("shmat");
        return 1;
    }
    
    // 初始化缓冲区
    memset(buf, 0, sizeof(struct buffer));
    buf->in = 0;
    buf->out = 0;
    
    // 创建信号量集
    semid = semget(key, 3, 0666 | IPC_CREAT);
    if (semid == -1) {
        perror("semget");
        return 1;
    }
    
    // 初始化信号量
    if (init_semaphore(semid, MUTEX, 1) == -1 ||
        init_semaphore(semid, EMPTY, BUFFER_SIZE) == -1 ||
        init_semaphore(semid, FULL, 0) == -1) {
        perror("semctl");
        return 1;
    }
    
    // 创建生产者进程
    pid_t prod_pid = fork();
    if (prod_pid == 0) {
        producer(semid, buf);
        exit(0);
    } else if (prod_pid < 0) {
        perror("fork producer");
        return 1;
    }
    
    // 创建消费者进程
    pid_t cons_pid = fork();
    if (cons_pid == 0) {
        consumer(semid, buf);
        exit(0);
    } else if (cons_pid < 0) {
        perror("fork consumer");
        return 1;
    }
    
    // 等待子进程完成
    waitpid(prod_pid, NULL, 0);
    waitpid(cons_pid, NULL, 0);
    
    // 清理资源
    shmdt(buf);
    shmctl(shmid, IPC_RMID, NULL);
    semctl(semid, 0, IPC_RMID);
    
    printf("Producer-consumer simulation completed.\n");
    return 0;
}

4.2 POSIX信号量

POSIX信号量提供了更简洁的API,分为命名信号量和未命名信号量。

命名信号量示例
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>

#define NUM_THREADS 5
#define SEM_NAME "/my_named_sem"

// 线程函数
void* worker_thread(void* arg) {
    int thread_id = *(int*)arg;
    sem_t *semaphore;
    
    // 打开命名信号量
    semaphore = sem_open(SEM_NAME, 0);
    if (semaphore == SEM_FAILED) {
        perror("sem_open");
        pthread_exit(NULL);
    }
    
    printf("Thread %d: waiting for semaphore...\n", thread_id);
    
    // 获取信号量
    if (sem_wait(semaphore) {
        perror("sem_wait");
        pthread_exit(NULL);
    }
    
    printf("Thread %d: acquired semaphore. Working...\n", thread_id);
    sleep(2);  // 模拟工作
    printf("Thread %d: work completed. Releasing semaphore.\n", thread_id);
    
    // 释放信号量
    if (sem_post(semaphore)) {
        perror("sem_post");
    }
    
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[NUM_THREADS];
    int thread_ids[NUM_THREADS];
    sem_t *semaphore;
    
    // 创建命名信号量
    semaphore = sem_open(SEM_NAME, O_CREAT, 0666, 3);  // 允许3个线程同时访问
    if (semaphore == SEM_FAILED) {
        perror("sem_open");
        return 1;
    }
    
    printf("Created named semaphore with value 3\n");
    
    // 创建线程
    for (int i = 0; i < NUM_THREADS; i++) {
        thread_ids[i] = i + 1;
        if (pthread_create(&threads[i], NULL, worker_thread, &thread_ids[i])) {
            perror("pthread_create");
            return 1;
        }
    }
    
    // 等待所有线程完成
    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }
    
    // 关闭和删除信号量
    sem_close(semaphore);
    sem_unlink(SEM_NAME);
    
    printf("All threads completed.\n");
    return 0;
}
未命名信号量示例
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/mman.h>

#define NUM_THREADS 4
#define NUM_ITERATIONS 3

struct shared_data {
    sem_t mutex;
    sem_t barrier;
    int counter;
    int waiting;
};

void* worker(void* arg) {
    struct shared_data *data = (struct shared_data*)arg;
    
    for (int i = 0; i < NUM_ITERATIONS; i++) {
        // 使用互斥锁保护共享数据
        sem_wait(&data->mutex);
        data->counter++;
        printf("Thread %lu: counter = %d (iteration %d)\n", 
               pthread_self(), data->counter, i + 1);
        sem_post(&data->mutex);
        
        // 屏障同步
        sem_wait(&data->mutex);
        data->waiting++;
        if (data->waiting == NUM_THREADS) {
            // 最后一个到达的线程释放屏障
            for (int j = 0; j < NUM_THREADS - 1; j++) {
                sem_post(&data->barrier);
            }
            data->waiting = 0;
            printf("--- Barrier released ---\n");
        } else {
            sem_post(&data->mutex);
            sem_wait(&data->barrier);
        }
        sem_post(&data->mutex);
        
        sleep(1);  // 模拟工作
    }
    
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[NUM_THREADS];
    struct shared_data *data;
    
    // 使用共享内存分配数据(用于进程间共享)
    data = mmap(NULL, sizeof(struct shared_data), 
                PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (data == MAP_FAILED) {
        perror("mmap");
        return 1;
    }
    
    // 初始化未命名信号量(进程间共享)
    if (sem_init(&data->mutex, 1, 1) == -1) {
        perror("sem_init mutex");
        return 1;
    }
    
    if (sem_init(&data->barrier, 1, 0) == -1) {
        perror("sem_init barrier");
        return 1;
    }
    
    data->counter = 0;
    data->waiting = 0;
    
    printf("Starting %d threads with barrier synchronization...\n", NUM_THREADS);
    
    // 创建线程
    for (int i = 0; i < NUM_THREADS; i++) {
        if (pthread_create(&threads[i], NULL, worker, data)) {
            perror("pthread_create");
            return 1;
        }
    }
    
    // 等待线程完成
    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }
    
    printf("Final counter value: %d\n", data->counter);
    
    // 清理
    sem_destroy(&data->mutex);
    sem_destroy(&data->barrier);
    munmap(data, sizeof(struct shared_data));
    
    return 0;
}

5. 信号(Signal)

信号是Linux系统中用于进程间异步通信的基本机制。

信号处理示例
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

volatile sig_atomic_t signal_received = 0;
volatile sig_atomic_t usr1_count = 0;
volatile sig_atomic_t usr2_count = 0;

// SIGINT处理函数
void sigint_handler(int sig) {
    signal_received = sig;
    printf("\nReceived SIGINT (Ctrl+C). Cleaning up...\n");
}

// SIGUSR1处理函数
void sigusr1_handler(int sig) {
    usr1_count++;
    printf("Received SIGUSR1 (%d times)\n", usr1_count);
}

// SIGUSR2处理函数
void sigusr2_handler(int sig) {
    usr2_count++;
    printf("Received SIGUSR2 (%d times)\n", usr2_count);
}

// SIGALRM处理函数
void sigalrm_handler(int sig) {
    printf("Alarm! Timeout occurred.\n");
}

// 信号集操作演示
void demonstrate_signal_sets() {
    sigset_t set, oldset;
    
    // 初始化信号集
    sigemptyset(&set);
    sigaddset(&set, SIGINT);
    sigaddset(&set, SIGQUIT);
    
    // 阻塞信号
    if (sigprocmask(SIG_BLOCK, &set, &oldset) == -1) {
        perror("sigprocmask");
        return;
    }
    
    printf("SIGINT and SIGQUIT are now blocked for 5 seconds...\n");
    printf("Try pressing Ctrl+C or Ctrl+\\ - they won't work.\n");
    sleep(5);
    
    // 恢复原来的信号掩码
    if (sigprocmask(SIG_SETMASK, &oldset, NULL) == -1) {
        perror("sigprocmask");
    }
    
    printf("Signals unblocked. Now they will work.\n");
}

// 发送信号给其他进程
void signal_sender() {
    printf("Signal sender started (PID: %d)\n", getpid());
    
    pid_t my_pid = getpid();
    
    // 给自己发送信号
    for (int i = 0; i < 3; i++) {
        sleep(2);
        if (kill(my_pid, SIGUSR1) == -1) {
            perror("kill");
        }
    }
    
    for (int i = 0; i < 2; i++) {
        sleep(2);
        if (kill(my_pid, SIGUSR2) == -1) {
            perror("kill");
        }
    }
}

// 实时信号处理
void realtime_signal_demo() {
    struct sigaction sa;
    
    // 设置实时信号处理
    sa.sa_sigaction = (void (*)(int, siginfo_t *, void *))sigusr1_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;  // 使用sa_sigaction而不是sa_handler
    
    if (sigaction(SIGRTMIN, &sa, NULL) == -1) {
        perror("sigaction");
        return;
    }
    
    printf("Real-time signal handler installed for SIGRTMIN\n");
}

int main() {
    struct sigaction sa_int, sa_usr1, sa_usr2, sa_alrm;
    
    printf("Signal demonstration program (PID: %d)\n", getpid());
    
    // 设置SIGINT处理
    sa_int.sa_handler = sigint_handler;
    sigemptyset(&sa_int.sa_mask);
    sa_int.sa_flags = 0;
    
    if (sigaction(SIGINT, &sa_int, NULL) == -1) {
        perror("sigaction SIGINT");
        return 1;
    }
    
    // 设置SIGUSR1处理
    sa_usr1.sa_handler = sigusr1_handler;
    sigemptyset(&sa_usr1.sa_mask);
    sa_usr1.sa_flags = 0;
    
    if (sigaction(SIGUSR1, &sa_usr1, NULL) == -1) {
        perror("sigaction SIGUSR1");
        return 1;
    }
    
    // 设置SIGUSR2处理
    sa_usr2.sa_handler = sigusr2_handler;
    sigemptyset(&sa_usr2.sa_mask);
    sa_usr2.sa_flags = 0;
    
    if (sigaction(SIGUSR2, &sa_usr2, NULL) == -1) {
        perror("sigaction SIGUSR2");
        return 1;
    }
    
    // 设置SIGALRM处理
    sa_alrm.sa_handler = sigalrm_handler;
    sigemptyset(&sa_alrm.sa_mask);
    sa_alrm.sa_flags = 0;
    
    if (sigaction(SIGALRM, &sa_alrm, NULL) == -1) {
        perror("sigaction SIGALRM");
        return 1;
    }
    
    // 演示信号集
    demonstrate_signal_sets();
    
    // 设置定时器
    printf("Setting alarm for 10 seconds...\n");
    alarm(10);
    
    // 主循环
    printf("Entering main loop. Send signals or press Ctrl+C to exit.\n");
    printf("You can send signals from another terminal: kill -USR1 %d\n", getpid());
    
    while (!signal_received) {
        printf("Main loop running... (SIGUSR1: %d, SIGUSR2: %d)\n", 
               usr1_count, usr2_count);
        sleep(3);
    }
    
    printf("Program exiting gracefully.\n");
    return 0;
}

6. 套接字(Socket)

6.1 Unix域套接字

Unix域套接字提供了同一台主机上进程间通信的高效方式。

流式套接字示例
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>

#define SOCKET_PATH "/tmp/unix_socket_example"
#define BUFFER_SIZE 1024

void unix_socket_server() {
    int server_fd, client_fd;
    struct sockaddr_un server_addr, client_addr;
    socklen_t client_len;
    char buffer[BUFFER_SIZE];
    ssize_t bytes_read;
    
    // 创建Unix域套接字
    server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (server_fd == -1) {
        perror("socket");
        exit(1);
    }
    
    // 设置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
    
    // 删除可能已存在的socket文件
    unlink(SOCKET_PATH);
    
    // 绑定套接字
    if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind");
        close(server_fd);
        exit(1);
    }
    
    // 开始监听
    if (listen(server_fd, 5) == -1) {
        perror("listen");
        close(server_fd);
        exit(1);
    }
    
    printf("Unix socket server listening on %s\n", SOCKET_PATH);
    
    while (1) {
        // 接受客户端连接
        client_len = sizeof(client_addr);
        client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
        if (client_fd == -1) {
            perror("accept");
            continue;
        }
        
        printf("Client connected\n");
        
        // 处理客户端请求
        while ((bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1)) > 0) {
            buffer[bytes_read] = '\0';
            printf("Received: %s", buffer);
            
            // 回显消息
            if (write(client_fd, buffer, bytes_read) == -1) {
                perror("write");
                break;
            }
            
            // 检查退出条件
            if (strncmp(buffer, "exit", 4) == 0) {
                break;
            }
        }
        
        if (bytes_read == -1) {
            perror("read");
        }
        
        printf("Client disconnected\n");
        close(client_fd);
    }
    
    close(server_fd);
    unlink(SOCKET_PATH);
}

void unix_socket_client() {
    int sockfd;
    struct sockaddr_un server_addr;
    char buffer[BUFFER_SIZE];
    ssize_t bytes_read, bytes_written;
    
    // 创建Unix域套接字
    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(1);
    }
    
    // 设置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
    
    // 连接服务器
    if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
        perror("connect");
        close(sockfd);
        exit(1);
    }
    
    printf("Connected to server. Type messages (type 'exit' to quit):\n");
    
    while (fgets(buffer, BUFFER_SIZE, stdin) != NULL) {
        // 发送消息
        bytes_written = write(sockfd, buffer, strlen(buffer));
        if (bytes_written == -1) {
            perror("write");
            break;
        }
        
        // 接收回显
        bytes_read = read(sockfd, buffer, BUFFER_SIZE - 1);
        if (bytes_read == -1) {
            perror("read");
            break;
        }
        
        buffer[bytes_read] = '\0';
        printf("Echo: %s", buffer);
        
        if (strncmp(buffer, "exit", 4) == 0) {
            break;
        }
    }
    
    close(sockfd);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s [server|client]\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "server") == 0) {
        unix_socket_server();
    } else if (strcmp(argv[1], "client") == 0) {
        unix_socket_client();
    } else {
        printf("Invalid argument. Use 'server' or 'client'\n");
        return 1;
    }
    
    return 0;
}
数据报套接字示例
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>

#define DGRAM_SOCKET_PATH "/tmp/unix_dgram_socket"
#define CLIENT_SOCKET_PATH "/tmp/unix_dgram_client"
#define BUFFER_SIZE 1024

void unix_dgram_server() {
    int sockfd;
    struct sockaddr_un server_addr, client_addr;
    socklen_t client_len;
    char buffer[BUFFER_SIZE];
    ssize_t bytes_received;
    
    // 创建数据报套接字
    sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(1);
    }
    
    // 设置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strncpy(server_addr.sun_path, DGRAM_SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
    
    // 删除可能已存在的socket文件
    unlink(DGRAM_SOCKET_PATH);
    
    // 绑定套接字
    if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind");
        close(sockfd);
        exit(1);
    }
    
    printf("Unix datagram server listening on %s\n", DGRAM_SOCKET_PATH);
    
    while (1) {
        // 接收数据
        client_len = sizeof(client_addr);
        bytes_received = recvfrom(sockfd, buffer, BUFFER_SIZE - 1, 0,
                                 (struct sockaddr*)&client_addr, &client_len);
        
        if (bytes_received == -1) {
            perror("recvfrom");
            continue;
        }
        
        buffer[bytes_received] = '\0';
        printf("Received from %s: %s", 
               client_addr.sun_path[0] ? client_addr.sun_path : "unknown", 
               buffer);
        
        // 发送响应
        if (sendto(sockfd, buffer, bytes_received, 0,
                  (struct sockaddr*)&client_addr, client_len) == -1) {
            perror("sendto");
        }
        
        if (strncmp(buffer, "exit", 4) == 0) {
            break;
        }
    }
    
    close(sockfd);
    unlink(DGRAM_SOCKET_PATH);
}

void unix_dgram_client() {
    int sockfd;
    struct sockaddr_un server_addr, client_addr;
    char buffer[BUFFER_SIZE];
    ssize_t bytes_sent, bytes_received;
    
    // 创建数据报套接字
    sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(1);
    }
    
    // 设置客户端地址(可选,用于接收响应)
    memset(&client_addr, 0, sizeof(client_addr));
    client_addr.sun_family = AF_UNIX;
    strncpy(client_addr.sun_path, CLIENT_SOCKET_PATH, sizeof(client_addr.sun_path) - 1);
    
    unlink(CLIENT_SOCKET_PATH);
    
    if (bind(sockfd, (struct sockaddr*)&client_addr, sizeof(client_addr)) == -1) {
        perror("bind");
        close(sockfd);
        exit(1);
    }
    
    // 设置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strncpy(server_addr.sun_path, DGRAM_SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
    
    printf("Datagram client started. Type messages (type 'exit' to quit):\n");
    
    while (fgets(buffer, BUFFER_SIZE, stdin) != NULL) {
        // 发送消息
        bytes_sent = sendto(sockfd, buffer, strlen(buffer), 0,
                           (struct sockaddr*)&server_addr, sizeof(server_addr));
        if (bytes_sent == -1) {
            perror("sendto");
            break;
        }
        
        // 接收响应
        bytes_received = recvfrom(sockfd, buffer, BUFFER_SIZE - 1, 0, NULL, NULL);
        if (bytes_received == -1) {
            perror("recvfrom");
            break;
        }
        
        buffer[bytes_received] = '\0';
        printf("Response: %s", buffer);
        
        if (strncmp(buffer, "exit", 4) == 0) {
            break;
        }
    }
    
    close(sockfd);
    unlink(CLIENT_SOCKET_PATH);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s [server|client]\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "server") == 0) {
        unix_dgram_server();
    } else if (strcmp(argv[1], "client") == 0) {
        unix_dgram_client();
    } else {
        printf("Invalid argument. Use 'server' or 'client'\n");
        return 1;
    }
    
    return 0;
}

7. 内存映射(Memory Mapping)

内存映射是一种高效的IPC机制,它允许不同进程通过映射同一文件到各自的地址空间来共享数据。

基本内存映射示例
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>

#define SHARED_FILE "/tmp/shared_memory_file"
#define FILE_SIZE 4096

struct shared_data {
    int counter;
    char message[256];
    int data_ready;
};

void mmap_writer() {
    int fd;
    struct shared_data *shared_mem;
    
    // 创建共享文件
    fd = open(SHARED_FILE, O_RDWR | O_CREAT, 0666);
    if (fd == -1) {
        perror("open");
        exit(1);
    }
    
    // 设置文件大小
    if (ftruncate(fd, FILE_SIZE) == -1) {
        perror("ftruncate");
        close(fd);
        exit(1);
    }
    
    // 内存映射
    shared_mem = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shared_mem == MAP_FAILED) {
        perror("mmap");
        close(fd);
        exit(1);
    }
    
    close(fd);  // 映射后可以关闭文件描述符
    
    // 初始化共享数据
    shared_mem->counter = 0;
    shared_mem->data_ready = 0;
    
    printf("MMAP Writer started. Writing data...\n");
    
    const char *messages[] = {
        "Hello from mmap writer!",
        "This is shared through memory mapping",
        "Memory mapping is efficient for large data",
        "Goodbye!",
        NULL
    };
    
    for (int i = 0; messages[i] != NULL; i++) {
        shared_mem->counter = i + 1;
        strncpy(shared_mem->message, messages[i], 255);
        shared_mem->message[255] = '\0';
        shared_mem->data_ready = 1;
        
        printf("Writer: counter=%d, message=%s\n", 
               shared_mem->counter, shared_mem->message);
        
        // 等待读者读取
        while (shared_mem->data_ready == 1) {
            usleep(100000);  // 100ms
        }
        
        sleep(1);
    }
    
    // 标记结束
    shared_mem->data_ready = -1;
    
    // 取消映射
    if (munmap(shared_mem, FILE_SIZE) == -1) {
        perror("munmap");
    }
    
    // 删除共享文件
    unlink(SHARED_FILE);
    
    printf("Writer finished.\n");
}

void mmap_reader() {
    int fd;
    struct shared_data *shared_mem;
    
    // 打开共享文件
    fd = open(SHARED_FILE, O_RDWR);
    if (fd == -1) {
        perror("open");
        exit(1);
    }
    
    // 内存映射
    shared_mem = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shared_mem == MAP_FAILED) {
        perror("mmap");
        close(fd);
        exit(1);
    }
    
    close(fd);
    
    printf("MMAP Reader started. Waiting for data...\n");
    
    while (1) {
        if (shared_mem->data_ready == -1) {
            break;
        }
        
        if (shared_mem->data_ready == 1) {
            printf("Reader: counter=%d, message=%s\n", 
                   shared_mem->counter, shared_mem->message);
            shared_mem->data_ready = 0;  // 标记为已读
        }
        
        usleep(500000);  // 500ms
    }
    
    // 取消映射
    if (munmap(shared_mem, FILE_SIZE) == -1) {
        perror("munmap");
    }
    
    printf("Reader finished.\n");
}

// 使用匿名内存映射的进程间通信
void anonymous_mmap_example() {
    int *shared_var;
    pid_t pid;
    
    // 创建匿名内存映射
    shared_var = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, 
                     MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (shared_var == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }
    
    *shared_var = 0;
    
    pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(1);
    }
    
    if (pid == 0) {
        // 子进程
        for (int i = 0; i < 5; i++) {
            (*shared_var)++;
            printf("Child: shared_var = %d\n", *shared_var);
            sleep(1);
        }
        exit(0);
    } else {
        // 父进程
        for (int i = 0; i < 5; i++) {
            (*shared_var) += 10;
            printf("Parent: shared_var = %d\n", *shared_var);
            sleep(1);
        }
        wait(NULL);
    }
    
    printf("Final value: %d\n", *shared_var);
    
    // 取消映射
    if (munmap(shared_var, sizeof(int)) == -1) {
        perror("munmap");
    }
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s [writer|reader|anonymous]\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "writer") == 0) {
        mmap_writer();
    } else if (strcmp(argv[1], "reader") == 0) {
        mmap_reader();
    } else if (strcmp(argv[1], "anonymous") == 0) {
        anonymous_mmap_example();
    } else {
        printf("Invalid argument. Use 'writer', 'reader', or 'anonymous'\n");
        return 1;
    }
    
    return 0;
}
高级内存映射特性
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>

#define LARGE_FILE "large_file.bin"
#define FILE_SIZE (100 * 1024 * 1024)  // 100MB

// 创建大文件并演示部分映射
void large_file_mmap() {
    int fd;
    char *mapped_addr;
    struct stat sb;
    
    printf("Creating large file: %s (%d MB)\n", LARGE_FILE, FILE_SIZE / (1024 * 1024));
    
    // 创建文件
    fd = open(LARGE_FILE, O_RDWR | O_CREAT | O_TRUNC, 0666);
    if (fd == -1) {
        perror("open");
        return;
    }
    
    // 设置文件大小
    if (ftruncate(fd, FILE_SIZE) == -1) {
        perror("ftruncate");
        close(fd);
        return;
    }
    
    // 映射整个文件
    mapped_addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (mapped_addr == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return;
    }
    
    printf("File mapped at address: %p\n", mapped_addr);
    
    // 写入数据
    printf("Writing data to mapped memory...\n");
    for (off_t i = 0; i < FILE_SIZE; i += 4096) {
        mapped_addr[i] = (char)(i % 256);
    }
    
    // 同步到磁盘
    if (msync(mapped_addr, FILE_SIZE, MS_SYNC) == -1) {
        perror("msync");
    }
    printf("Data synchronized to disk.\n");
    
    // 取消映射
    if (munmap(mapped_addr, FILE_SIZE) == -1) {
        perror("munmap");
    }
    
    close(fd);
}

// 演示部分文件映射
void partial_mmap() {
    int fd;
    char *mapped_addr;
    const size_t MAP_SIZE = 8192;  // 8KB
    const off_t MAP_OFFSET = 4096; // 从4KB处开始映射
    
    fd = open(LARGE_FILE, O_RDWR);
    if (fd == -1) {
        perror("open");
        return;
    }
    
    // 映射文件的特定部分
    mapped_addr = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, 
                      fd, MAP_OFFSET);
    if (mapped_addr == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return;
    }
    
    printf("Partial mapping: address=%p, offset=%ld, size=%zu\n", 
           mapped_addr, MAP_OFFSET, MAP_SIZE);
    
    // 读取和修改映射的数据
    printf("First 100 bytes of mapped region:\n");
    for (int i = 0; i < 100; i++) {
        printf("%02x ", (unsigned char)mapped_addr[i]);
        if ((i + 1) % 16 == 0) printf("\n");
    }
    printf("\n");
    
    // 修改数据
    strncpy(mapped_addr, "PARTIAL_MMAP_TEST", 17);
    
    // 同步修改
    if (msync(mapped_addr, 17, MS_SYNC) == -1) {
        perror("msync");
    }
    
    // 取消映射
    if (munmap(mapped_addr, MAP_SIZE) == -1) {
        perror("munmap");
    }
    
    close(fd);
}

// 使用madvise优化内存映射性能
void madvise_example() {
    int fd;
    char *mapped_addr;
    const size_t MAP_SIZE = 1024 * 1024;  // 1MB
    
    fd = open(LARGE_FILE, O_RDONLY);
    if (fd == -1) {
        perror("open");
        return;
    }
    
    mapped_addr = mmap(NULL, MAP_SIZE, PROT_READ, MAP_SHARED, fd, 0);
    if (mapped_addr == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return;
    }
    
    // 提供访问模式建议给内核
    if (madvise(mapped_addr, MAP_SIZE, MADV_SEQUENTIAL) == -1) {
        perror("madvise");
    }
    
    printf("Using madvise for sequential access pattern\n");
    
    // 顺序读取数据
    for (size_t i = 0; i < MAP_SIZE; i += 4096) {
        volatile char c = mapped_addr[i];  // 强制读取
        (void)c;  // 避免未使用变量警告
    }
    
    // 取消映射
    if (munmap(mapped_addr, MAP_SIZE) == -1) {
        perror("munmap");
    }
    
    close(fd);
}

int main() {
    printf("=== Large File Memory Mapping ===\n");
    large_file_mmap();
    
    printf("\n=== Partial File Mapping ===\n");
    partial_mmap();
    
    printf("\n=== MADVISE Optimization ===\n");
    madvise_example();
    
    // 清理
    unlink(LARGE_FILE);
    
    return 0;
}

8. 各种IPC方式对比

IPC机制性能比较

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>

#define TEST_DATA_SIZE 1024
#define NUM_ITERATIONS 10000

// 时间测量函数
double get_time() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + tv.tv_usec / 1000000.0;
}

// 管道性能测试
void test_pipe_performance() {
    int pipefd[2];
    pid_t pid;
    char buffer[TEST_DATA_SIZE];
    double start_time, end_time;
    
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return;
    }
    
    pid = fork();
    if (pid == -1) {
        perror("fork");
        return;
    }
    
    if (pid == 0) {
        // 子进程 - 读取端
        close(pipefd[1]);
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            read(pipefd[0], buffer, TEST_DATA_SIZE);
        }
        close(pipefd[0]);
        exit(0);
    } else {
        // 父进程 - 写入端
        close(pipefd[0]);
        memset(buffer, 'A', TEST_DATA_SIZE);
        
        start_time = get_time();
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            write(pipefd[1], buffer, TEST_DATA_SIZE);
        }
        end_time = get_time();
        
        close(pipefd[1]);
        wait(NULL);
        
        printf("Pipe: %.6f seconds (%.0f operations/sec)\n", 
               end_time - start_time, NUM_ITERATIONS / (end_time - start_time));
    }
}

// System V消息队列性能测试
void test_sysv_msgq_performance() {
    key_t key = ftok("/tmp", 'P');
    int msgid;
    pid_t pid;
    struct {
        long mtype;
        char mtext[TEST_DATA_SIZE];
    } message;
    
    msgid = msgget(key, 0666 | IPC_CREAT);
    if (msgid == -1) {
        perror("msgget");
        return;
    }
    
    pid = fork();
    if (pid == -1) {
        perror("fork");
        return;
    }
    
    if (pid == 0) {
        // 子进程 - 接收端
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            msgrcv(msgid, &message, TEST_DATA_SIZE, 1, 0);
        }
        exit(0);
    } else {
        // 父进程 - 发送端
        message.mtype = 1;
        memset(message.mtext, 'B', TEST_DATA_SIZE);
        
        double start_time = get_time();
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            msgsnd(msgid, &message, TEST_DATA_SIZE, 0);
        }
        double end_time = get_time();
        
        wait(NULL);
        
        // 清理
        msgctl(msgid, IPC_RMID, NULL);
        
        printf("System V Message Queue: %.6f seconds (%.0f operations/sec)\n", 
               end_time - start_time, NUM_ITERATIONS / (end_time - start_time));
    }
}

// 共享内存性能测试
void test_shm_performance() {
    key_t key = ftok("/tmp", 'S');
    int shmid;
    pid_t pid;
    char *shm_ptr;
    struct sembuf sb;
    int semid;
    
    // 创建共享内存
    shmid = shmget(key, TEST_DATA_SIZE, 0666 | IPC_CREAT);
    if (shmid == -1) {
        perror("shmget");
        return;
    }
    
    // 创建信号量用于同步
    semid = semget(key, 1, 0666 | IPC_CREAT);
    if (semid == -1) {
        perror("semget");
        return;
    }
    
    // 初始化信号量
    semctl(semid, 0, SETVAL, 1);
    
    shm_ptr = shmat(shmid, NULL, 0);
    if (shm_ptr == (char *)-1) {
        perror("shmat");
        return;
    }
    
    pid = fork();
    if (pid == -1) {
        perror("fork");
        return;
    }
    
    if (pid == 0) {
        // 子进程 - 读取端
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            sb.sem_num = 0;
            sb.sem_op = -1;
            sb.sem_flg = 0;
            semop(semid, &sb, 1);
            
            // 读取数据
            volatile char c = shm_ptr[0];
            (void)c;
            
            sb.sem_op = 1;
            semop(semid, &sb, 1);
        }
        exit(0);
    } else {
        // 父进程 - 写入端
        memset(shm_ptr, 'C', TEST_DATA_SIZE);
        
        double start_time = get_time();
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            sb.sem_num = 0;
            sb.sem_op = -1;
            sb.sem_flg = 0;
            semop(semid, &sb, 1);
            
            // 写入数据
            shm_ptr[0] = 'D';
            
            sb.sem_op = 1;
            semop(semid, &sb, 1);
        }
        double end_time = get_time();
        
        wait(NULL);
        
        // 清理
        shmdt(shm_ptr);
        shmctl(shmid, IPC_RMID, NULL);
        semctl(semid, 0, IPC_RMID);
        
        printf("Shared Memory: %.6f seconds (%.0f operations/sec)\n", 
               end_time - start_time, NUM_ITERATIONS / (end_time - start_time));
    }
}

// Unix域套接字性能测试
void test_unix_socket_performance() {
    int sockfd[2];
    pid_t pid;
    char buffer[TEST_DATA_SIZE];
    
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) == -1) {
        perror("socketpair");
        return;
    }
    
    pid = fork();
    if (pid == -1) {
        perror("fork");
        return;
    }
    
    if (pid == 0) {
        // 子进程
        close(sockfd[0]);
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            read(sockfd[1], buffer, TEST_DATA_SIZE);
            write(sockfd[1], buffer, TEST_DATA_SIZE);
        }
        close(sockfd[1]);
        exit(0);
    } else {
        // 父进程
        close(sockfd[1]);
        memset(buffer, 'E', TEST_DATA_SIZE);
        
        double start_time = get_time();
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            write(sockfd[0], buffer, TEST_DATA_SIZE);
            read(sockfd[0], buffer, TEST_DATA_SIZE);
        }
        double end_time = get_time();
        
        close(sockfd[0]);
        wait(NULL);
        
        printf("Unix Domain Socket: %.6f seconds (%.0f operations/sec)\n", 
               end_time - start_time, NUM_ITERATIONS / (end_time - start_time));
    }
}

int main() {
    printf("IPC Mechanism Performance Comparison\n");
    printf("Data size: %d bytes, Iterations: %d\n\n", TEST_DATA_SIZE, NUM_ITERATIONS);
    
    test_pipe_performance();
    test_sysv_msgq_performance();
    test_shm_performance();
    test_unix_socket_performance();
    
    return 0;
}

IPC特性对比表

特性 管道 消息队列 共享内存 信号量 信号 套接字
通信方向 单向 双向 双向 同步 异步 双向
通信模式 流/消息 消息 字节流 同步原语 事件 流/消息
速度 中等 中等 中等
容量 有限 有限 N/A
持久性 进程 内核 进程 内核 进程 进程
访问控制
复杂性 中等 中等 中等

9. 如何选择合适的IPC方式

选择合适的IPC机制需要考虑多个因素:

选择指南

c 复制代码
#include <stdio.h>
#include <string.h>

// IPC选择决策函数
void recommend_ipc(const char *scenario, int need_high_throughput, 
                   int need_low_latency, int need_synchronization,
                   int need_persistence, int need_broadcast,
                   int processes_related, int data_size_large) {
    
    printf("Scenario: %s\n", scenario);
    printf("Requirements: ");
    
    if (need_high_throughput) printf("High-Throughput ");
    if (need_low_latency) printf("Low-Latency ");
    if (need_synchronization) printf("Synchronization ");
    if (need_persistence) printf("Persistence ");
    if (need_broadcast) printf("Broadcast ");
    if (processes_related) printf("Related-Processes ");
    if (data_size_large) printf("Large-Data ");
    printf("\n");
    
    printf("Recommended IPC: ");
    
    if (need_high_throughput && data_size_large) {
        printf("Shared Memory (with proper synchronization)\n");
    } else if (need_low_latency && processes_related) {
        printf("Unix Domain Sockets or Pipes\n");
    } else if (need_synchronization) {
        printf("Semaphores or Mutexes with Shared Memory\n");
    } else if (need_persistence) {
        printf("Message Queues or Memory Mapped Files\n");
    } else if (need_broadcast) {
        printf("Signals or Message Queues with multiple readers\n");
    } else if (processes_related) {
        printf("Pipes or Unix Domain Sockets\n");
    } else {
        printf("Message Queues or Network Sockets\n");
    }
    
    printf("\n");
}

int main() {
    printf("IPC Mechanism Selection Guide\n\n");
    
    recommend_ipc("High-performance data processing", 1, 1, 1, 0, 0, 1, 1);
    recommend_ipc("Inter-process synchronization", 0, 1, 1, 0, 0, 1, 0);
    recommend_ipc("Persistent message storage", 0, 0, 0, 1, 0, 0, 0);
    recommend_ipc("Broadcast notifications", 0, 1, 0, 0, 1, 0, 0);
    recommend_ipc("Simple parent-child communication", 0, 0, 0, 0, 0, 1, 0);
    recommend_ipc("Unrelated process communication", 0, 0, 0, 0, 0, 0, 0);
    
    return 0;
}

10. 实战建议

最佳实践示例

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <errno.h>
#include <signal.h>

#define SHM_KEY 0x12345
#define SEM_KEY 0x54321
#define SHM_SIZE 4096

// 错误处理宏
#define CHECK(expr, msg) \
    do { \
        if ((expr) == -1) { \
            perror(msg); \
            exit(1); \
        } \
    } while(0)

// 资源清理函数
void cleanup_resources(int shmid, int semid) {
    if (shmid != -1) {
        shmctl(shmid, IPC_RMID, NULL);
    }
    if (semid != -1) {
        semctl(semid, 0, IPC_RMID);
    }
}

// 信号处理用于优雅退出
volatile sig_atomic_t graceful_shutdown = 0;

void shutdown_handler(int sig) {
    graceful_shutdown = 1;
    printf("\nShutdown signal received. Cleaning up...\n");
}

// 安全的共享内存访问
struct safe_shm {
    int shmid;
    int semid;
    char *data;
};

int init_safe_shm(struct safe_shm *shm, size_t size) {
    // 创建共享内存
    shm->shmid = shmget(SHM_KEY, size, 0666 | IPC_CREAT | IPC_EXCL);
    if (shm->shmid == -1) {
        if (errno == EEXIST) {
            // 已经存在,直接获取
            shm->shmid = shmget(SHM_KEY, size, 0666);
            CHECK(shm->shmid, "shmget existing");
        } else {
            perror("shmget new");
            return -1;
        }
    }
    
    // 附加共享内存
    shm->data = shmat(shm->shmid, NULL, 0);
    CHECK(shm->data == (char*)-1 ? -1 : 0, "shmat");
    
    // 创建信号量
    shm->semid = semget(SEM_KEY, 1, 0666 | IPC_CREAT);
    CHECK(shm->semid, "semget");
    
    // 初始化信号量
    union semun arg;
    arg.val = 1;
    CHECK(semctl(shm->semid, 0, SETVAL, arg), "semctl SETVAL");
    
    return 0;
}

void lock_shm(struct safe_shm *shm) {
    struct sembuf sb = {0, -1, 0};
    CHECK(semop(shm->semid, &sb, 1), "semop lock");
}

void unlock_shm(struct safe_shm *shm) {
    struct sembuf sb = {0, 1, 0};
    CHECK(semop(shm->semid, &sb, 1), "semop unlock");
}

void destroy_safe_shm(struct safe_shm *shm) {
    if (shm->data != NULL && shm->data != (char*)-1) {
        shmdt(shm->data);
    }
    cleanup_resources(shm->shmid, shm->semid);
}

// 生产者-消费者模式实现
struct ring_buffer {
    int head;
    int tail;
    int count;
    int capacity;
    char data[0];  // 柔性数组
};

void producer_worker(struct safe_shm *shm) {
    struct ring_buffer *rb = (struct ring_buffer*)shm->data;
    int message_id = 0;
    
    while (!graceful_shutdown) {
        lock_shm(shm);
        
        // 检查缓冲区是否满
        if (rb->count < rb->capacity) {
            // 生产数据
            int len = snprintf(rb->data + rb->head * 256, 256, 
                             "Message %d from producer", message_id++);
            
            rb->head = (rb->head + 1) % rb->capacity;
            rb->count++;
            
            printf("Produced: %s (count=%d)\n", rb->data + (rb->head * 256) % (rb->capacity * 256), rb->count);
        }
        
        unlock_shm(shm);
        usleep(100000);  // 100ms
    }
}

void consumer_worker(struct safe_shm *shm) {
    struct ring_buffer *rb = (struct ring_buffer*)shm->data;
    
    while (!graceful_shutdown) {
        lock_shm(shm);
        
        // 检查缓冲区是否有数据
        if (rb->count > 0) {
            // 消费数据
            printf("Consumed: %s (count=%d)\n", rb->data + rb->tail * 256, rb->count);
            
            rb->tail = (rb->tail + 1) % rb->capacity;
            rb->count--;
        }
        
        unlock_shm(shm);
        usleep(150000);  // 150ms
    }
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s [producer|consumer]\n", argv[0]);
        return 1;
    }
    
    // 设置信号处理
    struct sigaction sa;
    sa.sa_handler = shutdown_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);
    
    struct safe_shm shm = {-1, -1, NULL};
    size_t shm_size = sizeof(struct ring_buffer) + 10 * 256;  // 10个256字节的消息
    
    if (init_safe_shm(&shm, shm_size) == -1) {
        return 1;
    }
    
    // 初始化环形缓冲区
    struct ring_buffer *rb = (struct ring_buffer*)shm.data;
    if (shm.shmid != -1) {  // 只有创建者初始化
        rb->head = 0;
        rb->tail = 0;
        rb->count = 0;
        rb->capacity = 10;
    }
    
    printf("%s started (PID: %d)\n", argv[1], getpid());
    printf("Press Ctrl+C to exit gracefully\n");
    
    if (strcmp(argv[1], "producer") == 0) {
        producer_worker(&shm);
    } else if (strcmp(argv[1], "consumer") == 0) {
        consumer_worker(&shm);
    } else {
        printf("Invalid role. Use 'producer' or 'consumer'\n");
        destroy_safe_shm(&shm);
        return 1;
    }
    
    printf("Clean shutdown completed.\n");
    destroy_safe_shm(&shm);
    return 0;
}

11. 常见问题与解决方案

11.1 管道阻塞问题

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

// 设置文件描述符为非阻塞模式
int set_nonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) {
        perror("fcntl F_GETFL");
        return -1;
    }
    
    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
        perror("fcntl F_SETFL");
        return -1;
    }
    
    return 0;
}

// 处理SIGPIPE信号,避免进程终止
void handle_sigpipe(int sig) {
    printf("SIGPIPE received: Broken pipe. Continuing...\n");
}

// 安全的管道写入函数
ssize_t safe_pipe_write(int fd, const void *buf, size_t count) {
    ssize_t bytes_written;
    size_t total_written = 0;
    const char *ptr = buf;
    
    while (total_written < count) {
        bytes_written = write(fd, ptr + total_written, count - total_written);
        
        if (bytes_written == -1) {
            if (errno == EINTR) {
                continue;  // 被信号中断,重试
            } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
                // 管道缓冲区满,等待
                usleep(10000);  // 10ms
                continue;
            } else {
                perror("write");
                return -1;
            }
        }
        
        total_written += bytes_written;
    }
    
    return total_written;
}

// 演示管道阻塞问题的解决方案
void pipe_blocking_demo() {
    int pipefd[2];
    pid_t pid;
    char buffer[1024];
    
    // 设置SIGPIPE处理
    signal(SIGPIPE, handle_sigpipe);
    
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return;
    }
    
    // 设置写端为非阻塞
    if (set_nonblocking(pipefd[1]) == -1) {
        close(pipefd[0]);
        close(pipefd[1]);
        return;
    }
    
    pid = fork();
    if (pid == -1) {
        perror("fork");
        close(pipefd[0]);
        close(pipefd[1]);
        return;
    }
    
    if (pid == 0) {
        // 子进程 - 读取端
        close(pipefd[1]);
        
        // 缓慢读取,制造缓冲区满的情况
        while (1) {
            ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer));
            if (bytes_read == -1) {
                if (errno == EAGAIN) {
                    usleep(100000);  // 100ms
                    continue;
                }
                perror("read");
                break;
            } else if (bytes_read == 0) {
                printf("Child: pipe closed\n");
                break;
            } else {
                printf("Child: read %zd bytes\n", bytes_read);
                sleep(1);  // 故意慢读
            }
        }
        
        close(pipefd[0]);
        exit(0);
    } else {
        // 父进程 - 写入端
        close(pipefd[0]);
        
        memset(buffer, 'X', sizeof(buffer));
        
        for (int i = 0; i < 10; i++) {
            ssize_t bytes_written = safe_pipe_write(pipefd[1], buffer, sizeof(buffer));
            if (bytes_written == -1) {
                printf("Parent: write failed\n");
                break;
            } else {
                printf("Parent: wrote %zd bytes (iteration %d)\n", bytes_written, i + 1);
            }
        }
        
        close(pipefd[1]);
        wait(NULL);
    }
}

int main() {
    printf("Pipe Blocking Problem Demonstration\n");
    pipe_blocking_demo();
    return 0;
}

11.2 消息队列持久化

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

#define MSG_KEY 0x7777
#define MAX_MSG_SIZE 1024
#define PERSISTENT_FILE "/tmp/msgq_persistent"

struct persistent_message {
    long mtype;
    char mtext[MAX_MSG_SIZE];
    time_t timestamp;
    int sequence;
};

// 持久化消息队列状态
void persist_msgq_state(int msgid) {
    FILE *fp = fopen(PERSISTENT_FILE, "w");
    if (fp == NULL) {
        perror("fopen");
        return;
    }
    
    struct msqid_ds buf;
    if (msgctl(msgid, IPC_STAT, &buf) == -1) {
        perror("msgctl");
        fclose(fp);
        return;
    }
    
    fprintf(fp, "msgid: %d\n", msgid);
    fprintf(fp, "messages: %ld\n", buf.msg_qnum);
    fprintf(fp, "last_send: %ld\n", buf.msg_stime);
    fprintf(fp, "last_receive: %ld\n", buf.msg_rtime);
    fprintf(fp, "last_change: %ld\n", buf.msg_ctime);
    
    fclose(fp);
    printf("Message queue state persisted to %s\n", PERSISTENT_FILE);
}

// 恢复消息队列状态
void restore_msgq_state() {
    FILE *fp = fopen(PERSISTENT_FILE, "r");
    if (fp == NULL) {
        if (errno != ENOENT) {
            perror("fopen");
        }
        return;
    }
    
    char line[256];
    while (fgets(line, sizeof(line), fp)) {
        printf("Persisted: %s", line);
    }
    
    fclose(fp);
}

// 优雅关闭处理
volatile sig_atomic_t shutdown_requested = 0;

void graceful_shutdown(int sig) {
    shutdown_requested = 1;
    printf("\nShutdown requested. Finishing current work...\n");
}

// 持久化消息队列服务器
void persistent_msgq_server() {
    int msgid;
    struct persistent_message msg;
    int sequence = 0;
    
    // 设置信号处理
    signal(SIGINT, graceful_shutdown);
    signal(SIGTERM, graceful_shutdown);
    
    // 恢复之前的状态
    restore_msgq_state();
    
    // 创建消息队列
    msgid = msgget(MSG_KEY, 0666 | IPC_CREAT);
    if (msgid == -1) {
        perror("msgget");
        return;
    }
    
    printf("Persistent message queue server started (msgid: %d)\n", msgid);
    
    while (!shutdown_requested) {
        ssize_t bytes = msgrcv(msgid, &msg, sizeof(msg) - sizeof(long), 0, IPC_NOWAIT);
        
        if (bytes == -1) {
            if (errno == ENOMSG) {
                // 没有消息,继续等待
                sleep(1);
                continue;
            } else {
                perror("msgrcv");
                break;
            }
        }
        
        printf("Received: type=%ld, seq=%d, time=%ld, message=%s\n",
               msg.mtype, msg.sequence, msg.timestamp, msg.mtext);
        
        sequence++;
        
        // 定期持久化状态
        if (sequence % 5 == 0) {
            persist_msgq_state(msgid);
        }
    }
    
    // 最终持久化
    persist_msgq_state(msgid);
    
    printf("Server shutdown complete.\n");
}

// 持久化消息队列客户端
void persistent_msgq_client() {
    int msgid = msgget(MSG_KEY, 0666);
    if (msgid == -1) {
        perror("msgget");
        return;
    }
    
    struct persistent_message msg;
    int sequence = 0;
    
    printf("Persistent message queue client started\n");
    
    for (int i = 0; i < 10; i++) {
        msg.mtype = 1;
        msg.timestamp = time(NULL);
        msg.sequence = sequence++;
        snprintf(msg.mtext, MAX_MSG_SIZE, "Persistent message %d", i + 1);
        
        if (msgsnd(msgid, &msg, sizeof(msg) - sizeof(long), IPC_NOWAIT) == -1) {
            if (errno == EAGAIN) {
                printf("Message queue full, retrying...\n");
                sleep(1);
                i--;  // 重试
                continue;
            } else {
                perror("msgsnd");
                break;
            }
        }
        
        printf("Sent: %s\n", msg.mtext);
        sleep(2);
    }
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s [server|client]\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "server") == 0) {
        persistent_msgq_server();
    } else if (strcmp(argv[1], "client") == 0) {
        persistent_msgq_client();
    } else {
        printf("Invalid argument. Use 'server' or 'client'\n");
        return 1;
    }
    
    return 0;
}

11.3 共享内存同步

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <pthread.h>
#include <errno.h>

#define SHM_KEY 0x8888
#define SEM_KEY 0x9999
#define SHM_SIZE 4096

// 高级同步结构
struct synchronized_shm {
    int shmid;
    int semid;
    pthread_mutex_t *mutex;
    pthread_cond_t *condition;
    char *data;
    int data_ready;
};

// 初始化POSIX同步原语在共享内存中
int init_shared_sync(struct synchronized_shm *shm) {
    // 创建共享内存
    shm->shmid = shmget(SHM_KEY, SHM_SIZE, 0666 | IPC_CREAT);
    if (shm->shmid == -1) {
        perror("shmget");
        return -1;
    }
    
    // 附加共享内存
    shm->data = shmat(shm->shmid, NULL, 0);
    if (shm->data == (char*)-1) {
        perror("shmat");
        return -1;
    }
    
    // 在共享内存中设置互斥锁和条件变量
    shm->mutex = (pthread_mutex_t*)shm->data;
    shm->condition = (pthread_cond_t*)(shm->data + sizeof(pthread_mutex_t));
    char *user_data = shm->data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t);
    int *data_ready_ptr = (int*)user_data;
    shm->data_ready = *data_ready_ptr;
    
    // 只在第一次初始化同步原语
    static int initialized = 0;
    if (!initialized) {
        pthread_mutexattr_t mutex_attr;
        pthread_condattr_t cond_attr;
        
        // 初始化互斥锁属性(进程间共享)
        pthread_mutexattr_init(&mutex_attr);
        pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
        pthread_mutex_init(shm->mutex, &mutex_attr);
        pthread_mutexattr_destroy(&mutex_attr);
        
        // 初始化条件变量属性(进程间共享)
        pthread_condattr_init(&cond_attr);
        pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
        pthread_cond_init(shm->condition, &cond_attr);
        pthread_condattr_destroy(&cond_attr);
        
        *data_ready_ptr = 0;
        initialized = 1;
    }
    
    return 0;
}

// 生产者进程
void synchronized_producer() {
    struct synchronized_shm shm;
    
    if (init_shared_sync(&shm) == -1) {
        return;
    }
    
    printf("Synchronized producer started\n");
    
    char *user_data = shm.data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t) + sizeof(int);
    int *data_ready = (int*)(shm.data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t));
    
    for (int i = 0; i < 5; i++) {
        pthread_mutex_lock(shm.mutex);
        
        // 等待消费者读取前一个消息
        while (*data_ready == 1) {
            pthread_cond_wait(shm.condition, shm.mutex);
        }
        
        // 生产数据
        snprintf(user_data, SHM_SIZE - (user_data - shm.data), 
                "Message %d from synchronized producer", i + 1);
        *data_ready = 1;
        
        printf("Producer: %s\n", user_data);
        
        // 通知消费者
        pthread_cond_signal(shm.condition);
        pthread_mutex_unlock(shm.mutex);
        
        sleep(2);
    }
    
    // 标记结束
    pthread_mutex_lock(shm.mutex);
    snprintf(user_data, SHM_SIZE - (user_data - shm.data), "END");
    *data_ready = 1;
    pthread_cond_signal(shm.condition);
    pthread_mutex_unlock(shm.mutex);
    
    shmdt(shm.data);
    printf("Producer finished\n");
}

// 消费者进程
void synchronized_consumer() {
    struct synchronized_shm shm;
    
    if (init_shared_sync(&shm) == -1) {
        return;
    }
    
    printf("Synchronized consumer started\n");
    
    char *user_data = shm.data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t) + sizeof(int);
    int *data_ready = (int*)(shm.data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t));
    
    while (1) {
        pthread_mutex_lock(shm.mutex);
        
        // 等待生产者提供数据
        while (*data_ready == 0) {
            pthread_cond_wait(shm.condition, shm.mutex);
        }
        
        // 消费数据
        printf("Consumer: %s\n", user_data);
        
        if (strcmp(user_data, "END") == 0) {
            *data_ready = 0;
            pthread_mutex_unlock(shm.mutex);
            break;
        }
        
        *data_ready = 0;
        
        // 通知生产者
        pthread_cond_signal(shm.condition);
        pthread_mutex_unlock(shm.mutex);
        
        sleep(1);
    }
    
    shmdt(shm.data);
    
    // 清理共享内存
    shmctl(shm.shmid, IPC_RMID, NULL);
    printf("Consumer finished\n");
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s [producer|consumer]\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "producer") == 0) {
        synchronized_producer();
    } else if (strcmp(argv[1], "consumer") == 0) {
        synchronized_consumer();
    } else {
        printf("Invalid argument. Use 'producer' or 'consumer'\n");
        return 1;
    }
    
    return 0;
}

11.4 信号处理的可重入性

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>

// 可重入的全局变量(使用sig_atomic_t)
volatile sig_atomic_t signal_count = 0;
volatile sig_atomic_t main_loop_active = 1;

// 线程安全的字符串操作(可重入)
void safe_strcpy(char *dest, const char *src, size_t size) {
    if (size > 0) {
        while (--size > 0 && (*dest++ = *src++));
        *dest = '\0';
    }
}

// 可重入的信号处理函数
void reentrant_signal_handler(int sig) {
    // 只修改sig_atomic_t变量,这些操作是原子的
    signal_count++;
    
    // 使用可重入的字符串函数
    char buffer[100];
    const char *msg = "Signal received: ";
    safe_strcpy(buffer, msg, sizeof(buffer));
    
    // 使用可重入的I/O函数
    const char *signal_name;
    switch (sig) {
        case SIGUSR1: signal_name = "SIGUSR1"; break;
        case SIGUSR2: signal_name = "SIGUSR2"; break;
        case SIGTERM: signal_name = "SIGTERM"; break;
        default: signal_name = "UNKNOWN"; break;
    }
    
    // 使用write而不是printf(printf不可重入)
    write(STDOUT_FILENO, buffer, strlen(buffer));
    write(STDOUT_FILENO, signal_name, strlen(signal_name));
    write(STDOUT_FILENO, "\n", 1);
    
    if (sig == SIGTERM) {
        main_loop_active = 0;
    }
}

// 非可重入的危险示例(不要在实际中使用)
char global_buffer[100];  // 全局变量,在信号处理中访问危险

void dangerous_signal_handler(int sig) {
    // 危险:使用非可重入函数
    sprintf(global_buffer, "Dangerous: Signal %d received", sig);
    printf("%s\n", global_buffer);  // printf不可重入!
}

// 设置可重入信号处理
void setup_reentrant_signals() {
    struct sigaction sa;
    
    sa.sa_handler = reentrant_signal_handler;
    sigemptyset(&sa.sa_mask);
    
    // 在处理信号时阻塞其他信号
    sigaddset(&sa.sa_mask, SIGUSR1);
    sigaddset(&sa.sa_mask, SIGUSR2);
    sigaddset(&sa.sa_mask, SIGTERM);
    
    sa.sa_flags = 0;  // 不设置SA_RESTART,让系统调用可中断
    
    sigaction(SIGUSR1, &sa, NULL);
    sigaction(SIGUSR2, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);
}

// 信号安全的日志函数
void signal_safe_log(const char *message) {
    // 使用可重入的I/O操作
    size_t len = 0;
    const char *p = message;
    while (*p++) len++;
    
    write(STDERR_FILENO, message, len);
    write(STDERR_FILENO, "\n", 1);
}

// 演示可重入信号处理的程序
void reentrant_signal_demo() {
    setup_reentrant_signals();
    
    printf("Reentrant signal demo started (PID: %d)\n", getpid());
    printf("Send signals: kill -USR1 %d, kill -USR2 %d, kill -TERM %d\n", 
           getpid(), getpid(), getpid());
    
    int counter = 0;
    while (main_loop_active) {
        // 主工作循环
        counter++;
        
        // 模拟工作
        for (int i = 0; i < 1000000; i++) {
            // 一些计算工作
            volatile int x = i * i;
            (void)x;
        }
        
        if (counter % 100 == 0) {
            printf("Main loop iteration %d, signals received: %d\n", 
                   counter, (int)signal_count);
        }
        
        // 检查是否应该退出
        if (!main_loop_active) {
            signal_safe_log("Shutting down due to SIGTERM");
            break;
        }
    }
    
    printf("Demo finished. Total signals received: %d\n", (int)signal_count);
}

int main() {
    printf("Signal Reentrancy Demonstration\n\n");
    
    reentrant_signal_demo();
    
    return 0;
}

12. 结语

Linux进程间通信机制为开发者提供了丰富多样的工具集,从简单的管道到复杂的共享内存,每种机制都有其特定的应用场景和优势。通过本文的详细分析和代码示例,我们可以看到:

  1. 管道和FIFO 适合简单的流式数据传递,特别是在有亲缘关系的进程间
  2. 消息队列 提供了结构化的消息传递,支持优先级和持久化
  3. 共享内存 在需要高性能数据共享的场景中无可替代,但需要仔细处理同步问题
  4. 信号量 是进程同步的核心机制,确保对共享资源的互斥访问
  5. 信号 提供了异步事件通知机制,但需要注意可重入性问题
  6. 套接字 不仅支持网络通信,Unix域套接字在本地进程间通信中也很高效
  7. 内存映射 结合了文件I/O和内存操作的优势,适合大文件处理

在实际项目中,选择哪种IPC机制应该基于具体需求:数据量大小、性能要求、进程关系、持久化需求等。通常,一个复杂的系统会组合使用多种IPC机制,每种机制负责最适合的任务。

掌握这些IPC机制不仅有助于编写高效的Linux应用程序,还能帮助开发者更好地理解操作系统的工作原理。随着容器化和微服务架构的普及,进程间通信的重要性更加凸显,这些基础知识将成为每个系统开发者必备的技能。

希望本文提供的理论分析和实践示例能够帮助读者深入理解Linux IPC机制,并在实际项目中做出明智的技术选择。

相关推荐
你好~每一天1 小时前
未来3年,最值得拿下的5个AI证书!
数据结构·人工智能·算法·sqlite·hbase·散列表·模拟退火算法
杰克尼1 小时前
3. 分巧克力
java·数据结构·算法
Protein_zmm1 小时前
Wireshark实验一:Web 浏览器与服务器的协议报文捕获与分析
服务器·测试工具·wireshark
lijiatu100861 小时前
C++ 类成员变量声明语法错误
java·开发语言·c++
zore_c1 小时前
【C语言】带你层层深入指针——指针详解2
c语言·开发语言·c++·经验分享·笔记
cookies_s_s1 小时前
项目--协程库(C++)前置知识篇
linux·服务器·c++
不过普通话一乙不改名1 小时前
Linux 网络发包的极致之路:从普通模式到 AF_XDP ZeroCopy
linux·运维·网络
zmzb01032 小时前
C++课后习题训练记录Day39
数据结构·c++·算法
jquerybootstrap2 小时前
大地2000转经纬度坐标
linux·开发语言·python