进程间通信:Linux IPC 精要

0. 全息通讯方式概览

通信方式描述优势缺点典型场景
管道(Pipes)单向通信的机制,通常用于父子进程间通信。数据流从一个进程流向另一个进程。- 简单易用
- 基于文件描述符
- 单向通信,不适合双向通信
- 只能在具有共同祖先的进程间使用
进程链中相邻两个进程之间的通信
消息队列(Message Queues)进程通过消息队列传递数据,可以实现多对多通信。消息被放入队列并按照一定顺序被接收。- 异步通信
- 队列缓存多个消息,避免数据丢失
- 适用于多对多通信
- 有消息队列大小限制
- 冗余的消息复制
进程间需要异步通信的情景
信号(Signals)通过向进程发送信号来通知或请求处理。每个信号都有一个特定的含义。- 轻量级,开销小- 信号不包含数据信息,只是通知
- 信号的处理不保证顺序
进程需要接收通知,如中断处理
共享内存(Shared Memory)进程间共享同一块物理内存区域,进程可以直接读写对方的数据。需要使用信号量或互斥锁进行同步。- 高效,数据直接共享
- 适用于频繁的大量数据交换
- 需要手动处理同步和互斥
- 内存泄漏和越界访问的风险
数据交换较频繁,需要高性能的场景
套接字(Sockets)基于网络协议的通信方式,可以用于本地或网络通信。提供TCP和UDP两种套接字类型。- 可用于本地和网络通信
- 支持多种协议
- 相对较慢,适用于大数据量或可靠性要求高的通信
- 需要处理网络协议
网络通信,如客户端与服务器之间的通信
文件映射(Memory-mapped Files)将文件映射到进程的地址空间,多个进程可以直接读写这个文件。一种高效的共享数据的方式。- 高效,减少数据拷贝
- 数据直接映射到内存
- 需要文件系统的支持
- 内存映射区域需要手动同步
需要大规模数据共享的场景

1.管道

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

int main() {
    // 创建管道
    int pipe_fd[2];   
    if (pipe(pipe_fd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    // 创建子进程
    pid_t child_pid = fork();

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

    if (child_pid == 0) {
        // 子进程
        close(pipe_fd[1]);  // 关闭写端

        char buffer[256];
        ssize_t read_bytes = read(pipe_fd[0], buffer, sizeof(buffer));
        if (read_bytes == -1) {
            perror("read");
            exit(EXIT_FAILURE);
        }

        printf("子进程收到消息: %s\n", buffer);

        close(pipe_fd[0]);  // 关闭读端

    } else {
        // 父进程
        close(pipe_fd[0]);  // 关闭读端

        const char *message = "Hello, child process!";
        ssize_t write_bytes = write(pipe_fd[1], message, strlen(message) + 1);
        if (write_bytes == -1) {
            perror("write");
            exit(EXIT_FAILURE);
        }

        printf("父进程发送消息: %s\n", message);

        close(pipe_fd[1]);  // 关闭写端
    }

    return 0;
}

2. 消息队列

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

#define MSG_SIZE 256
#define MSG_TYPE 1

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

void send_message(int msg_id, char *message) {
    struct msg_buffer msg_snd;
    msg_snd.msg_type = MSG_TYPE;
    strncpy(msg_snd.msg_text, message, sizeof(msg_snd.msg_text));

    if (msgsnd(msg_id, &msg_snd, sizeof(msg_snd.msg_text), 0) == -1) {
        perror("msgsnd");
        exit(EXIT_FAILURE);
    }
}

void receive_messages(int msg_id) {
    struct msg_buffer msg_rcv;

    while (1) {
        if (msgrcv(msg_id, &msg_rcv, sizeof(msg_rcv.msg_text), MSG_TYPE, 0) == -1) {
            perror("msgrcv");
            exit(EXIT_FAILURE);
        }

        printf("进程 %d 收到消息: %s\n", getpid(), msg_rcv.msg_text);
    }
}

int main() {
    key_t key;
    int msg_id;

    // 生成唯一的键值
    if ((key = ftok("msg_queue_example", 'B')) == -1) {
        perror("ftok");
        exit(EXIT_FAILURE);
    }

    // 创建消息队列
    if ((msg_id = msgget(key, 0666 | IPC_CREAT)) == -1) {
        perror("msgget");
        exit(EXIT_FAILURE);
    }

    // 创建多个进程
    pid_t child_pids[3];

    for (int i = 0; i < 3; ++i) {
        pid_t child_pid = fork();

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

        if (child_pid == 0) {
            // 子进程
            receive_messages(msg_id);
            exit(EXIT_SUCCESS);
        } else {
            // 父进程
            child_pids[i] = child_pid;
        }
    }

    // 父进程发送消息
    for (int i = 0; i < 3; ++i) {
        char message[MSG_SIZE];
        printf("请输入要发送的消息给进程 %d: ", child_pids[i]);
        fgets(message, MSG_SIZE, stdin);
        send_message(msg_id, message);
    }

    // 父进程等待子进程结束
    for (int i = 0; i < 3; ++i) {
        waitpid(child_pids[i], NULL, 0);
    }

    // 删除消息队列
    if (msgctl(msg_id, IPC_RMID, NULL) == -1) {
        perror("msgctl");
        exit(EXIT_FAILURE);
    }

    return 0;
}

3.信号

common.h 文件

// common.h
#ifndef COMMON_H 
#define COMMON_H

#define SIGNAL_NAME SIGUSR1

#endif // COMMON_H

sender.c 文件

// sender.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>   
#include <unistd.h>
#include "common.h"

void signal_handler(int signum) {
    if (signum == SIGNAL_NAME) {
        printf("Sender: Signal received by sender!\n");
    }
}

int main() {
    signal(SIGNAL_NAME, signal_handler);

    printf("Sender: Sending signal to receiver...\n");
    pid_t receiver_pid = /* Replace with the actual PID of the receiver process */;
    if (kill(receiver_pid, SIGNAL_NAME) == -1) {
        perror("Sender: Error sending signal");
        exit(EXIT_FAILURE);
    }

    return 0;
}

receiver.c 文件

// receiver.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include "common.h"

void signal_handler(int signum) {
    if (signum == SIGNAL_NAME) {
        printf("Receiver: Signal received by receiver!\n");
    }
}

int main() {
    signal(SIGNAL_NAME, signal_handler);

    printf("Receiver: Waiting for signal...\n");
    pause(); // Wait for a signal

    return 0;
}

4. 共享内存

common.h 文件

// common.h
#ifndef COMMON_H 
#define COMMON_H

#include <sys/types.h>

#define SHARED_MEMORY_SIZE 1024

struct SharedData {
    char message[SHARED_MEMORY_SIZE];
};

#endif // COMMON_H

writer.c 文件

// writer.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>   
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
#include "common.h"

int main() {
    key_t key = ftok(".", 'A');
    if (key == -1) {
        perror("Writer: ftok failed");
        exit(EXIT_FAILURE);
    }

    int shmid = shmget(key, sizeof(struct SharedData), 0666 | IPC_CREAT);
    if (shmid == -1) {
        perror("Writer: shmget failed");
        exit(EXIT_FAILURE);
    }

    struct SharedData *shared_data = (struct SharedData *)shmat(shmid, NULL, 0);
    if (shared_data == (void *)-1) {
        perror("Writer: shmat failed");
        exit(EXIT_FAILURE);
    }

    printf("Writer: Enter a message: ");
    fgets(shared_data->message, SHARED_MEMORY_SIZE, stdin);

    shmdt(shared_data);

    return 0;
}

reader.c 文件

// reader.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
#include "common.h"

int main() {
    key_t key = ftok(".", 'A');
    if (key == -1) {
        perror("Reader: ftok failed");
        exit(EXIT_FAILURE);
    }

    int shmid = shmget(key, sizeof(struct SharedData), 0666 | IPC_CREAT);
    if (shmid == -1) {
        perror("Reader: shmget failed");
        exit(EXIT_FAILURE);
    }

    struct SharedData *shared_data = (struct SharedData *)shmat(shmid, NULL, 0);
    if (shared_data == (void *)-1) {
        perror("Reader: shmat failed");
        exit(EXIT_FAILURE);
    }

    printf("Reader: Received message: %s", shared_data->message);

    shmdt(shared_data);
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

5. 套接字

套接字通信在后面的网络编程会详细讲,此处略!

6. 文件映射

common.h 文件

// common.h
#ifndef COMMON_H 
#define COMMON_H

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

#define FILE_PATH "shared_file"
#define FILE_SIZE 1024

struct SharedData {
    char message[FILE_SIZE];
};

#endif // COMMON_H

writer.c 文件

// writer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"

int main() {
    int fd = open(FILE_PATH, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        perror("Writer: open failed");
        exit(EXIT_FAILURE);
    }

    if (ftruncate(fd, sizeof(struct SharedData)) == -1) {
        perror("Writer: ftruncate failed");
        exit(EXIT_FAILURE);
    }

    struct SharedData *shared_data = mmap(NULL, sizeof(struct SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shared_data == MAP_FAILED) {
        perror("Writer: mmap failed");
        exit(EXIT_FAILURE);
    }

    printf("Writer: Enter a message: ");
    fgets(shared_data->message, FILE_SIZE, stdin);

    munmap(shared_data, sizeof(struct SharedData));
    close(fd);

    return 0;
}

reader.c 文件

// reader.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
#include "common.h"

int main() {
    int fd = open(FILE_PATH, O_RDONLY);
    if (fd == -1) {
        perror("Reader: open failed");
        exit(EXIT_FAILURE);
    }

    struct SharedData *shared_data = mmap(NULL, sizeof(struct SharedData), PROT_READ, MAP_SHARED, fd, 0);
    if (shared_data == MAP_FAILED) {
        perror("Reader: mmap failed");
        exit(EXIT_FAILURE);
    }

    printf("Reader: Received message: %s", shared_data->message);

    munmap(shared_data, sizeof(struct SharedData));
    close(fd);

    return 0;
}

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值