IPC进程通信:linux

进程通信

进程通信( InterProcess Communication,IPC)就是指进程之间的信息交换。实际上,进程的同步与互斥本质上也是一种进程通信(这也就是待会我们会在进程通信机制中看见信号量和 PV 操作的原因了),只不过它传输的仅仅是信号量,通过修改信号量,使得进程之间建立联系,相互协调和协同工作,但是它缺乏传递数据的能力。

虽然存在某些情况,进程之间交换的信息量很少,比如仅仅交换某个状态信息,这样进程的同步与互斥机制完全可以胜任这项工作。但是大多数情况下,进程之间需要交换大批数据,比如传送一批信息或整个文件,这就需要通过一种新的通信机制来完成,也就是所谓的进程通信。

再来从操作系统层面直观的看一些进程通信:我们知道,为了保证安全,每个进程的用户地址空间都是独立的,一般而言一个进程不能直接访问另一个进程的地址空间,不过内核空间是每个进程都共享的,所以进程之间想要进行信息交换就必须通过内核。

Linux 内核提供的常见的进程通信机制:

  • 管道(也称作共享文件)
  • 消息队列(也称作消息传递)
  • 共享内存(也称作共享存储)
  • 信号量和 PV 操作
  • 信号
  • 套接字(Socket)

一、 管道

1. 匿名管道

管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。如果想实现相互通信(全双工通信),我们需要创建两个管道才行。另外,通过管道符 | 创建的管道是匿名管道,用完了就会被自动销毁。并且,匿名管道只能在具有亲缘关系(父子进程)的进程间使用。也就是说,匿名管道只能用于父子进程之间的通信。

在 Linux 的实际编码中,是通过 pipe 函数来创建匿名管道的,若创建成功则返回 0,创建失败就返回 -1。fork()调用后,操作系统会复制父进程的地址空间到子进程,包括代码段、数据段、堆栈等。这意味着子进程拥有父进程的内存空间的一个副本。fork()返回两次,一次在父进程中,返回子进程的PID;一次在子进程中,返回0。如果fork()调用失败,它会在父进程中返回-1,并且设置errno来指示错误:

如果pid1 == 0,则当前代码运行在子进程中。
如果pid1 > 0,则当前代码运行在父进程中。:

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
    pid_t pid1;
    int fields[2];
    char buffer[80];
    char s[100];
    char ss[100];
    if (pipe(fields) != 0)
    {
        fprintf(stderr, "Createpipe error:%s\n\a", strerror(errno));
        exit(1);
    }
    if ((pid1 = fork()) < 0)
        printf("fork child error!\n");
    /* 子进程写入数据 */
    if (pid1 == 0)
    {
        printf("fork child,pid is %d, child is sending a message !\n", pid1);
        char s[] = "hello!\n";
        write(fields[1], s, sizeof(s));
        exit(0);
    }
    /* 父进程读取数据 */
    else
    {
        printf("parent read start, pid is %d!\n", pid1);
        read(fields[0], buffer, 80);
        printf("parent receive the message:%s", buffer);
    }
    exit(0);
}

2.命名管道

命名管道fifo解决了pipe只能有关系的进程才能通信的问题,实现一个有名管道实际上就是实现一个FIFO文件,有名管道一旦建立,之后它的读,以及关闭操作都与普通管道完全相同。虽然FIFO文件的inode节点在磁盘上,但仅是一个节点而已,文件的数据还是存在内核缓冲页面上,和普通管道相同。
使用 Linux 命令 mkfifo 来创建有名管道:

mkfifo myPipe

myPipe 就是这个管道的名称,接下来,我们往 myPipe 这个有名管道中写入数据:

echo "hello" > myPipe

执行这行命令后,你会发现它就停在这了,这是因为管道里的内容没有被读取,只有当管道里的数据被读完后,命令才可以正常退出。于是,我们执行另外一个命令来读取这个有名管道里的数据:

cat < myPipe

读进程,定义一个结束消息,保证在写进程先退出时读进程不会发生疯读现象:

// 读进程
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define FIFO_PATH "myfifofile"
#define END_MESSAGE "END" // 定义结束消息

int main()
{
    int fd;
    char cont_r[256]; // 增加1字节来存储结束字符

    // 创建命名管道
    if (mkfifo(FIFO_PATH, 0666) < 0 && errno != EEXIST)
    {
        perror("create fifo failed");
        return -1;
    }
    else
    {
        printf("create fifo success\n");

        // 打开文件进行读操作
        fd = open(FIFO_PATH, O_RDONLY, 0666);
        if (fd > 0)
        {
            while (1)
            {
                // 读取数据
                ssize_t bytes_read = read(fd, cont_r, sizeof(cont_r) - 1);
                if (bytes_read <= 0)
                {
                    // 如果没有数据可读,退出循环
                    break;
                }

                // 确保字符串以空字符结尾
                cont_r[bytes_read] = '\0';

                // 打印读取到的数据
                printf("read: %s\n", cont_r);

                // 检查是否接收到结束消息
                if (strcmp(cont_r, END_MESSAGE) == 0)
                {
                    printf("Received end message, exiting...\n");
                    break;
                }
            }
            close(fd);
        }
        else
            perror("open failed");
    }
    return 0;
}

写进程:

// 写进程
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define FIFO_PATH "myfifofile"

int main()
{
    int fd;
    char cont_w[] = "hello sundy";
    const char *end_message = "END"; // 结束消息

    if (mkfifo(FIFO_PATH, 0666) < 0 && errno != EEXIST)
    {
        perror("create fifo failed");
        return -1;
    }
    else
    {
        p
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值