linux 系统编程 -- 进程间通讯IPC 管道(八)

因为在linux系啊,进程地址空间相互独立,所以进程和进程之间不能相互访问。要交换数据必须通过内核。
有很多方法都可以完成这类功能,比如文件、管道、命名管道、信号、共享内存、消息队列、套接字等,随着技术的发展,有些方法已经被淘汰。现今最常用的方法是:
① 管道 :使用最简单,传递数据量较小 ,本机程序。

②信号:开销最小

③共享内存: 无血缘关系,相对复杂

④套接字:难度最高,最稳定,传递大量数据,不同机器。

整个linux文件可分为7种类型,- 文件 、 d 目录、 l 符号链接、s套接字、b 块设备、 c字符设备、 p管道

但实际上只有- 文件 、 d 目录、 l 符号链接 这3种是真正占用磁盘空间。
而后4种属于伪文件,不会占用磁盘空间。

管道就是伪文件,实质上内核缓冲区(内核使用环形队列方式实现),缓冲区大小默认是4k。
因此管道具有如下特性:
① 数据从一端写入,从另一端读出,不能自己读自己写。

② 数据一旦被读走,便不存在于管道中,因此不可反复读取。

③半双工通信方式、数据只能在一个方向上流动。

④只能在有血缘关系的进程之间使用。

int pipe(int pipefd[2]) 成功返回0,失败返回-1
pipefd[2] 存储着两个文件描述符,pipefd[0]表示写入端,pipefd[1]表示读取端。

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


int main()
{
    int pipefd[2];
    pid_t pid;
    int rep;
    char buf[20]="hello pipe\n";

    rep=pipe(pipefd);
    if (rep==-1)
    {
        perror("pipe");
        exit(1);
    }
    pid=fork();

    if (pid>0)
    {
        // parent wirte
        close(pipefd[0]);
        write(pipefd[1],buf,20);
        wait(NULL);

    }
    else if(pid ==0)
    {
        // child read
        close(pipefd[1]);
        read(pipefd[0],buf,20);
        printf("child has read %s",buf);

    }
    else
    {
        perror("fork");
        exit(1);
    }
    
    return 0;
}

这里有个思考,父写子读的时候,那就默认父进程必须要先执行,子进程后执行,但是linux中父子进程的先后顺序又没有保证。
那么程序会不会有bug?
子进程会不会先执行?
所以我在父进程中添加

 // parent wirte
        sleep(3);
        printf("is parent \n");

在这里插入图片描述
父进程依然先执行了。

如果把程序改写成子写父读,会怎么样呢?
会不会父进程先执行,但是此时并没有写入,所以程序出错读不出来?

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


int main()
{
    int pipefd[2];
    pid_t pid;
    int rep;
    char buf[20]="hello pipe\n";

    rep=pipe(pipefd);
    if (rep==-1)
    {
        perror("pipe");
        exit(1);
    }
    pid=fork();

    if (pid==0)
    {
        // child wirte
        close(pipefd[0]);
        write(pipefd[1],buf,20);
        wait(NULL);

    }
    else if(pid >0)
    {
        // parent read
        printf("is parent\n");
        close(pipefd[1]);
        read(pipefd[0],buf,20);
        printf("parent has read %s",buf);

    }
    else
    {
        perror("fork");
        exit(1);
    }
    
    return 0;
}

在这里插入图片描述
程序正确执行,并没有问题,为什么呢?
原因在于wirte和read本身就是阻塞型I/O,一旦有了read或者wirte,系统会阻塞等待直到另一方完成操作。
所以在管道通信时,无需自己再去考虑进程的执行次序问题。

有名管道

改进了管道的一些缺点,可以用于血缘关系进程,也可以用于非血缘关系进程。

两个独立运行的进程,最传统的可以用文件通讯,一个进程打开某个文件写入,然后另一个进程读取该文件。
这种方式要考虑到先后次序问题,即竞争问题,数据是被真正写入文件。
有名管道有点像文件通讯,但实际上并不真正把数据写入文件中,它和文件通讯的写法基本一样,有一个专用的函数来完成此功能。
#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode); 成功返回0,错误返回-1

只需要其中一个独立进程创建有名管道就可以了,另一个进程需要去检测有名管道是否存在,如果存在,就打开,如果不存在,这一个进程可以选择等待,也可以选择创建有名管道。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值