【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
1.无名管道
定义:
无名管道是一种特殊类型的文件,在内核空间中对应的资源即是一段内存空间,内核在这段空间以循环对列的方式临时存入一个进程发送给另一个进程的信息,这段内核空间完全由操作系统管理和维护,应用程序只需要,也只能通过系统调用来访它。
无名管道和普通的文件有很大的差异:
- 无名管道的内核资源在通信两进程退出后会自动释放。跟普通文件不同。
- 存储大量常规信息,但是编程方式,具有和普通文件一样的特点,可以使用read/write等函数进行读写操作,只是注意:特殊的文件只能用文件IO操作。
- 读写的特点有一定的差异,另外,不能用lseek函数来修改当前的读写位置,因为FIFO需要满足FIFO的原则。
特点:
- 使用条件:只能用于具有亲缘关系(父子进程,兄弟进程等)的进程之间的通信
- 通信模式:半双工模式,fd[0]作为读端,fd[1]作为写端
- 读写方式:对于它的读写采用文件IO(不支持lseek函数)
- 读操作会阻塞(等待):在管道中无数据情况下。写操作会阻塞(等待):当管道被写满时,无名管道的大小为64K
- 管道破裂:管道读端关闭,再向管道中写数据时,即向管道中写入数据的进程将收到内核传来的SIGPIPE信号。
函数接口:
/*
* 函数的功能:在内核空间创建一个无名管道
* 参数:pipefd 固定的读写端 pipefd[0]---无名管道读端,
* pipefd[1]---写端
* 返回值:成功返回0,失败-1
*/
#include <unistd.h>
int pipe(int pipefd[2]);
无名管道的4种特殊形式:
-
以阻塞的方式读无名管道:
如果当前没有进程(包括当前进程)访问写端,读操作立即返回,管道中无数据:立即返回0
注意:如果写端关闭,并且管道中实现没有数据的话,读端没有任何意义。
-
以阻塞的方式读无名管道,如果某个进程(包括当前进程)可以访问写端,管道端阻塞(等待)
注意:读写端打开,当前管道中没有数据,阻塞等待。
-
如果以阻塞的方式写无名管道,如果没有某个进程(包括当前进程)可以访问读端,写操作会收到SIGPIPE信号,write函数返回-1,如果当前有某进程可以访问读端,且管道中有空间,则写入成功。
-
如果以阻塞的方式写无名管道,如果当前管道已经满,则阻塞等待当前进程,如果有多个进程试图写,当进程读管道唤醒写操作时 ,唤醒哪个进程未知,需要多个进程同时写入时,需要相应的避免竞争机制。
注意:无名管道只有所有进程都结束的时候,管道内的资源才完全释放,否则一直存在
2.有名管道
定义:有名管道:有自己的名字,但是有名管道名称保存在磁盘上,但是内容保存在内核中,有名管道和普通的文件一样具有磁盘存放路径,文件的权限和其他的属性信息,但是有名管道和普通文件又有区别,有名管道没有在磁盘上存真正的信息,而是在内存中存放,2个进程结束后自动丢失,通信结束后有名管道的文件路径本身存在,这是和无名管道区别的地方。
特点:
- 有名管道可以使互不相关的两个进程互相通信。
- 有名管道可以通过路径名来指出,并且在文件系统中可见。
- 读写方式:对于它的读写采用文件IO(不支持lseek函数)
- 其它与无名管道一样
- 有名管道读端写端不固定。一段读,另一端写。当然某一端既可以读也可以写,要用父子进程实现。
函数接口:
/*
* 函数的功能:创建一个有名管道
* 参数:pathname 有名管道的路径加名称
* mode 打开权限 0664
* mode & ~umask
* 返回值:成功返回0,失败-1
*/
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
有名管道的4种特殊形式:
- 如果希望以写的方式打开管道,则需要另一进程以读的方式打开管道。即如果以某种方式打开有名管道,则系统将阻塞进程,直到另一个进程(包括自己)以另一种方式打开该管道后才会继续运行,显然一个进程可以通过可读可写的方式打开管道,当前进程充满了读写2个身份,此时进程入会阻塞。
- 两进程已经完全打开管道,阻塞读操作,无数据,阻塞读,有数据,读出来
- 两进程已经完成打开管道的操作,阻塞写操作,管道中没空间,阻塞,有空间写入,写满阻塞。
- 两进程已经完全打开管道操作,中途其中一个进程退出,未退出一端是写操作,将返回SIGPIPE信号,未退出一端是读操作,读操作将不再阻塞,直接返回0(父子进程)
3.信号---进程间唯一的一种异步通信方式
函数接口:
/*
* 函数的功能:向执行的进程发送信号
* 参数:pid 执行的进程号
* pid <-1 发送当前的进程|id|等于调用进程组id下的任意一个子进程
* pid=-1 发送给所有的进程,除了init(1号进程)
* pid =0 发送给同组下的所有进程
* pid >0 发送给指定的进程
* sig 指定的信号(kill -l)
* 返回值:成功返回0,失败-1;
*/
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
4. 消息队列
消息对列是消息的链式队列,消息队列的FIFO原则仅仅适用于同类型的消息,在消息队列进行如下规定,不同系统的限制值可以通过msgctl函数使用IPC_INFO参数获得,注意不同的linux版本此值不同。消息队列即满足的管道的先进先出的原则,用户又可以按照指定的消息类型去存取,消息队列存在于内存当中,在使用消息队列的时候,通过指定消息对列的标识符。默认的情况下:整个系统最多允许有16个消息队列。每个消息对列最大为16384字节 消息对列中每个消息最大为8192字节。
消息对列的使用步骤:
- ftok----使外部不相关的进程建立联系
- msgget---创建一个消息队列
- msgctl---删除消息队列
- msgsnd--发送消息
- msgrcv----接收消息
5. 共享内存-----进程间最高效的通信方式
定义:共享内存进程间通信机制主要用于实现进程间的大量数据的传输。共享内存是在内存中单独开辟的一段内存空间,这段内存空间有自己特有的数据结构,包括访问权限,大小和最近的访问的时间。
两个进程在使用此共享内存空间之前,需要在进程地址空间和共享内存空间之间建立联系,即将共享内存空间挂载到进程中。在使用共享内存进行数据存取时,有必要使用二元信号量来同步两个进程以实现对共享内存的写操作。
/*
* 函数的功能:创建或者打开一个共享内存
* 参数:key 外部键值
* size 设置共享内存大小
* shmflg 标志位 IPC_CRAT (flag)
IPC_EXCL 0664
* 返回值:成功返回共享内存的标识符 失败:-1
*/
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
/*
* 函数的功能:将共享内存映射到各自进程空间
* 参数:shmid 进程标识符
* shmaddr 映射后进程的地址
* shmflg 0 可读可写
* SHM_RDONLY 只读
* 返回值:成功返回映射后地址,失败NULL===(void *)-1
*/
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
/*
* 函数的功能:解除映射
* 参数:shmaddr 映射的地址
* 返回值:成功返回0,失败-1
*/
int shmdt(const void *shmaddr);
/*
* 函数的功能:删除共享内存
* 参数:shmid 共享内存的标识符
* cmd IPC_STAT :得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中.
* IPC_SET :改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内.
* IPC_RMID :删除这片共享内存。
* buf 共享内存的属性信息 NULL
* 返回值:IPC_RMID 成功返回0,失败-1
*/
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
6.信号灯集----同步互斥机制