在Linux中进程间通信主要有:
1.管道
2.信号
3.消息队列
4.共享内存
5.信号量
6.套接字
管道:
管道分为有名管道和无名管道。无名管道只能用于亲属进程间的通信。它具有固定的读端和写端。有名管道可以实现两个互不相关的进程间通信。通过创建一个中间文件,然后两个进程操作这个中间文件。
管道是一种特殊的文件,可以使用普通的read()、write()来操作它。就和操作普通文件一样,凡是它不属于任何文件系统,它存在在内存中。
无名管道的创建:
int pipe(int fd[]);
fd是包含两个元素的整型数组,存放管道对应的文件。
函数成功返回0,失败返回-1。
由于无名管道用于亲属进程,所以进程间共享内存资源,每个进程中都含有一对描述符fd[0]、fd[1]。所以必须关一个。要不传输数据混乱了。
向管道写数据时,管道缓冲区只要有空间写进程就会试图向缓冲区写数据,如果缓存区满了,那么就会阻塞。
有名管道的创建:
int mkfifo(const char* filename,mode_t mode)
filename:要创建的管道名。
mode:管道的访问权限
函数成功返回0,失败返回-1。
信号:
信号是进程间通信机制中唯一的异步通信机制。
进程可以通过三种方式来响应一个信号:
1.忽略,对信号不做任何处理
2.捕捉信号,定义信号处理函数,当信号发生时,执行相应的处理函数
3.执行默认操作
信号发送:
int kill(pid_t pid,int sig)
pid:正数:发送信号给进程为pid的进程
0:信号被发送到所有和当前进程在同一个进程组的进程
-1:信号发送给所有进程表中的进程
<-1:信号发送给进程组号为-pid的每一个进程
sig:信号类型
成功返回0,失败返回-1。
kill不仅可以终止进程,也可以向进程发送其他信号。
int raise(int sig);
sig:信号类型
成功返回0;失败返回-1
定时器信号:
unsigned int alarm(unsigned int seconds);
second:系统经过second秒后向该进程发送SIGALARM信号。
注意:一个进程只能有一个闹钟时间,如果在调用alarm()之气那以设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。
int pause(void)
函数返回-1,并把errno值设置为EINTR。
该函数用于将调用进程挂起直至接收到信号。
信号的设置:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
signum:指定信号代码
handler:SIG_IGN忽略该信号
SIG_DFL:采用系统默认方式处理信号
自定义的信号处理函数
函数成功返回指向一个无返回值并且带一个整型参数的函数指针。失败返回-1。
该函数主要用于前32种非实时性信号的处理,不支持信号传递信息。
int sigaction(int signum,const struct sigaction* act,struct sigaction* oldset);
signum:信号类型,除SIGKILL和SIGSTOP之外的任何一个信号
act:指向sigaction结构体的指针,包含对特定信号的处理。
oldact:保存之前的处理方式
成功返回0,失败返回-1。
信号量:
信号量是用来解决进程/线程间的同步与互斥问题的一种机制。信号量对于某一种资源,取一个非负的整数值。信号量指的是当前可用的该资源的数量。若它等于0,则意味着目前没有可用的资源。
p操作:如果有可用的资源,信号量大于0,然后进入临界区资源后,信号量减一,如果没有可用资源,则会阻塞,信号量等于0,一直等到有资源时再被唤醒。
v操作:如果在该信号量的等待队列里有任务在等待资源,就唤醒一个阻塞任务,如果没有任务等待它,则释放一个资源,信号量减一。
1.创建信号量或者获得系统已经存在的信号量,此时需要调用semget()函数。不同进程通过使用同一个信号量键值来获得同一个信号量。
2.初始化信号量,此时使用semctl()函数的SETVAL操作。当使用二维信号量时,通常将信号量初始化为1。
3.进行信号量的PV操作,此时调用semop()函数。这一步是实现进程之间的同步和互斥。
4.使用semclt()函数的IPC_RMID操作从系统中删除信号量。
int semget(key_t key,int nsems,int semflag);
key:信号量的键值,多个进程可以通过它访问同一个信号量。
nsems:需要创建信号量的数目,通常取值为1。
semflag:同open()函数的权限位。
成功返回信号量标识符,失败返回-1。
int semctl(int semid,int semnum,int cmd,union semun arg);
semid:semget()函数返回的信号量标识符。
semnum:信号量编号,当使用信号量集时才会被用到,通常取值为0,表示使用单个信号量。
cmd:指定对信号量的各种操作
函数成功根据cmd返回不同的值,失败返回-1。
int semop(int semid,struct sembuf* sops,size_t nsops);
semid:semget()函数返回的信号量标识符。
sops:指向信号量的操作数组。
nsops:操作数组sops中的操作个数,通常取值为1,表示一个操作。
函数成功返回信号量标识符,失败返回-1。
共享内存:
共享内存是最高效的进程间通信方式。因为进程可直接读写内存,不需要进行任何数据的复制。
共享内存的实现分两步:
1.使用shmget()创建一段共享内存。
2.使用shmat()将这段共享内存映射到具体的进程空间。
3.使用shmdt()解除映射。
int shmget(key_t key,int size,int shmflg);
key:共享内存的键值,多个进程可以通过它访问同一个共享内存。
size:共享内存区的大小。
shmflg:同open()函数的权限位。
成功返回共享内存段标识,失败返回-1。
char *shmat(int shmid,const void *shmaddr,int shmflg);
shmid:要映射的共享内存区标识符。
shmaddr:将共享内存映射到指定地址。为0系统自动分配。
shmflg:共享内存可读写设置。
成功返回被映射的段地址,失败返回-1。
int shmdt(const void *shmaddr);
shmaddr:被映射的共享内存段地址
成功返回0,失败返回-1。
消息队列:
消息队列就有FIFO特性,但是它可以实现消息的随即查询,这些消息存在内核中,由队列ID实现。
消息队列的实现步骤:
1.使用msgget()函数创建或者打开消息队列。
2.使用msgsnd()函数添加消息,它会把消息添加到已打开的消息队列的末尾
3.使用msgrcv()函数读取消息,它会把消息从消息队列中取走。
4.使用msgctl()函数控制消息队列。
int msgget(key_t key,int msgflg);
key:消息队列的键值,多个进程可以通过它访问同一个消息队列。
msgflag:权限标志位。
成功返回消息队列ID、失败返回-1。
int msgsnd(int msgid,const char* msgp,size_t msgsz,int msgflg);
msgid:消息队列的队列ID
msgp:只想消息结构的指针。
msgsz:消息正文字节数
msgflg:0表示msgsnd调用阻塞知道发送成功为止,IPC_NOWAIT表示若消息无法立即发送,函数立即返回。
成功返回0,失败返回-1。
int msgrcv(int msgid,void *msgp,size_t msgsz,long int msgtype,int msgflg);
msgid:消息队列的队列ID
msgp:消息缓冲区
msgsz:消息正文的字节数
msgtype:0表示接收消息队列的第一个消息
大于0表示接收消息队列中第一个类型为msgtyp的消息
小于0表示接收消息队列中第一个类型值不小于msgtyp绝对值且类型值右最小的消息。
msgflg:0表示msgsnd()调用到阻塞直到接收到一条相应类型消息为止。
IPC_NOWAIT表示在消息队列中没有相应的类型接收,直接返回。
MSG_NOERROR表示若返回的消息比msgsz字节多,就截短到msgsz字节,且不通知消息发送进程。
成功返回0,失败返回-1。
int msgctl(int msgid,int cmd,strucr msgid_ds* buf);
msgid:消息队列的队列ID
cmd:IPC_STAT表示读取消息队列的数据结构msgid_ds,并将其存储在buf指定的地址中。
IPC_SET表示设置消息队列的数据结构msgid_ds中的ipc_perm域值。这个值取自buf参数
IPC_RMID表示从系统内核中删除消息队列列
buf:描述消息队列的msgid_ds结构类型的变量
成功返回0,失败返回-1。
最后,在Linux中使用ipcs命令查看进程通信机制状态。