一.管道:
1.特点:单向、半双工、缓冲区大小受限制、传输的是无格式的字节流
2.无名管道和有名管道
2.1 无名管道:只能用于有亲缘关系的进程使用(父子进程或兄弟进程)
2.1.1 函数原型:无名管道
#include<unistd.h>
//fd[0]只能用于读,fd[1]只能用于写,一般文件的 1/0 函数都可以用于管道, 如 close、 read、 write.
int pipe(int fd[2]);
2.2 有名管道:它提供一个路径名与之关联,以FIFO的形式存在于文件系统中;可以使互不相关的两个进程间实现彼此通信;该管道可 以通过路径名来指出,并且在文件系统中是可见的。 在建立了管道之后,两个进程就可以把 它当作普通文件一样进行读写操作,使用非常方便;FIFO 严格地遵循先进先出规则,对 管道及 FIFO 的读操作总是从开始处返回数据,对它们的写操作则是把数据添加到末尾
2.2.1 函数原型:有名管道
#include <sys/types.h>
#include <sys/stat.h>
/*
该函数的第一个参数是一个普通的路径名,也就是创建后 FIFO 的名字。
第二个参数与 打开普通文件的 open()函数中的 mode 参数相同。
如果 mkfifo 的第一个参数是一个已经存在 的路径名时,会返回 EEXIST 错误;
所以一般典型的调用代码首先会检查是否返回该错误, 如果确实返回该错误,
那么只要调用打开 FIFO 的函数就可以了。一般文件的 I/O 函数都可 以用于 FIFO,如 close、 read、 write 等。
*/
int mkfifo (canst char * pathname, mode_t mode)
二.消息队列
1.特点:消息队列用于运行于同一台机器上的进程间通信,它和管道很相似,是一个在系统内核 中用来保存消息的队列,它在系统内核中是以消息链表的形式出现。 解耦、异步、顶峰消除
2.函数原型
2.1 创建新消息队列或取得已存在消息队列
/*
1.key 可以认为是一个端口号,也可以由函数 ftok 生成。
2.msgflg 如果等于IPC_CREAT,若没有该队列,则创建一个并返回新标识符,若己存在则返回原标识符;
msgflg 如果等于 IPC_EXCL,若没有该队列,则返回-1 ;若已存在,则返回 O;
*/
int msgget(key_t key,int msgflg) ;
2.2 向队列读 /写消息
//msgrcv 从队列中取用消息:
ssize_t msgrcv(int msqid , void *msgp, size_t msgsz, long msgtyp, int msgflg);
//msgsnd 将数据放到消息队列中:
int msgsnd(int msqid, const void *msgp, size_t msgsz,int msgflg);
参数中:
① msqid 是消息队列的标识码;
② msgp 是指向消息缓冲区的指针。 此位置用 来暂时存储发送和接收的消息,是一个用户可定义的通用结构,但一般定义为以下结构:
struct msgstru{
long mtype; //大于0
char mtext[512];
}
msgsz 是指消息的大小;
msgtyp 是指从消息队列内读取的消息形态。 如果值为零,则表 示消息队列中的所有消息都会被读取。
msgflg :用来指明核心程序在队列没有数据的情况下所应采取的行动。 如果 msgflg 和常 数 IPC_NOWAIT 合用,则在 msgsnd 执行时若是消息队列己满,则 msgsnd将不会阻塞, 而会立即返回;如果执行的是 msgrcv,则在消息队列呈空时,不做等待马上返回-1 ,并设定错误码为 ENOMSG。当 msgflg 为 0 时, msgsnd及 msgrcv在队列呈满或呈空的情形 时,采取阻塞等待的处理模式。
2.3 设置消息队列属性
/*
参数中的 msgctl 系统调用对 msgqid 标识的消息队列执行 cmd 操作,
系统定义了 3 种 cmd 操作: IPC_STAT、 IPC_SET、 IPC_RMID。
IPC_STAT 用来获取消息队列对应的 msqid_ds 数据结构, 并将其保存到 buf指定的地址空间;
IPC_SET 用来设置消息队列的属性,要设置的属性 存储在 buf 中;
IPC_RMID 用来从内核中删除 msqid 标识的消息队列。
*/
int msgctl(int msgqid, int cmd, struct msqid_ds *buf);
3.对比
消息队列跟有名管道有不少的相同之处,消息队列进行通信的进程可以是不相关的进程,同时它们都是通过发送和接收的方式来传递数据的。 在命名管道中, 发送数据用 write 函数,接收数据用 read 函数,则在消息队列中,发送数据用 msgsnd 函数, 接收数据用 msgrcv 函数。 而且它们对每个数据都有一个最大长度的限制。 与命名管道相比,消息队列的优势在于: ①消息队列可以独立于发送和接收进程而存 在 ②可以同时通过发送消息 以避免命名管道的同步和阻塞问题,而不需要由进程自己来提供同步方法; ③接收程序可以 通过消息类型有选择地接收数据,而不是像命名管道中那样,只能默认地接收。
三.共享内存
1.特点:共享内存就是允许两个不相关的进程访问同一个逻辑内存。 共享内存是在两 个正在运行的进程之间共享和传递数据的一种非常有效的方式。 不同进程之间共享的内存通 常安排在同一段物理内存中。 进程可以将同一段共享内存连接到它们 自己的地址空间中,所 有进程都可以访问共享内存中的地址。 而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内 存的任何其他进程。 不过,共享内存并未提供同步机制,也就是说,在第一个进程对共享内存的写操作结束 之前,并无自动机制可以阻止第二个进程对它进行读取。 所以通常需要用其他的机制来同步 对共享内存的访问。
2.函数原型
2.1 创建共享内存
#include <sys/shm.h>
/*
第一个参数,程序需要提供一个参数 key (非 0 整数),它有效地为共享内存段命名, shmget 函数运行成功时会返回一个与 key 相关的共享内存标识符(非负整数),用于后续的共 享内存函数;调用失败返回-1。 不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某 个资源,程序对所有共享内存的访问都是间接的。 程序先通过调用 shmget 函数并提供一个 键,再由系统生成一个相应的共享内存标识符(shmget 函数的返回值)。
第二个参数, size 以字节为单位指定需要共享的内存容量。
第三个参数, shmfl.g 是权限标志,它的作用与 open 函数的 mode 参数一样,如果要想在 key 标识的共享内存不存在的条件下创建它的话,可以与 IPC_CREAT 做或操作。 共享内存 的权限标志与文件的读写权限一样,举例来说, 0644 表示允许一个进程创建的共享内存被内 存创建者所拥有的进程向共享内存读取和写人数据,同时其他用户创建的进程只能读取共享 内存
*/
int shmget(key_ t key, int size, int flag) ;
2.2使用共享内存
/*
shmid 为 shmget 函数返回的共享存储标识符;
addr 和 flag 参数决定了以什么方式来确定连接的地址;
函数的返回值即是该进程数据段所连接的实际地址,其他进程可以对此进程进行读写操作。
*/
void *shmat(int shmid, void *addr, int flag) ;
2.3分离共享内存
/*
shmdt 函数用于将共享内存从当前进程中分离。
注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。
参数 shmaddr 是 shmat 函数返回的地址指针,调用成功时返回 0,失败时返回-1。
*/
int shmdt(const void *shmaddr) ;
3.优缺点
( 1 )优点:使用共享内存进行进程间的通信非常方便,而且函数的接口也简单,数据的 共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。 同时,它也不 像无名管道那样要求通信的进程有一定的父子关系。
(2 )缺点:共享内存没有提供同步的机制,这使得在使用共享内存进行进程间通信时, 往往要借助其他的手段来进行进程间的同步工作。
四.信号量
1.特点:可以解决通信的同步问题
2.函数原型
2.1信号量的创建
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
/*
该函数执行成功返回信号量标示符,失败则返回-1。
参数 key 是函数通过调用ftok得到的键值,nsems 代表创建信号量的个数,如果只是访问而不创建则可以指定该参数为 0 ;但一旦创建了该信号量,就不能更改其信号量个数。 只要不删除该信号量,就可以重新 调用该函数创建该键值的信号量,该函数只是返回以前创建的值,而不会重新创建。
semfig指定该信号量的读写权限, 当创建信号量时不许加 IPC_CREAT,若指定 IPC_CREAT|IPC_EXCL后创建时发现存在该信号量,创建失败。
*/
int semget(key_ t key, int nsems , int semflg) ;
2.2信号量的改变
/*
sem_id 是由 semget 返回的信号量标识符, sembuf结构的定义如下:
struct sembuf
{
short sem_num; //除非使用一组信号量,否则它为 O
short sem_op; //信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即 p (等待)操作,一个是+1,即 v (发送信号)操作。
short sem_flg; //通常为 SEM_UNDO,使操作系统跟踪信号,并在进程没有释放该信号量而终止时,操作系统释放信号量
*/
int semop(int semid, struct sembuf *sops , unsigned nsops);
2.3信号量的控制
/*
如果有第 4 个参数,它通常是一个 union semum 结构,定义如下:
union semun{
int val;
struct semid_ds *buf;
unsigned short *arry;
}
前两个参数与前面一个函数中的一样, cmd 通常是 SETVAL 或 IPC RMID。 SETVAL 用来把信号量初始化为一个己知的值。 p 值通过 union semun 中的 val 成员设置,其作用是 在信号量第一次使用前对它进行设置。 IPC_RMID 用于删除一个已经无须继续使用的信号量 标识符。
*/
int semctl (int semid, int semnum, int cmd, ... ) ;