进程间通信(总结)

转自:http://blog.sina.com.cn/s/blog_726c4bd20100ryan.html


信号

进程间通信的方法有: 信号、文件锁、管道、FIFO、信号量、共享内存、消息队列
最有效,最快的方法是:共享内存
信号量、共享内存、消息队列是system V IPC
必须要求是亲属进程间才能通信的方法是:管道
 
信号机制:
收到信号后是异步运行的。
信号(signal)机制是Linux系统中最为古老的进程之间的通信机制
信号事件的发生有两个来源:
硬件来源,比如我们按下了键盘或者其它硬件故障;
软件来源,最常用发送信号的系统函数是kill(), raise(), alarm()和setitimer()等函数,软件来源还包括一些非法运算等操作。
         kill()函数可以对任何进程发送信号,raise()只能对自己发信号,alarm()和setitimer()发送定时信号。
         可以用kill  -l 命令列出所有的信号。
         程序在休眠时搜索不到信息。
         信息未处理完成时,系统无法运行。
进程对信号的处理
v       进程可以通过三种方式来响应和处理一个信号:
忽略信号、捕捉信号、执行缺省操作
但有两个信号不能忽略:SIGKILL 和SIGSTOP
信号处理函数的安装
#include<signal.h>
void( *signal(int sig, void( *func)(int)))(int);
         int sigaction(int  signum,const struct sigacton *act,struct sigaction *oldact);
         其中:signal()在可靠的信号系统调用的基础上实现,是库函数。它只是两个参数,不支持信号传递信息。第一个参数是要安装的信号量,第二个参数是对第一个参数指定的信号的处理。
如果func不是函数指针,必须是下列两个宏:
SIG_IGN:忽略信号。
SIG_DEF:采用系统默认的方式处理信号,执行缺省操作。
返回值:返回先前的信号处理函数指针,如果有错误则返回-1。
 
信号的发送
除了内核和超级用户,并不是每个进程都可以向其他的进程发送信号。
一般的进程只能向具有相同uid和gid的进程发送信号,或向相同进程组中的其他进程发送信号。
常用的发送信号的函数有kill()、raise ()、alarm()、setitimer()、abort() 等。
 
kill()函数:给指定的进程发送某一个信号
#include <sys/types.h>
#include <signal.h>
 int kill(pid_t pid, int sig);
pid>0  给PID为pid的进程发送信号
pid=0  给同一个进程组的所有进程发送信号
pid<0 且 pid!=-1  给进程组ID为-pid的所有进程发送信号
pid=-1  给出了自身之外的PID大于1的进程发送信号
第二个参数是要发送的信号值,当第二个参数为0的时候,实际上不会发送任何信号
返回值:成功为0;失败返回-1。
EINVAL:所发送的信号无效
EPERM:没有向目标进程发送信号的权限
ESRCH:目标进程不存在或者进程已经终止,处于僵尸。
raise()函数:给进程本身发送一个信号
    #include <signal.h>
    int raise(int sig);  (给自己发信号)
相当于kill(getpid(),sig);
返回值:成功为0;失败返回-1
alarm()函数:是一个简单定时器,专为SIGALRM信号设计
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
在指定的seconds秒之后,给进程本身发生一个SIGALRM信号
 
setitimer()功能强大更强大的定时器函数,支持3种类型的定时器,但是从本质上,它是和alarm共享同一个进程内的定时器
#include <sys/time.h>
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue); 
abort():向进程发送SIGABORT信号,默认情况下进程会异常退出
 
文件锁
当多个进程都需要对同一个文件进行读写操作的时候,有时候需要确保进程在一次读写操作完成之前,文件不被其他进程修改。文件锁机制提供了这样的同步功能。
int fcntl(int fd, int cmd, struct flock *lock);
fd 文件描述符
cmd参数:F_GETLK:获取文件锁当前的状态。
F_SETLK:设置文件锁。
F_SETLKW:这是F_SETLK的阻塞版本,如果新加的锁被拒绝,那么进程被阻塞直到可以加锁。
struct flock{
        …
         short l_type;           
         short l_whence;         
         off_t l_start;              
         off_t l_len;            
         …};
l_type类型:
F_RDLCK:读锁
F_WRLCK:写锁
F_UNLCK:解锁
为了锁定整个文件,通常的方法是将l_start指定为0,l_whence指定为SEEK_SEK, l_len指定为0。
 
管道
 
管道是针对于本地计算机的两个进程之间的通信而设计的通信方法,管道建立后,实际获得两个文件描述符:一个用于读取而另外一个用于写入。
管道是半双工的,数据只能向一个方向流动,需要双方通信时,需要建立起两个管道。
只能用于父子进程或者兄弟进程之间(具有亲缘关心的进程)。
单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是单独构成一种文件系统,并且只存在于内存中。
数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
 
管道的创建
系统调用pipe()用于创建一个管道
int pipe(int filedes[2]);
建立管道:  
filedes[0]: 为pipe的读出端
filedes[1]: 为pipe的写入端
两个文件描述符数组。
 
FIFO
FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中
在文件系统中是一个有名字的管道
任何进程都可以打开
进程间无需关联
注:直接映射到磁盘上,有文件名,故任何进程都可以打开。
FIFO的创建
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
 
信号量
System IPC中,对于每一个新建的信号量、消息队列以及共享内存,都有一个在整个系统中唯一的标识符。每个标识符也都有唯一对应的关键字,关键字的数据类型由系统定义为key_t。
创建/获取信号量
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
Key:键值
1. 指定键值
2. IPC_PRIVATE
系统指定键值
nsems:信号量的数目
信号量集合的数目
Smeflg:信号量标志
1.IPC_CREATE 如果内核中没有此队列,则创建它。
2.IPC_EXECL     当和IPC_CREAT一起使用时,如果队列已经存在,则返回错误。
3.mode_flags:       类似于文件的权限
 
信号量操作
当进程需要申请或者释放公共资源的时候,可以调用semop()来对信号量进行操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
sops:指向sembuf结构的数组指针
unsigned short sem_num:组中包含的信号量数量
short  sem_op:操作类型的整数
整数:加到信号量的值上
负数:信号量的值减去绝对值,如果小于零,进程阻塞,直到信号量的值至少等于其绝对值
0:导致操作阻塞,直到信号量的值为0才继续。
 
short  sem_flg:一个符号位
IPC_NOWAIT: 非阻塞操作
SEM_UNDO:linux会在进程退出的时候自动撤销该次操作。
 
信号量控制
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
第一个参数semid指定信号量集,第二个参数semnum指定了读信号量集里面的哪一个信号集进行操作(编号),第三个参数cmd指定具体的操作类型(指令)。
当进程创建了新的信号量集的时候,调用了atexit(&sem_delete)函数,使得程序退出时,信号量集被释放。
 
消息队列
消息队列是系统内核地址空间中的一个内部的链表。消息可以按照顺序发送到队列中,也可以以几种不同的方式从队列中读取。每一个消息队列用一个唯一的IPC标识符表示
Msgbuf数据结构
struct msgbuf {
       long mtype;       
       char mtext[1];      
};
mtype指消息的类型,它由一个整数来代表,并且它只能是大于0的整数。
mtext是消息数据本身
 
创建消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>   
int msgget(key_t key, int msgflg);
Key:键值
1. 指定键值(可有ftok()函数获得)
2. IPC_PRIVATE  系统指定键值
msgflg :信号量标志
1.IPC_CREATE  如果内核中没有此队列,则创建它。
2.IPC_EXECL   当和IPC_CREAT一起使用时,如果队列已经存在,则返回错误。
3.mode_flags:类似于文件的权限
发送和接收消息
msgsnd()系统调用用于向队列发送一条消息:
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);
msgrcv()系统调用用于从消息队列读取一条消息:
ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg);
如果msgtype=0,接收队列的第一个消息(按时间顺序依次收到一个消息),大于0接收队列中消息类型等于这个值的第一个消息(特定值的消息),小于0接收消息队列中小于或等于msgtype绝对值的所有消息中的最小一个消息(按编号顺序依次收消息)
 
消息队列的控制
通过msgctl()可以对消息队列进行控制或者一些属性的修改:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
cmd:消息队列的操作
IPC_STAT:读取消息队列的数据结构msqid_ds,并将其存储在buf指定的地址中。
IPC_SET:设置消息队列的数据结构msqid_ds中的ipc_perm、msg_qbytes、msg_ctime元素的值。这个值取自buf参数。  
IPC_RMID:从系统内核中移走消息队列。
msgid_ds结构体
struct msqid_ds{
  struct ipc_perm msg_perm;     
  time_t          msg_stime;    
  time_t          msg_rtime;    
  time_t          msg_ctime;    
  unsigned long   __msg_cbytes; 
  msgqnum_t       msg_qnum;     
  msglen_t        msg_qbytes;   
  pid_t           msg_lspid;    
  pid_t           msg_lrpid;    
}
共享内存
两个不同进程A、B共享内存的基本原理是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。
 
创建和获取共享内存
系统调用shmget()用于创建共享内存或者获取一个已经存在的共享内存的标识符:
int shmget(key_t key, size_t size, int shmflg);
key:键值
1. 指定键值
2. IPC_PRIVATE  系统指定键值
size:共享内存大小
msgflg :共享内存标志
1.IPC_CREATE  如果内核中没有此队列,则创建它。
2.IPC_EXECL   当和IPC_CREAT一起使用时,如果队列已经存在,则返回错误。
3.mode_flags:   类似于文件的权限
 
 
系统调用shmat()可以获取一个共享内存的地址,并将其连接到进程中:
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid:
由shmget返回的共享内存标志
shmaddr:
映射该共享内存块的进程内存地址
如果为NULL,Linux将自动选择合适的地址
shmflg:
SHM_RND
SHM_RDONLY
 
通过shmctl()可以对消息队列进行控制或者一些属性的修改:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
cmd:共享内存的操作
IPC_STAT:读取一个共享内存的数据结构shmid_ds,并将其存储在buf指定的地址中。
IPC_SET:设置消息队列的数据结构shmid_ds中各个元素的值。这个值取自buf参数。
IPC_RMID:把共亨内存标记为可删除,当最后一个进程脱连此共享内存的时候,系统将删除该共享内存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值