进程间通信的方式
进程间通信 IPC(Inter-process communication)。在操作系统中,每一个进程都是独立的执行体。但是在有些时候,进程之间又需要相互交互,所以就有了进程间通讯,Linux下进程间的通讯方式有: 信号、管道(有名、无名)、信号量、消息队列、共享内存、socket等。
信号
当我们使用了多线程来编写代码的时候,程序中总是存在一部分临界代码,我们需要确保只有一个进程或者执行线程可以进入这个临界代码并且对这块资源拥有独占式的访问权。
临界资源:同一个时刻只允许一个进程或者执行进程访问的资源;
临界区域:程序中访问临界资源的代码段,这块区域被称作是临界区雨。
为了防止出现多个程序同时访问同一个共享资源而引发的问题,可以通过通过生成并使用令牌来授权,使得在任意一个时刻只能有一个执行线程访问代码的临界区域。
**信号:是一个特殊变量,只允许对它进行等待(wait)和发送信号(signal)两种操作。其中P:等待,代表获取资源,对信号量进行减1,V:发送信号,释放操作,进行加1操作;*
- 信号量就用来控制对临界资源访问的情况,避免出现交叉访问。
信号量函数的定义:
semget() 函数的作用是创建一个信号量或者获取一个已有信号量的键。key是一个整形值,起标记作用。nums_sems是参数指定需要的信号量数目,基本取直为1.flag是标志位。
semid=semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);
//全新创建一个信号量,如果信号存在,则失败;
if(semid==-1) //可能已被创建
{
semid=semget((key_t)1234,1,IPC_CREAT|0600);//获取已存在的信号量
if(semid==-1)
{
perror("semget error");
}
}
semop是用于改变信号量的值。其中sem_ops是指向sembuf结构体的指针:
最后一个参数设置为SEM_UNDO;
semctl函数用来直接控制信号量信息。
代码:
//sem.h
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<sys/sem.h>
#include<string.h>
union semun{
int val;
};
void sem_init(); //初始化
void sem_p(); //P操作
void sem_v(); //V操作
void sem_destroy(); //销毁
//sem.c
#include "sem.h"
int semid=-1;
void sem_init() //创建或者获取semid
{
semid=semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);
//全新创建一个信号量,如果信号存在,则失败;
if(semid==-1) //可能已被创建
{
semid=semget((key_t)1234,1,IPC_CREAT|0600);//获取已存在的信号量
if(semid==-1)
{
perror("semget error");
}
}
else //全新创建成功 初始化信号量
{
union semun a; //头文件中写的结构体;
a.val=1;
if(-1==semctl(semid,0,SETVAL,a))//初始化一个变量为1;
{
perror("semctl error");
}
}
}
void sem_p() //P操作 -1
{
struct sembuf buf;
buf.sem_num=0; //下标
buf.sem_op=-1; //p操作
buf.sem_flg=SEM_UNDO; //防止系统异常
if(semop(semid,&buf,1)==-1)
{
perror("semop error");
}
}
void sem_v() //V操作 +1
{
struct sembuf buf;
buf.sem_num=0; //下标
buf.sem_op=1; //p操作
buf.sem_flg=SEM_UNDO; //防止系统异常
if(semop(semid,&buf,1)==-1)
{
perror("semop error");
}
}
void sem_destroy() //销毁
{
if(semctl(semid,0,IPC_RMID)==-1)
{
perror("semctl destroy error");
}
}
共享内存
共享内存也是一种IPC机制。允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间传递数据的一种有效方式。共享内存是一个特殊的地址范围。其他进程可以将同一段共享内存连接到自己的地址空间中。所有进程都可以访问共享内存中的地址。如果此块内存发生改变,所做的改动可以立刻被访问同一段共享内存的多个线程看到。
共享内存使用的函数类似于信号量之间的函数:
shmget函数来创建共享内存,返回值是共享内存标识符,是一个非负整数,如果创建失败,会返回-1。key为共享内存段命名。size是以字节为单位指定的共享内存的大小。shmflg是标志位。
shmat函数用来链接共享内存和进程间的访问通信。相反,shmdt是将共享内存从当前进程中分离出来。也就是断开共享内存和进程之间的映射关系。
shmctl是共享内存的控制函数。
command是要采取的动作。分别是:
buff是一个指针,指向包含共享内存模式模式和访问权限的结构。
消息队列
消息队列和管道相似,但少了打开和关闭管道的复杂性。消息队列的优势在于独立于接受和发送进程而存在。通过消息队列发送消息机会完全避免了命名管道的同步和阻塞问题。