进程间通信及管道
Linux 下进程间通信
处于用户态的不同进程之间是彼此隔离。
Linux下的进程通信方式:
- 管道及有名管道
管道可用于具有亲缘关系进程间的通信,
有名管道,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
- 信号
信号是在软件层次上对中断机制的一种模拟。
- 消息队列
消息队列是消息的链接表。具有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读取消息。
- 共享内存
它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种通信方式需要依靠某种同步机制,如互斥锁和信号量等。
- 信号量
主要作为进程间以及同一进程不同线程之间的同步手段。
- 套接字(socket)
更为一般的进程间通信机制,它可用于不同机器之间的
进程间通信,应用非常广泛。
管道
管道是基于文件描述符的通信方式,当一个管道建立时,它会创建两个文件描述符fds[0]和fds[1],其中fds[0]固定用于读管道,而fd[1]固定用于写管道。
这样就构成了一个半双工的通道。
关闭管道时,只要close两个文件描述符。
- 创建管道
SYNOPSIS
#include <unistd.h>
int pipe(int pipefd[2]);
- 读写管道
fork的子进程也继承了父进程的管道。父子进程分别拥有自己的读写的通道,为了实现父子进程之间的读写,只需把无关的读
端或写端的文件描述符关闭即可。
实例:
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int pipe_fd[2];
pid_t pid;
char buf_r[100] = {0};
char* p_wbuf;
int r_num;
if(pipe(pipe_fd) < 0){
printf("pipe create error\n");
return -1;
}
if((pid = fork()) == 0){
printf("\n");
close(pipe_fd[1]);
/* make sure parent process write over */
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){
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);
}
}
结果输出:
xxx@xxx-pc:~/Documents$ ./a.out
parent write1 success!
parent write2 success!
10 numbers read from the pipe is Hello Pipe
标准流管道
标准流管道是基于文件流的管道,由popen函数创建。完成了以下事:
- 创建一个管道
- fork一个子进程
- 在父子进程中关闭不必要的文件描述符
- 执行exec函数族调用
- 执行函数中指定的命令
用popen创建的管道必须使用标准I/O函数进行操作,
但不能使用前面的read、write一类不带缓冲的I/O 函数。
SYNOPSIS
#include <stdio.h>
/* command: 指向null结尾的字符串,被送到/bin/sh以-c参数执行
* type: "r",文件指针连接到command的标准输出;"w",文件指针连接到command的标准输入
* return: 文件指针
*/
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
实例:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define BUFSIZE 1000
int main()
{
FILE *fp;
char *cmd = "ps -ef";
char buf[BUFSIZE] = {0};
if((fp = popen(cmd,"r")) == NULL)
perror("popen");
while((fgets(buf,BUFSIZE,fp)) != NULL)
printf("%s",buf);
pclose(fp);
exit(0);
}
FIFO
有名管道它可以使互不相关的两个进程实现彼此通信。
该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当作普通文件一样进行读写操作,使用非常方便。
在创建管道成功之后,就可以使用open、read、write这些函数了。
在管道的读写中有阻塞的可能,open以O_NONBLOCK
打开管道可以设置为非阻塞模式。
SYNOPSIS
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
实例:
读进程中创建FIFO
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO "/tmp/myfifo"
int main()
{
int fd;
char buf[256] = {0};
unlink(FIFO);
if(mkfifo(FIFO, 0666) < 0){
printf("cannot create fifo\n");
}
fd = open(FIFO, O_RDONLY | O_NONBLOCK, 0);
if(fd == -1){
perror("open");
exit(1);
}
while(1){
memset(buf, 0, sizeof(buf));
if(read(fd, buf, sizeof(buf)) == -1){
if(errno == EAGAIN){
printf("no data yet\n");
}
}
printf("read %s from FIFO\n", buf);
sleep(1);
}
return 0;
}
另外一个进程,写进程写FIFO
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO "/tmp/myfifo"
int main(int argc, char *argv[])
{
int fd;
char buf[256] = {0};
fd = open(FIFO, O_WRONLY | O_NONBLOCK, 0);
if(fd == -1){
perror("open");
if(errno == ENXIO){
printf("open error; no reading process\n");
}
exit(1);
}
if(argc != 2){
return -1;
}
memcpy(buf, argv[1], strlen(argv[1]));
if(write(fd, buf, sizeof(buf)) == -1){
/* nonblock mod call the block operation */
if(errno == EAGAIN){
printf("The FIFO has not been read yet. Please try later\n");
}
}else{
printf("write %s to FIFO\n", buf);
}
return 0;
}
结果如下:
先启动读进程,后启动写进程。
FIFO文件
xxx@xxx-pc:~/Documents$ ls -l /tmp/myfifo
prw-rw-r-- 1 xxx xxx 0 Apr 28 01:37 /tmp/myfifo
读进程
xxx@xxx-pc:~/Documents$ ./read
read from FIFO
read my-fifo from FIFO
写进程
xxx@xxx-pc:~/Documents$ ./write my-fifo
write my-fifo to FIFO
如果先启动写进程,open FIFO管道时会报错。
xxx@xxx-pc:~/Documents$ ./write my-world2
open: No such device or address
open error; no reading process