进程间通信方式(IPC)

常见的进程间通信(IPC)方式包括管道(Pipe)、命名管道(FIFO)、消息队列(Message Queue)、共享内存(Shared Memory)、信号(Signal)和套接字(Socket)。
下面是这些通信方式的定义、共同点和不同点、可以执行代码的详细说明:

1. 管道(Pipe)

定义:管道是最基本的进程间通信机制,允许一个进程将数据写入管道,而另一个进程从管道中读取数据。管道分为匿名管道和有名管道。

共同点

  • 半双工:数据只能在一个方向流动,通常是从写端到读端。
  • 进程间:通常用于在父子进程之间进行通信。

不同点

  • 匿名管道:通常在创建进程时使用(如 fork()),只在创建它们的进程之间有效。
  • 有名管道(FIFO):可以在不同进程间通信,即使这些进程不是父子关系,也可以通过文件系统路径进行通信。

代码

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

int main() {
    int fd[2];  // 创建一个整型数组,用于存储管道的读写文件描述符
    pid_t pid;  // 存储子进程ID

    pipe(fd);  // 创建管道,fd[0] 是读端,fd[1] 是写端

    pid = fork();  // 创建子进程

    if (pid == 0) {  // 子进程
        close(fd[0]);  // 子进程关闭管道的读端
        char message[] = "Hello from child process!";
        write(fd[1], message, strlen(message));  // 子进程向管道写数据
        close(fd[1]);  // 子进程关闭管道的写端
    } else {  // 父进程
        close(fd[1]);  // 父进程关闭管道的写端
        char buffer[100];
        read(fd[0], buffer, sizeof(buffer));  // 父进程从管道读取数据
        printf("Parent process received: %s\n", buffer);  // 打印从子进程收到的消息
        close(fd[0]);  // 父进程关闭管道的读端
    }

    return 0;
}

2. 命名管道(FIFO)

定义:命名管道是一种特殊类型的文件,允许不相关的进程通过文件系统中的路径进行通信。

共同点

  • 全双工:数据流动可以是双向的,但必须手动控制读写端。
  • 进程间:可以在不相关的进程间进行通信。

不同点

  • 持久性:FIFO 文件存在于文件系统中,可以被任何进程打开和使用。
  • 路径:通信需要通过一个特定的文件路径进行。

代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h> 
#include <string.h>

#define FIFO_PATH "/tmp/myfifo"  // FIFO 文件路径
#define BUFFER_SIZE 100

int main() {
    // 创建 FIFO 文件
    if (mkfifo(FIFO_PATH, 0666) == -1) {
        perror("mkfifo");
        exit(EXIT_FAILURE);
    }

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

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

    if (pid == 0) {  // 子进程
        char buffer[BUFFER_SIZE];
        int fd = open(FIFO_PATH, O_RDONLY);  // 以只读模式打开 FIFO 文件

        if (fd == -1) {
            perror("open");
            exit(EXIT_FAILURE);
        }

        // 从 FIFO 文件中读取数据
        ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1);
        if (bytesRead == -1) {
            perror("read");
            exit(EXIT_FAILURE);
        }

        buffer[bytesRead] = '\0';  // 确保字符串以 null 结尾
        printf("Child process received: %s\n", buffer);  // 打印接收到的数据

        close(fd);  // 关闭 FIFO 文件
        exit(EXIT_SUCCESS);
    } else {  // 父进程
        int fd = open(FIFO_PATH, O_WRONLY);  // 以只写模式打开 FIFO 文件

        if (fd == -1) {
            perror("open");
            exit(EXIT_FAILURE);
        }

        const char *message = "Hello from parent";
        ssize_t bytesWritten = write(fd, message, strlen(message));
        
        if (bytesWritten == -1) {
            perror("write");
            exit(EXIT_FAILURE);
        }

        close(fd);  // 关闭 FIFO 文件

        // 等待子进程完成
        wait(NULL);

        // 删除 FIFO 文件
        if (unlink(FIFO_PATH) == -1) {
            perror("unlink");
            exit(EXIT_FAILURE);
        }
    }

    return 0;
}

3. 消息队列(Message Queue)

定义:消息队列允许进程以消息的形式发送和接收数据,每条消息都有一个类型,并可以在消息队列中排队。

共同点

  • 异步通信:发送消息的进程和接收消息的进程可以在不同的时间进行操作。
  • 进程间:可以在不相关的进程之间进行通信。

不同点

  • 消息类型:消息可以按照类型进行分类和接收。
  • 内存管理:操作系统负责管理消息队列中的消息,支持消息的排队和优先级排序。

代码

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

struct msg_buffer {
    long msg_type;  // 消息类型
    char msg_text[100];  // 消息内容
};

int main() {
    key_t key = ftok("progfile", 65);  // 生成唯一的键
    int msgid = msgget(key, 0666 | IPC_CREAT);  // 创建消息队列并返回消息ID
    struct msg_buffer message;

    if (fork() == 0) {  // 子进程
        message.msg_type = 1;
        sprintf(message.msg_text, "Hello from child process!");
        msgsnd(msgid, &message, sizeof(message), 0);  // 子进程发送消息
    } else {  // 父进程
        msgrcv(msgid, &message, sizeof(message), 1, 0);  // 父进程接收消息
        printf("Parent process received: %s\n", message.msg_text);  // 打印接收到的消息
        msgctl(msgid, IPC_RMID, NULL);  // 删除消息队列
    }

    return 0;
}

4. 共享内存(Shared Memory)

定义:共享内存允许多个进程访问同一块内存区域,通常用于高速交换大量数据。

共同点

  • 高效:由于所有进程共享相同的内存区域,数据交换速度非常快。
  • 进程间:可以在不相关的进程之间进行通信。

不同点

  • 同步:需要额外的同步机制(如信号量)来确保数据的一致性和避免竞争条件。
  • 直接访问:进程直接操作共享的内存区域,降低了数据交换的开销。

代码

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

int main() {
    key_t key = ftok("shmfile", 65);  // 生成唯一的键
    int shmid = shmget(key, 1024, 0666 | IPC_CREAT);  // 创建共享内存段并返回ID

    if (fork() == 0) {  // 子进程
        char *str = (char*) shmat(shmid, (void*)0, 0);  // 连接到共享内存
        sprintf(str, "Hello from child process!");  // 子进程写入数据
        shmdt(str);  // 断开与共享内存的连接
    } else {  // 父进程
        wait(NULL);  // 等待子进程完成
        char *str = (char*) shmat(shmid, (void*)0, 0);  // 连接到共享内存
        printf("Parent process received: %s\n", str);  // 打印共享内存中的数据
        shmdt(str);  // 断开与共享内存的连接
        shmctl(shmid, IPC_RMID, NULL);  // 删除共享内存段
    }

    return 0;
}

5. 信号(Signal)

定义:信号是一种用于通知进程某种事件发生的机制,信号通常不用于传递大量数据,只用于传递控制信息。

共同点

  • 异步通知:进程可以在接收到信号时立即执行信号处理程序。
  • 进程间:可以在不同进程间传递控制信息。

不同点

  • 数据传输限制:信号只能传递有限的控制信息,不适合传递大量数据。
  • 处理程序:需要设置信号处理程序来响应特定信号。

代码

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

void signal_handler(int signum) {
    printf("Received signal %d\n", signum);  // 信号处理函数,打印接收到的信号
}

int main() {
    signal(SIGUSR1, signal_handler);  // 注册信号处理程序

    if (fork() == 0) {  // 子进程
        sleep(1);  // 等待父进程准备好
        kill(getppid(), SIGUSR1);  // 子进程向父进程发送信号
        exit(0);
    } else {  // 父进程
        pause();  // 父进程等待信号,使父进程挂起,直到接收到一个信号。该信号会使进程继续执行并调用相应的信号处理程序。
        printf("Signal received from child process\n");
    }

    return 0;
}

6. 套接字(Socket)

定义:套接字是一种进程间通信机制,尤其用于网络通信。套接字可以用于同一台计算机上的进程间通信,也可以用于不同计算机之间的通信。

共同点

  • 全双工:数据流动可以是双向的。
  • 进程间:可以在同一台计算机或不同计算机上的进程间进行通信。

不同点

  • 网络通信:套接字支持网络通信,可以用于不同主机之间的进程通信。
  • 协议支持:支持多种通信协议,如 TCP 和 UDP。

代码

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

void signal_handler(int signum) {
    printf("Received signal %d\n", signum);  // 信号处理函数,打印接收到的信号
}

int main() {
    signal(SIGUSR1, signal_handler);  // 注册信号处理程序

    if (fork() == 0) {  // 子进程
        sleep(1);  // 等待父进程准备好
        kill(getppid(), SIGUSR1);  // 子进程向父进程发送信号
        exit(0);
    } else {  // 父进程
        pause();  // 父进程等待信号,使父进程挂起,直到接收到一个信号。该信号会使进程继续执行并调用相应的信号处理程序。
        printf("Signal received from child process\n");
    }

    return 0;
}

总结

  • 共同点

    • 以上所有IPC机制都用于进程间数据传输或通信。
    • 都需要在进程间共享某种形式的资源或状态。
  • 不同点

    • 方向性:如管道通常是半双工的,而消息队列和共享内存可以是全双工的。
    • 数据量:信号不适合传递大量数据,而共享内存适合传递大量数据。
    • 同步:共享内存需要额外的同步机制,而管道和消息队列内置了数据同步机制。
    • 持久性:FIFO 文件持久存在于文件系统中,而管道是临时的,通常在进程结束后消失。
    • 网络能力:套接字支持网络通信,而其他IPC机制通常只在同一台计算机上使用。

如果对你有帮助的话,欢迎打赏

图片

  • 13
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值