进程间通信及管道

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  阅读(170)  评论(0编辑  收藏  举报