进程间通信及管道

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

posted @ 2017-04-27 11:26  fuluwwa  阅读(190)  评论(0)    收藏  举报