进程间的通信之无名管道

从一个进程连接数据流到另一个进程时,就使用管道,通常是把一个进程的输出通过管道连接到另一个进程的输入。

1.pipe函数
该函数的原型为int pipe(int file_descritor[2]),其参数是一个由两个整数类型的文件描述符组成的数组的指针,该函数在数组中填上两个新的文件描述符后返回0,如果失败返回-1.两个文件描述符以一种特殊的方式连接起来,写到file_descriptor[1]的数据都可以从file_descriptor[0]读回来。数据基于先进先出的原则进行处理。

2.读写规则
管道两端可分别用描述字fd[0]以及fd[1]来描述,需要注意的是,管道的两端是固定了任务的。即一端只能用于读,由描述字fd[0]表示,称其为管道 读端;另一端则只能用于写,由描述字fd[1]来表示,称其为管道写端。如果试图从管道写端读取数据,或者向管道读端写入数据都将导致错误发生。一般文件 的I/O函数都可以用于管道,如close、read、write等等。

2.1从管道中读取数据
如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0; 当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现 有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数据量不小于请求的数据量)。

2.2向管道中写入数据
向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。
注:只有在管道的读端存在时,向管道中写入数据才有意义。

3.read
read(fd,buf,nbyte)
功能:从fd所指示的文件中读出nbyte个字节的数据,并将它们送至由指针buf所指示的缓冲区中。如该文件被加锁,等待,直到锁打开为止。

4.write
write(fd,buf,nbyte)
功能:把nbyte个字节的数据,从buf所指向的缓冲区写到由fd所指向的文件中。如文件加锁,暂停写入,直至开锁。

5.sprintf
sprintf(str, format )
功能:根据参数format 字符串来转换并格式化数据,然后将结果复制到参数str所指的字符串数组,直到出现字符结束(‘\0’)为止。

6.程序源码
创建文件 vi noNamepipe.c

#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,child is sending a message !\n");
    char s[]="hello!\n";
    write(fields[1],s,sizeof(s));
    exit(0)
        }
/* 父进程读取数据 */
    else 
    {
    printf("parent read start !\n");
    read(fields[0],buffer,80);
printf("parent receive the message:%s",buffer);
    }
    exit (0);
}


编译运行

cc -o unamepipe unamepipe.c -g
./unamepipe

进程间通信之无名管道

无名管道(Unnamed Pipe)是Unix/Linux系统中一种最基本的进程间通信(IPC)机制,主要用于具有亲缘关系的进程(如父子进程)之间的通信。

基本特性

  1. 半双工通信:数据只能单向流动,一端用于写入,另一端用于读取
  2. 亲缘关系:通常用于父子进程或兄弟进程间的通信
  3. 内存结构:基于内核缓冲区实现,大小有限(通常为几KB)
  4. 生命周期:随进程的终止而自动销毁

创建和使用

在C语言中,使用pipe()系统调用创建无名管道:

#include <unistd.h>

int pipe(int pipefd[2]);
  • pipefd[0]:读取端文件描述符
  • pipefd[1]:写入端文件描述符
  • 成功返回0,失败返回-1

典型使用场景

父子进程通信

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

int main() {
    int pipefd[2];
    char buf[100];
    pid_t pid;
    
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return 1;
    }
    
    pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }
    
    if (pid == 0) { // 子进程
        close(pipefd[1]); // 关闭写端
        read(pipefd[0], buf, sizeof(buf));
        printf("Child received: %s\n", buf);
        close(pipefd[0]);
    } else { // 父进程
        close(pipefd[0]); // 关闭读端
        const char *msg = "Hello from parent";
        write(pipefd[1], msg, strlen(msg) + 1);
        close(pipefd[1]);
    }
    
    return 0;
}

代码解析

1. 创建无名管道

int pipefd[2];  // pipefd[0]是读端,pipefd[1]是写端
if (pipe(pipefd) == -1) {
    perror("pipe");
    return 1;
}
  • pipe(pipefd) 创建一个无名管道,返回两个文件描述符:
    • pipefd[0]读端(用于读取数据)
    • pipefd[1]写端(用于写入数据)
  • 如果失败,返回 -1 并打印错误信息。
  • 2. 创建子进程(fork)
  • pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

  • fork() 创建子进程:

    • 父进程fork() 返回 子进程的 PID(>0)
    • 子进程fork() 返回 0
    • 失败:返回 -1(如系统资源不足)
    • 3. 子进程(读取数据)
    • if (pid == 0) { // 子进程
          close(pipefd[1]); // 关闭写端(子进程只读)
          read(pipefd[0], buf, sizeof(buf)); // 从管道读取数据
          printf("Child received: %s\n", buf);
          close(pipefd[0]); // 关闭读端
      }

    • 关闭写端:子进程不需要写入,关闭 pipefd[1] 避免资源泄漏。
    • 读取数据
      • read(pipefd[0], buf, sizeof(buf)) 从管道读取数据到 buf
      • 如果管道为空,read() 会 阻塞,直到父进程写入数据。
    • 打印数据:子进程输出接收到的消息。

    • 4. 父进程(写入数据)

    • else { // 父进程
          close(pipefd[0]); // 关闭读端(父进程只写)
          const char *msg = "Hello from parent";
          write(pipefd[1], msg, strlen(msg) + 1); // 写入管道
          close(pipefd[1]); // 关闭写端
      }

    • 写入数据
      • write(pipefd[1], msg, strlen(msg) + 1) 向管道写入数据。
      • strlen(msg) + 1 确保写入字符串的 '\0' 终止符。
      • 如果管道已满,write() 会 阻塞,直到子进程读取数据。
    • 关闭写端:写入完成后关闭 pipefd[1]

注意事项

  1. 同步问题:当管道为空时,读操作会阻塞;当管道满时,写操作会阻塞
  2. 关闭未使用的描述符:应该及时关闭不需要的管道端,避免资源泄漏
  3. 原子性:小于PIPE_BUF(通常4096字节)的写入是原子的
  4. EOF处理:当所有写端关闭后,读端会返回0(EOF)

优缺点

优点

  • 简单易用
  • 不需要考虑同步问题(内核处理)
  • 适合少量数据传输

缺点

  • 只能用于有亲缘关系的进程
  • 半双工,单向通信
  • 缓冲区大小有限

无名管道是学习进程间通信的基础,在实际开发中,对于简单场景仍然非常有用。

————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.youkuaiyun.com/qq_38048756/article/details/109207767

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值