Linux进程间通信
linux下进程间通信的几种主要手段简介:
- 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;
- 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数);
- 报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
- 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
- 信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
- 套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。
-------------------------------------------------------------------------------
1. 管道。
匿名一次性使用的,半双工。一个进程往输出端写管道,另一个进程从输入端读管道。
#include
int pipe(int fd[2]);
fd[0]:表示读端
fd[1]:表示写端
2. FIFO
有名管道,作为特别文件存储于文件系统中。有名管道一旦建立就存在于文件系统中,除非显示的unlink
#include
#include
int mknod(const char *path,mode_t mod,dev_t dev);
int mkfifo(const char *path,mode_t mode);
path:创建有名管道的全路径名
mod:创建有名管道的模式,指存取权限
dev:设备值,该值取决于文件创建的种类,它只在创建设备文件时才会用到
注意:有名管道创建后就可以使用了,有名管道和管道的使用方法基本是相同的。只是使用有名管道的时候必须先调用open()将其打开
因为有名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊文件
3. 消息队列、信号量和共享内存
函数 msgget semget shmget msgctl semctl shmctl
消息队列、信号量和共享内存都是ipdc资源
每个ipc资源有两个唯一的标志与其相连 关键字和标识(关键字类似于文件名,标志类似于文件描述字用来访问资源的操作)
关键字获得函数
#include
#include
key_t ftok(const char * pathname,int proj_id);
ipc_perm{ //结构,当进程创建ipc资源时,内核存储了ipc资源的属主和组id和访问权限等信息。
uid_t uid //当前有效用户id
gid_t gid //当前有效组id
cuid_t cuid //创建用户id
cgid_t cgid //创建组id
mode_t mode //访问权限
};
ipc资源创建之后,可以长期存储在文件系统中,直到被删除或者系统重启。
shell命令:ipcs -q -s -m ipcrm
A、消息队列
struct msqid_ds { //消息队列相连的数据结构
struct ipc_perm msg_perm; //该消息队列的属主和访问权限
struct msg* msg_first; //指向第一个消息的指针
struct msg* msg_last; //指向最后一个消息的指针
msgqnum_t msg_qnum; //当前消息的个数
msglen_t msg_qytes; //最大容量字节数
pid_t msg_lspid; //最后一个调用msgsnd的进程
pid_t msg_lrpid; //最后一个调用msgrcv的进程
time_t msg_stime; //最后发送时间
time_t msg_rtime; //最后接收时间
time_t msg_ctime; //最后修改时间
}
struct msg {
struct msg* mas_next; 下个消息的指针
long msg_type; 消息的类型
caddr_t msg_sport; 消息正文地址
sort msg_ts; 消息正文的大小
}
int msgget(key_t key, int msgflg);
key:为ftok函数的返回值
msgflag:标志参数,可取值为IPC_CREATE,IPC_EXCL
IPC_CREATE:当系统不存在和key相连的消息队列时,就创建一个key为关键字的消息队列(用于创建一个新的队列)
IPC_EXCL: 和IPC_CREAT一起使用时,当已经存在队列时,则返回失败(防止关键字重复)
int msgctl(int msgid, int cmd, struct msqid_ds* buf);
控制消息队列cmd
IPC_STAT 获得msgid的消息队列数据结果到buf中
IPC_SET 设置buf中ipc_perm为消息队列的新值
IPC_RMID 删除消息队列
int msgsnd(int msgid, const void* msg, size_t size, int msgflag);
mysgid:消息队列标识符
msg:指向发送的消息
size:要发送消息的大小,不包含消息类型占用的4个字节
msgflag:操作标志位,可以设置为0或者IPC_NOWAIT
0:当消息队列已满的时候,msgsnd将会阻塞,直到消息可以写进消息队列
IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回。
msgsnd()为阻塞函数,当消息队列容量满或者消息个数满会阻塞,如果消息队列被删除,则EIDRM错误,被信号中断E_INTR。
如果设置IPC_NOWAIT会返回-1并且置EAGAIN错误
ssize_t msgrcv(int msgid, void* msg, size_t size, long int msgtyp, int msgflag);
size实际上是指正文段的大小,总大小应该是size+4字节,接收是同样的意思
msgtyp:请求读取的消息类型。
msgflag: 操作标志位,可以为IPC_NOWAIT,IPC_EXCEPT,IPC_NOERROR
IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG;
IPC_EXCEPT:与msgtyp配合使用返回队列中第一个类型不为msgtyp的消息
IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将被丢弃
long int msgtyp if == 0 接收第一个消息
if > 0 接收类型等于msgtyp的第一个消息
if < 0 接收类型等于或者小于msgtyp绝对值的第一个消息
B、信号量
struct semid_ds {
struct ipc_perm sem_perm; 包含信号量资源的属主和访问权限
struct sem *sem_base; 指向信号量集合的指针
unsigned short int sem_nsems; 集合中信号的个数
time_t sem_otime; 最后一次操作的时间
time_t sem_ctime; 最后一次修改的时间
}
struct sem {
unsigned short semval; 当前信号量值
pid_t sempid; 最后修改信号量的进程
unsignde short semcnt; 等待进行p操作的进程数
unsigned short semzcnt; 等待semval为0的进程数
}
int semget(key_t key, int semnum, int flag)
key:ftok函数的返回值
semnum:要创建的信号集包含的信号个数,如果只是打开信号集,其值为0即可
flag:标志参数,可取值为IPC_CREATE,IPC_EXCL,IPC_CREATE|IPC_EXCL
IPC_CREATE:当系统不存在和key相连的消息队列时,就创建一个key为关键字的消息队列(用于创建一个新的队列)
IPC_EXCL: 和IPC_CREAT一起使用时,当已经存在队列时,则返回失败(防止关键字重复)
信号量操作
int semop(int semid, struct sembuf* buf, int bufsize);
semid:信号集的标志符
buf:指向进行操作的结构体数组首地址
bufsize:指出将要进行操作的信号的个数
struct sembuf {
short semnum; 信号量集合中的信号量编号
short val; val>0进行v操作加val,表示进程释放控制的资源;
val<0进行p操作减val,若没有设置IPC_NOWAIT,则调用进程阻塞,直到资源可用;否则进程直接返回EAGAIN
val=0时阻塞等待信号量为0;若没有设置IPC_NOWAIT,调用进程进入睡眠状态,直到信号值为0;否则进程不会睡眠,直接返回EAGAIN
short flag; IPC_NOWAIT IPC_UNDO当执行pv操作之后,进程突然终止没有进行相应的vp操作
}buf;
semctl(int semid, int semnum, int cmd, union semun arg) 可以为信号量初始值,删除信号量设置权限等
semid:信号集标志符
semnum: 为信号量集合中的某个信号量操作
cmd: SETVAL,IPC_STAT,IPC_SET,SETALL,GETALL,IPC_INFO,IPC_RMID
union semun {
short val; 设置信号量的值SETVAL
struct semid_ds* buf; 设置或者获得semid_ds结构IPC_STAT,IPC_SET
unsigned short* array; 指向信号量的数组,用于集体初始化SETALL,GETALL
struct seminfo *buf; 为控制IPC_INFO提供的缓存
} arg;
IPC_SET:对信号集的属性进行设置
IPC_RMID:把semid指定的信号集从系统中删除
GETPID:返回最后一个执行semop操作的进程的ID
GETVAL:返回信号集中semnum指定信号的值
GETALL:返回信号集中所有信号的值
GETNCNT:返回正在等待资源的进程的数量
GETZCNT:返回正在等待完全空闲资源的进程数量
SETVAL:设置信号集中semnum指定的信号的值
SETALL:设置信号集中所有信号的值
C、共享存储
shmget(key_t key, int size, int flag)
key:ftok返回的值
size:以字节为单位指定内存的大小
flag:标志参数,可取值为IPC_CREATE,IPC_EXCL,IPC_CREATE|IPC_EXCL
IPC_CREATE:当系统不存在和key相连的消息队列时,就创建一个key为关键字的消息队列(用于创建一个新的队列)
IPC_EXCL: 和IPC_CREAT一起使用时,当已经存在队列时,则返回失败(防止关键字重复)
size为n时表示获得或者创建这么大的共享段
size为0时表示直接获得整个共享段
size大于创建的共享段时,返回错误
(创建时指定n, 获得时一般直接为 0)
struct shmid_ds {
struct ipc_perm shm_perm;
size_t shm_segsz 大小
pid_t shm_pid 最后操作的进程id
pid_t shm_cpid 创建的进程id
shmatt_t shm_nattch 当前连接数
time_t shm_atime 最后调用shmat的时间
time_t shm_dtime 最后调用shmdt的时间
time_t shm_ctime 最后调用shmctl的时间
}
void* shmat(int shmid, const void* shmaddr, int flag);
shmid:共享内存区的标志符
shmaddr一般为0,内核自动为程序一个连接到共享内存段的地址返回给进程;共享内存的附加点
flag SHM_RDONLY SHM_RND(当shmaddr设置为自己的值时,shm_rnd标志设置后,将会对这个地址做适当的截断以适合系统的对齐)
参数shmaddr不同取值的含义如下:
如果为空,则由内核选择一个空闲的内存区;如果非空,返回地址取决于调用者是否给flag参数指定了SHM_RND值,如果没有指定,则共享内存区附加到由shmaddr指定
的地址;否则附加地址为shmaddr向下舍入一个共享内存低端边界地址后的地址(SHMLBA,一个常址)
通常将参数shmaddr设置为NULL
int shmdt(const void* shmaddr); //断开与共享内存区的联系
shmaddr:shmat的返回值
只有当shm_nattch为0后,即没有任何进程再使用该共享内存区,共享内存区才在内核中被删除,一般来说,当一个进程终止时,他所附加的共享内存区都会自动脱离
shmctl(int shmid, int cmd, struct shmid_d* buf);
shmid:共享内存区的标志符
cmd:IPC_STAT IPC_SET IPC_RMID SHM_LOCK SHM_UNLOCKroot用户专用
buf:指向shmid_ds结构体的指针
阅读(1087) | 评论(0) | 转发(0) |