linux 进程间共用errno,进程间通信

进程间通信(IPC(InterProcess Communication))

在上一节进程中我们提到了,等待进程的机制是老张隔一段时间去厨房看一下水有没有烧开(非阻塞同步)。

跟站在旁边等水开(阻塞同步)相比,这样做有优势,但是仍然浪费了很多时间和资源。最好是买个能发出信号的水壶。

水开时通知老张,也就是需要进程间的通信。

进程间有几种经典通讯方式:管道、信号、消息队列、信号量、共享内存。

无名管道(匿名管道)

在linux系统中,一切皆是文件。管道、也是一种文件。因为是无名管道,也就是没有名字,没有文件描述符。

和普通文件不同的是,无名管道不占用内存。

管道是半双工的通讯方式,类似于stm32中的串口。我们这里复习一下半双工的概念:

单工:只支持单向的数据传输。比如电视 广播

半双工:同一时间,只能有一个方向的数据传输。比如对讲机

全双工:同一时间,可以互相发送数据,实现同时收发的功能。比如电话

这里介绍的管道是半双工通讯。同一时间,只能由一个进程写,另一个进程读。

(那么有没有全双工的管道呢?有。我们叫他流管道,后面有机会再说)

使用管道之前我们要创建一个管道,前面说过管道也是一个文件。那么创建管道就和创建文件类似

int pipe( int fd[2] );

fd[2]  其实就是前面文件iO中的文件描述符,这里的表达形式表示:

能且只能返回2个参数,分别是 fd[0] 和 fd[1],这两个文件描述符分别用来读、写。

成功返回0,失败返回 -1 .(所以可以用perror()了)

除了创建之外,读写关闭都可以用文件IO中的函数操作。

我们使用这个函数创建一个管道:

#include #include#include#include

intmain()

{int pipe_fd[2];/*创建一无名管道*/

if(pipe(pipe_fd)<0)

{

perror("pipe create error");return -1;

}else{

printf("pipe create success\n");

}/*关闭管道描述符*/close(pipe_fd[0]);

close(pipe_fd[1]);

}

那么如何使用这个管道?

创建一个管道的目的,自然是让父子进程都可以使用。也就是说让父子进程共用一个管道

这样才能保证他们之间的通信,那么就需要pipe() 在 fork() 之前。否则子进程创建自己的管道他们之间信息传递不了

然后关掉父进程的读端fd[0]  子进程的写端fd[1]

变成了这个样子:

这样就达到了一个父进程写,子进程读的目的。

看下面的例子:

#include #include#include#include#include#include#include

intmain()

{int pipe_fd[2];

pid_t pid;char buf_r[100];char*p_wbuf;intr_num;

memset(buf_r,0,sizeof(buf_r));/*创建管道*/

if(pipe(pipe_fd)<0)

{

perror("pipe create error");return -1;

}/*创建一子进程*/

if((pid=fork())==0)

{

printf("\n");/*关闭子进程写描述符,并通过使父进程暂停 2 秒确保父进程已关闭相应的读描述符*/close(pipe_fd[1]);

sleep(2);/*子进程读取管道内容*/

if((r_num=read(pipe_fd[0],buf_r,100))>0)

{

printf("%d numbers read from the pipe is %s\n",r_num,buf_r);

}/*关闭子进程读描述符*/close(pipe_fd[0]);

exit(0);

}else if(pid>0)

{/*/关闭父进程读描述符,并分两次向管道中写入 Hello Pipe*/close(pipe_fd[0]);if(write(pipe_fd[1],"Hello",5)!= -1)

printf("parent write1 success!\n");if(write(pipe_fd[1],"Pipe",5)!= -1)

printf("parent write2 success!\n");/*关闭父进程写描述符*/close(pipe_fd[1]);

sleep(3);/*收集子进程退出信息*/waitpid(pid,NULL,0);

exit(0);

}

}

在fork()之后,父子进程分别关闭对应的读写端,使父进程只写 子进程只读

然后父进程写入 Hello Pipe,子进程读并打印,接下来分别关闭管道,父进程收尸退出。

因为无名管道的创建方式决定了,他只能用于有亲缘关系之间的进程通信。

向管道中写入数据时,linux 将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读取管道缓冲区中的数据,那么写操作将会一直阻塞。

命名管道(有名管道)

上面提到的无名管道只能进行有亲缘关系的通信,那么命名管道就是解决无亲缘关系的进程之间通信的问题

也就是说 在同一个电脑上的进程,都可以通过命名管道通信。

和无名管道类似,命名管道也是文件,所以读写关闭的文件IO操作同样可作用于此文件。

创建方法如下:

#include #include

int mkfifo(const char *pathname, mode_t mode);

int mknod(const char *pathname, mode_t mode, dev_t dev);

mkfifo是POSIX.1首先提出的。SVR3用mknod(2)系统调用创建FIFO。

而在SVR4中,mkfifo调用mknod创建FIFO。

POSIX.2已经建议了一个mkfifo(1)命令。SVR4和4.3+BSD现在支持此命令。

于是,用一条shell命令就可以创建一个FIFO,然后用一般的shell I/O重新定向对其进行存取。

以上摘自《UNIX环境高级编程》。下文中我们使用mkfifo()函数。

很类似我们的open函数,第一个参数是文件名,第二个参数mode是创建模式。dev是设备值,只有创建设备文件时会用到,一般填0。

mode参数可选值如下:

O_RDONLY:读管道

O_WRONLY:写管道

O_RDWR:读写管道

O_NONBLOCK:非阻塞

O_CREAT:如果该文件不存在,那么就创建一个新的文件,并用 第三的参数为其设置权限

O_EXCL:如果使用 O_CREAT 时文件存在,那么可返回错误消息。这一参数可测试文件是否存在

错误信息:

EACCESS 参数 filename 所指定的目录路径无可执行的权限

EEXIST 参数 filename 所指定的文件已存在

ENAMETOOLONG 参数 filename 的路径名称太长

ENOENT 参数 filename 包含的目录不存在

ENOSPC 文件系统的剩余空间不足

ENOTDIR 参数 filename 路径中的目录存在但却非真正的目录

EROFS 参数 filename 指定的文件存在于只读文件系统内

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值