进程间的用户地址空间是相互独立的,所以进程间通信必须要通过内核,在内核空间中开辟一块缓冲区,进程1把用户空间的数据copy到内核缓冲区,进程2再从内核缓冲区中将数据copy到属于自己的用户空间,这就是进程间的通信。(IPC)
一、管道
(a)无名管道
/**
*input:文件描述符数组
*return 大于0等于创建成功
*/
int pipe(fddes[2]);//在内核中开辟管道
pid=fork();//创建子进程,将父进程创建的无名管道clone到子进
程中,读写端口的文件描述符也是一样的
无名管道是有血缘关系进程间的通信手段,通过以上的函数在内核中创建环形队列来进行半双工的读写通信;fddes[0]指向管道的读端口,fddes[1]指向管道的写端口。
#include <stdio.h>
#include <unistd.h>
#define MAXLINE 80
int main(void){
int fd[2];
pid_t pid;
char line[MAXLINE];
if(pid=pipe(fd)<0){
printf("error");
exit(1);
}
if(pid=fork()>0){
close(fd[0]);//关闭父进程的读端
write(fd[1],"Hello World!\n",12);
wait(NULL);
}else{
close(fd[1]);//关闭子进程的写端
int n=read(fd[0],line,MAXLINE);
write(STDOUT_FILENO,line,n);
}
}
管道里面是字节流,父子进程都写、都读,就会导致内容混在一起,对于读管道的一方,解析起来就比较困难。常规的使用方法是父子进程一方只能写入,另一方只能读出,管道变成一个单向的通道,以方便使用。如图下图所示,父进程放弃读,子进程放弃写,变成父进程写入,子进程读出,成为一个单向通道。

(b)有名管道(FIFO)
/**
*p1 磁盘文件路径名
*p2 权限
**/
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
创建有名管道使得无血缘进程间得以通信,有名管道严格遵循先进先出(first in first out)。有名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。FIFO内核实现时可以支持双向通信。(pipe单向通信,因为父子进程共享同一个file结构体)

二、共享内存(MMAP)
mmap可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存地址,对文件的读写可以直接用指针来做而不需要read/write函数。

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
如果addr参数为NULL,内核会自己在进程地址空间中选择合适的地址建立映射。如果addr不是NULL,则给内核一个提示,应该从什么地址开始映射,内核会选择addr之上的某个合适的地址开始映射。建立映射后,真正的映射首地址通过返回值可以得到。len参数是需
要映射的那一部分文件的长度。off参数是从文件的什么位置开始映射,必须是页大小的整数倍(在32位体系统结构上通常是4K)。filedes是代表该文件的描述符。
prot参数有四种取值:
- PROT_EXEC表示映射的这一段可执行,例如映射共享库
- PROT_READ表示映射的这一段可读
- PROT_WRITE表示映射的这一段可写
- PROT_NONE表示映射的这一段不可访问
flag参数有很多种取值,这里只讲两种,其它取值可查看mmap(2)
- MAP_SHARED多个进程对同一个文件的映射是共享的,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。
- MAP_PRIVATE多个进程对同一个文件的映射不是共享的,一个进程对映射的内存做了修改,另一个进程并不会看到这种变化,也不会真的写到文件中去。
如果mmap成功则返回映射首地址,如果出错则返回常数MAP_FAILED。当进程终止时,该进程的映射内存会自动解除,也可以调用munmap解除映射。munmap成功返回0,出错返回-1。