进程通信 [ fork() 管道( pipe() ) FIFO ]
来源:互联网 发布:巨人网络涨停 编辑:程序博客网 时间:2024/06/14 02:05
X86/Debian Linux/gcc
1 进程通信手段
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcessCommunication)。
Figure1.进程通信机制
进程间通信必须通过内核提供的通道,而且必须有一种办法在进程中标识内核提供的某个通道。
(1) 进程通信手段总结(抄)
- 父进程通过fork可以将打开文件的描述符传递给子进程
- 子进程结束时,父进程调用wait可以得到子进程的终止信息
- 几个进程可以在文件系统中读写某个共享文件,也可以通过给文件加锁来实现进程间同步
- 进程之间互发信号,一般使用SIGUSR1和SIGUSR2实现用户自定义功能
- 管道
- FIFO
- mmap函数,几个进程可以映射同一内存区
- SYS V IPC,以前的SYSV UNIX系统实现的IPC机制,包括消息队列、信号量和共享内存,现在已经基本废弃
- UNIX Domain Socket,目前最广泛使用的IPC机制
此笔记练习管道和FIFO。
(1) 管道
管道是一种最基本的IPC机制,由pipe函数创建:
int pipe(int filedes[2]);
调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。
开辟了管道之后实现两个进程间的通信,一般是用下列步骤:
(2) FIFO和UNIX Domain Socket
文件系统中的路径名是全局的,各进程都可以访问,因此可以用文件系统中的路径名来标识一个IPC通道。
用mkfifo 来创建一个FIFO
mkfifo fifo
ls -l fifo
prw-r--r-- 1 lly lly 0 Aug 10 16:10 fifo
FIFO文件在磁盘上没有数据块,仅用来标识内核中的一条通道,各进程可以打开这个文件进行read/write,实际上是在读写内核通道(根本原因在于这个file结构体所指向的read、write函数和常规文件不一样),这样就实现了进程间通信。
UNIXDomain Socket和FIFO的原理类似,也需要一个特殊的socket文件来标识内核中的通道。这些文件在磁盘上也没有数据块。
2 利用管道实现进程通信
(1) PIPE(2) LinuxProgrammer’s Manual
[1] 头文件及原型
#include <unistd.h>
int pipe(int filedes[2]);
[2] 功能简述
进程通信的一种手段,见1.1。
[3] 返回值
pipe函数调用成功返回0,调用失败返回-1。
(2) pipe()用于进程通信的一些情况
[1] 父进程写父进程读
/* Filename:pipe.c * Brife:Create one pipe to be used communication beteween two progress * Author:One fish * Date:2014.8.10 Sunday */#include <unistd.h>#include <stdlib.h>#include <string.h>#include <stdio.h>#define MAXPPSIZE80#defineMSG"Hello, world\n"int main(void){intn;intfd[2];pid_tpid;charpp_content[MAXPPSIZE];if (pipe(fd) < 0) {perror("pipe");exit(1);}if ( (pid = fork() ) < 0 ) {perror("fork");exit(1);}if (pid > 0) {//In parent progresswrite(fd[1], MSG, strlen(MSG) );n= read(fd[0], pp_content, MAXPPSIZE);write(STDOUT_FILENO, pp_content, n);} else {//In child progress;}return 0;}
- ssize_t read(int fd, void *buf, size_t count);尝试着从文件描述符fd哪里读到count字节保存到缓冲区buf内。当函数执行成功时,函数返回所读到数据的字节数m(读到文件末尾则返回0),并且使文件的位置向前移动m个位置。如果m < count并不一定代表着错误,遇到以下情况时m 会小于count:只有m个可读字节(可能是到了文件末尾),也有可能正在读一个管道,或者在读终端,或者read()被一个信号打断。如果函数执行错误则返回-1。
- 父进程往管道的写端写入数据,然后再从管道的读端将数据读出来。
程序运行结果如下:
Hello, world
[2] 父进程写,子进程读
将父进程调用fork()后的一段代码改为:
if (pid > 0) {//In parent progresswrite(fd[1], MSG, strlen(MSG) );wait(NULL);} else {//In child progressn= read(fd[0], pp_content, MAXPPSIZE);write(STDOUT_FILENO, pp_content, n);}
根据fork创建子进程的机制,父子进程同时拥有管道的读、写端描述符。程序运行结果如下:
Hello, world
这个Hello world是子进程读出来并输出来的。
[3] 父进程写,子进程读,子进程读
将父进程调用fork()后的一段代码改为:
if (pid > 0) {//In parent progresswrite(fd[1], MSG, strlen(MSG) );wait(NULL);n= read(fd[0], pp_content, MAXPPSIZE);write(STDOUT_FILENO, pp_content, n);} else {//In child progressn= read(fd[0], pp_content, MAXPPSIZE);write(STDOUT_FILENO, pp_content, n);//Read againprintf("Child read again\n");n= read(fd[0], pp_content, MAXPPSIZE);write(STDOUT_FILENO, pp_content, n);}
程序运行结果如下:
Hello, world
Child read again
如果是在Linux字符界面下,可以看到光标在最后一行闪动,说明子进程阻塞。管道读端将数据读完后,再次从管道读端read就会遭遇阻塞。
[4] 父进程写子进程读,子进程写父进程读
将父进程调用fork()后的一段代码改为:
if (pid > 0) {//In parent progresswrite(fd[1], MSG, strlen(MSG) );wait(NULL);printf("After child, read pipe's data which child write in:\n");n= read(fd[0], pp_content, MAXPPSIZE);write(STDOUT_FILENO, pp_content, n);} else {//In child progressprintf("Read pipe's data which parent write in:\n");n= read(fd[0], pp_content, MAXPPSIZE);write(STDOUT_FILENO, pp_content, n);//Child progress write data for parent progresswrite(fd[1], MSG, strlen(MSG));}
程序运行结果如下:
Read pipe's data which parent write in:
Hello, world
After child, read pipe's data which child write in:
Hello, world
可见,在这个例子里面,父子进程至少可以用管道实现一个轮回的相互通信。
3 利用FIFO实现进程通信
(1) 创建FIFO文件
用mkfifo 来创建一个FIFO
mkfifo fifo
ls -l fifo
prw-r--r-- 1 lly lly 0 Aug 10 16:10 fifo
(2) FIFO进程通信代码
/*Filename:fifo.c *Brife:Two progress communicate by fifo *Author:One fish *Date:2014.8.10 Sunday */#include <unistd.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#defineFIFO_FILE"fifo"#defineMSG"Hello, world\n"#define MAX_CT20int main(void){int fd;pid_tpid;fd= open(FIFO_FILE, O_RDWR);if (-1 == fd) {perror("open");exit(1);}write(fd, "HW\n", 3);if ( ( pid = fork() ) < 0 ) {perror("fork");exit(1);}if (pid > 0) {//Parent write(fd, MSG, strlen(MSG));wait(NULL);} else {//Childint n;char buf[MAX_CT];n= read(fd, buf, MAX_CT);write(STDOUT_FILENO, buf, n);}return 0;}
程序运行结果:
HW
Hello, world
在父进程中写write(fd, "HW\n",3);语句主要是为了测试如果子进程先运行的情况。手动多运行了几次都是以上运行结果(虽然很可能是碰巧)。
[2014.8.10– 15.05 --- 2014.8.10– 17.15]
- 进程通信 [ fork() 管道( pipe() ) FIFO ]
- 进程间通信(一): 管道(pipe+fifo)
- linux进程间通信之管道(pipe与fifo)
- 进程间通信---------有名管道(named pipe/FIFO)
- 【Linux】进程间通信之管道pipe与FIFO
- linux进程间管道通信pipe与fifo
- 进程间的通信—管道pipe和fifo
- fork产生子进程利用pipe管道通信
- fork产生子进程利用pipe管道通信
- fork创建子进程利用pipe管道通信
- fork()+pipe() --> 父子进程间通过管道通信
- fork创建子进程利用pipe管道通信
- 进程通信----管道(pipe)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- 进程间通信IPC之--无名管道(pipe)和有名管道(fifo)
- sdfsdfsdf
- Windows环境下进行Nginx安装和配置的介绍
- 控制台应用程序转成MFC程序错误—OcrRec.exe触发一个触点,原因可能是堆被破坏
- URAL 1109 简单二分图匹配
- hadoop默认参数
- 进程通信 [ fork() 管道( pipe() ) FIFO ]
- 使用QT实现一个图像处理软件1 —— 图片的加载和显示
- 最小堆与最大堆
- AcitonBar自定义布局
- Populating Next Right Pointers in Each Node
- Optimization Algorithms
- 疯狂html5讲义(二):HTML5简的常用元素与属性(三):html5新增的常用元素
- 新浪微博推送API
- phpmyadmin导入数据库大小限制修改