进程间通信 (InterProcess Communication,IPC)
原因:由于不同进程地址空间也不同,A进程无法访问B进程的地址空间。任何一个进程的全局变量也无法被其他进程所见,因此,进程间通信必须通过内核。在内核中开辟一块缓冲区,A进程把数据从用户空间拷贝到内核缓冲区,B进程从内核缓冲区把数据读走,这便是IPC。
管道(PIPE)
1.PIPE是UNIX系统IPC最古老的形式,基本所有UNIX系统都支持这种机制。
2.历史上,PIPE是半双工的,现在某些系统提供了全双工PIPE,但是为了最佳的可移植性,我们不能假设系统支持全双工。
3.PIPE只能在具有血缘关系的两个进程之间使用。通常一个进程创建PIPE,fork之后的子进程和父进程之间就可以通过PIPE通信。
4.PIPE提供流式服务
5.PIPE生命周期和进程同步,进程结束则PIPE也消失了(即 匿名管道)
6.内核会对PIPE进行同步互斥
#include <unistd.h>
int pipe(int fd[2]);
若成功,则返回0,若出错,则返回-1
fd[0]为读而打开
fd[1]为写而打开
因为管道是单向通信的,所以父子进程之间必须一个关闭读端,一个关闭写端。
如果管道的一端被关闭,则下面两条规则起作用:
1.当read一个写端已经被关闭的管道,在所有数据被读取之后,read返回0,表示文件结束。
2.如果write一个读端已被关闭的管道,则产生信号SIGPIPE。如果忽略该信号或者捕捉该信号并从其处理程序返回,则write返回-1,errno被设置为EPIPE。
在写管道时,常量PIPE_BUF规定了管道缓冲区的大小。如果对管道调用write,而且写的字节数小于或者等于PIPE_BUF,则此操作不会与其他进程对同一管道的write操作交叉进行。但是,若是有多个进程同时写一个管道,而且写的数据大于PIPE_BUF,那么我们所写的数据可能会和其他进程所写的数据相互交叉。
PIPE_BUF的值可以用pathconf或者fpathconf函数获取。
代码示例 :父进程写管道,子进程读管道
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
int fd[2] = {0};
pid_t pid;
char msg[32] = {0};
int nBytes = 0;
pipe(fd);
if ((pid = fork())<0){
printf("fork error\n");
}else if(pid>0){/*parent*/
close(fd[0]);
write(fd[1], "This is father\n", 32);
}else{/*child*/
close(fd[1]);
nBytes = read(fd[0], msg, 32);
if (nBytes>0)
printf("child recv %s\n", msg);
}
exit(0);
}
函数popen和pclose
#include <stdio.h>
FILE *popen(const char *cmdstring, const char *type);
若成功,返回文件指针,若失败,返回NULL
int pclose(FILE *fp);
若成功,返回cmdstring的终止状态,若出错,返回-1
函数popen先执行fork,然后调用exec执行cmdstring,并且返回一个标准I/O文件指针。
如果type是“r”,则文件指针连接到cmdstring的标准输出;
如果type是“w”,则文件指针连接到cmdstring的标准输入。
cmdstring由BSHELL以下列方式执行:
sh -c cmdstring
可以使用:fp = popen("ls *.c", "r");
命名管道|有名管道(FIFO)
匿名管道只能在有血缘关系的进程之间传递数据,而有名管道可以在不相关的进程之间使用。
#include <sys/stat.h>
int mkfifo(const char *path, mode_t mode);
int mkfifoat(int fd, const char *path, mode_t mode);
若成功,返回0,若失败,返回-1
当我们创建FIFO时,都需要用open来打开它,正常的文件I/O函数如write read close unlink都需要FIFO。
当open一个FIFO时,非阻塞标志(O_NONBLOCK)会产生下列影响:
1.在一般情况下,只读open要阻塞到某个其他进程为写而打开这个FIFO为止。类似的,只写open要阻塞到某个其他进程为读而打开它为止。
2.如果指定了O_NONBLOCK,则只读open立即返回。但是,如果没有进程为读而打开一个FIFP,那么只写open将返回-1,并将errno设置为ENXIO。
参考书目《APUE》