待我慢慢总结..........
参考文章:
[1] 进程间的五种通信方式介绍
[2] 《嵌入式Linux开发教程(上册)》
进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。
IPC的方式通常有管道(包括无名管道和命名管道)、共享存储、消息队列、信号量、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。
1. 管道
管道是一个进程连接数据流到另外一个进程的通道,它通常被用作把一个进程的输出通过管道连接到拎一个进程的输入。管道分为匿名管道和命名管道(FIFO:先进先出)。
1.1 匿名管道(无名管道)
特点:
- 匿名管道是一种无法在文件系统以任何方式看到的半双工管道。
- 匿名管道用于具有亲缘关系的进程之间的通信(父子进程或者兄弟进程之间)
- 它可以看成一种特殊的文件,但是不属于任何文件系统,只存在于内存之中
- 对匿名管道可以使用普通的read、write函数
pipe()函数可以用来创建一条匿名管道,它的原型如下:
#include <unistd.h>
int pipe(int pipefd[2]);
//pipefd[0] : 读端read
//pipefd[1] : 写端write
函数成功返回0,否则返回-1。
单个进程中匿名管道的使用:
单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 IPC 通道
fork进程之间的匿名管道通信:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
/* 父进程向管道写入字符串hello son :关闭读fd[0] : close
* 子进程读取管道,并且打印出所读取的字符串:关闭写fd[1] : close
* */
int main(){
pid_t proc;
int fd[2];
char buff[20];
if(pipe(fd) < 0)
printf("pipe create error!\n");
proc = fork();
if(proc < 0)
printf("create error!\n");
else if(proc > 0){
sleep(1);
close(fd[0]);
//sleep(1);
write(fd[1],"hello,son!\n",12);
printf("this is father!\n");
wait(0);
}
else{
//关闭写fd
sleep(1);
close(fd[1]);
//sleep(1);
read(fd[0],buff,20);
printf("this is son!\n");
printf("i get the string = %s\n",buff);
}
}
输出:
1.2 命名管道(FIFO:先进先出)
命名管道也被成为FIFO文件。
特点:
- 使同一主机的所有进程都可以相互通信。
- FIFO是一种特殊的文件类型,他在文件系统中以文件名的形式存在,在stat结构中,mode指明一个文件结点是不是命名管道
建立匿名管道
mkfifo()函数用来创建一个命名管道,它的原型如下:
#include <sys/types.h>
#incldue <sys/stat.h>
int mkfifo(char * pathname, mode_t mode);
- mkfifo()创建了一个真实存在于文件系统中的命名管道,参数pathname制定了文件名,参数mode制定了文件的读写权限。函数成功返回0,否则返回-1并设置error
- mkfifo()创建命名管道以后,需要通过命名管道通信的进程需要打开该管道文件,然后通过read(write)函数像操作普通文件一样进行通信。
删除命名管道
如果要删除一个使用完的FIFO文件,则需要unlink命令,示例如下:
Linux也提供了一个相应的unlink函数:Linux常用C函数
2. 共享存储
共享内存就是两个(或多个)进程共同占有一段内存空间,这些进程可以是有亲缘关系的进程,也可以是完全不相关的进程。同一块物理内存空间被映射到两个进程,两个进程都可以访问这段共享空间从而实现了进程间通信。但是值得注意的是:Linux的共享内存通信只提供了数据交换功能,并没有提供同步机制,需要使用其它机制来同步不同进程对共享内存的读写操作(如信号量)。
共享内存区设计四个主要步骤:
- 指定一个名字参数调用shm_open,以创建一个新的共享内存区对象(或打开一个已经存在的共享内存区对象);
- 调用mmap()把这个共享内存区映射到调用进程的地址空间
- 调用munmap()取消共享内存映射
- 调用shm_unlink()函数函数共享内存段
2.1 创建或者打开共享存储shm_open
函数原型:
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int shm_open(const char* name, int oflag, mode_t mode);
函数成功返回创建或打开的内存共享描述符,与文件描述符作用相同,否则返回-1
参数name:指定创建的共享内存名称,其他进程可以根据这个名称来打开共享内存
参数flag可以为以下值:
- O_RDONLY:共享内存以只读方式打开
- O_RDWR:共享内存以可读可写的方式打开
- O_CREAT:共享内存不存在时才创建
- O_EXCL:如果指定了O_CREAT,但共享内存已经存在时返回错误信息
- O_TRUNC:如果共享内存已经存在,则将其大小设置为0
参数mode只有指定O_CREAT才有效,指出共享内存的权限,与open()函数类似
注意:新创建或者打开的共享内存大小默认为0,需要设置大小才能使用。
2.2 设置共享内存大小
使用ftruncate()函数来调整文件或者共享内存的大小,原型如下:
#include <unistd.h>
#include <sys/types.h>
int ftruncate(int fd, off_t length);
函数成功返回0,失败返回-1
参数fd:需要调整的共享内存或者文件。
参数length:需要调整的大小(单位是字节)。
2.3 映射共享内存
创建共享内存之后,需要将这块内存区映射到调用进程的地址空间中,可以使用mmap()来完成,mmap()原型如下:
#include <sys/mman.h>
void * mmap(void *addr, size_t length, int prot, int flag, int fd, off_t offset);
函数成功返回映射后指向共享内存的虚拟地址,失败则返回MAP_FAILED值。
参数如下:
- addr:指向映射存储区的虚拟地址,通常将其设置为NULL,这表示由系统选择该映射区的起始地址
- len:映射的字节数
- port:对映射存取区的保护要求,对映射存储区的保护要求不能超过文件open模式的访问权限。他可以使用一下标志或值:
- PORT_READ:映射区可读;
- PORT_WRITE:映射区可写;
- PORT_EXEC:映射区可执行;
- PORT_NONE:映射区不可访问
- flag:映射标志位,可以为以下标志或值
- MAP_FIXED:返回值必须等于addr,不利于可移植性,故不推荐使用;
- MAP_SHARED:对进程对同一个文件的映射是共享的,一个京城对映射的内存做了修改,另一个进程也会看到这个变化;
- MAP_PRICATE:多进程对同一个映射区不是共享的,一个进程对映射的内存进行了修改,另外一个进程看不到这个变化;
- fd:要被映射的文件描述符或者共享内存的描述符
- offset:要映射字节在文件中的起始偏移量。
2.4 取消共享内存映射
在已经建立的共享内存映射,可以使用munmap()函数来取消。munmap()函数原型如下:
#include <sys/mman.h>
int munmap(coid *addr,size_t length);
函数成功返回0,否则返回-1.
- addr:munmap()函数的返回地址;
- length:映射的字节数
取消映射后在对映射地址对象访问会导致调用进程收到SIGSEGC信号。
2.5 删除共享内存
当使用完共享内存后,需要将其删除以释放系统资源,可以通过shm_unlink()函数完成,其原型如下:
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int shm_unlink(const char * name);
函数成功返回0,否则返回-1.
参数name为共享内存的名字。
2.6 实例
3. 消息队列
4. 信号量