linux进程通信---无名管道
进程是程序一次执行过程,用户态进程之间是隔离的,就像不同城市的人们,他们之间的通信方式有很多种Linux通信方式主要从unix继承而来,而对unix发展做出巨大贡献的是AT&T和BSD,前者主要是对早期的unix进程间通信进行系统的改进和升级,形成了system V IPC,这种通信方式主要是单个计算机内,而后者则跳出了这个限制,形成了socket。而linux把这两者都继承下来了具体如下:
进程是程序一次执行过程,用户态进程之间是隔离的,就像不同城市的人们,他们之间的通信方式有很多种
Linux通信方式主要从unix继承而来,而对unix发展做出巨大贡献的是AT&T和BSD,前者主要是对早期的unix进程间通信进行系统的改进和升级,形成了system V IPC,这种通信方式主要是单个计算机内,而后者则跳出了这个限制,形成了socket。而linux把这两者都继承下来了具体如下:
UNIX进程间通信(IPC)方式包括:管道,FIFO,信号
system v进程间通信(IPC):system v消息队列,system v信号量,system v共享内存区
posix进程间通信(IPC):posix消息队列,posix信号量,posix共享内存区
现在linux中使用较多的进程间通信有以下几种:
- 管道和有名管道:管道用于有亲缘关系的进程之间,有名管道可以用在没有亲缘关系的进程之间
- 信号:是在软件层次上对中断机制的一种模拟,比较复杂,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上是一样的
- 消息队列:消息队列是消息的链接表,包括posix消息队列,system v 消息队列,克服了前两种信息量有限的限制,具有写权限可以按照一定的规则向消息队列中添加消息,对消息队列有读权限的进程可以从消息队列中读取消息
- 共享内存:可以说这是最有用的进程间通信的方式,他使得多个进程可以访问同一个内存空间,不同进程可以看到对方进程中对共享内存中数据的更新,这种通信方式需要某种同步机制如互斥锁,信号量
- 信号量:主要作为进程之间,以及同一进程的不同线程之间的同步或者互斥手段
- 套接字:用于网络中进程的通信,应用非常广泛
管道是linux中很重要的通信方式,它是把一个程序的输出直接连接到另一个程序的输入,例如ps -ef| grep ntp如下图:
管道是linux中重要的通信方式,这里所说的是无名管道,无名管道具有以下特点:
- 他只能用于具有亲缘关系的进程之间(也就是父子进程或者是兄弟进程之间)
- 是一种半双工的通信模式,具有固定的读端和写端
- 可以把管道看成是一种普通的文件,对他的读写可以使用普通的read()和write()函数,但是又不是普通的文件,不属于其他任何文件系统,并且只存在于内核的内存空间中
管道是基于文件描述符的通信方式,当一个管道建立时,他会创建两个文件描述符fds[0], fds[1],其中fds[0]用于读管道,fds[1]用于写管道,这样就构成了半双工的通道:
关闭管道只需调用close()逐个关闭各个文件描述符即可
ps:当一个管道共享多对文件描述符时,若将其中一对读写文件描述符删除,则此管道就失效了
管道创建函数pipe()函数语法如下:
管道读写说明:
通常是先创建一个管道,再通过fork()函数创建一个子进程,该子进程会继承父进程所创建的管道,此时父子进程的文件描述符如下所示:
此时看上出非常复杂,只需将相关的文件描述符关闭即可,将父进程的fd[1]和子进程的fd[0]关闭,就变成了子进程写,父进程读的管道:
ps:父进程可以创建多个子进程,子进程会继承相应的fd[0]和fd[1]此时只需要关闭相应端口就可以建立起各子进程之间的通道
这里的例子首先创建一个管道,然后使用fork()创建一个子进程,然后通过关闭父子进程描述符建立起他们之间的管道通信
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_DATA_LEN 256
#define DELAY_TIME 1
int main()
{
pid_t pid ;
int pipe_fd[2] ;
char buf[MAX_DATA_LEN] ;
const char data[] = "pipe test program" ;
int real_read, real_write ;
memset((void *)buf, 0, sizeof(buf)) ;
//创建管道
if (pipe(pipe_fd) < 0)
{
printf("pipe create error\n") ;
exit(1) ;
}
if ((pid = fork()) == 0)//子进程
{
close(pipe_fd[1]) ;//关闭写端
sleep(DELAY_TIME * 3) ;//等待父进程关闭文件描述符
if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN))>0)
{
printf("%d bytes read from the pipe is '%s'\n", real_read,
buf) ;
}
close(pipe_fd[0]) ;
exit(0) ;
}
else if (pid > 0)//父进程
{
close(pipe_fd[0]) ;//关闭读端
sleep(DELAY_TIME) ;//等待子进程关闭写端
if ((real_write = write(pipe_fd[1], data, strlen(data))) != -1)
{
printf("parent wrote %d bytes:'%s'\n", real_write, data) ;
}
close(pipe_fd[1]) ;
waitpid(pid, NULL, 0) ;//收集子进程退出消息
exit(0) ;
}
}
运行结果如下:
管道读写注意一下几点:
- 只有存在读端,向管道中写才有意义,否则会传来SIGPIPE信号
- 向管道写入数据时不能保证其原子性,管道缓冲区一有空闲区域写端就是想其中写入,如果读进程不读取管道缓冲区中的数据,则写操作就会被阻塞
- 父子进程在运行时不能保证先后次序,这里为了保证父子进程已经关闭了相应的文件描述符,可以在两个进程中调用sleep函数,这个不是好的解决办法,学习了进程的同步与互斥之后可以修改程序
更多推荐
所有评论(0)