消息队列、信号量、共享内存各个函数

时间:2021-10-29 14:44:30

信号量

信号量是一个特殊的整数值,主要用来控制多个进程对临界资源的互斥访问,进程根据信号量来判断是否有 访问的资源,这与前面所讲的信号是不同的,信号是一种处理异步事件的方法,而信号量是一种进程同步机制,信号与信号量是两个不同的东西。

信号量是一个计数器,可用于同步多进程对共享数据对象得访问,为了获得共享资源,进程需要执行以下操作:

1、测试控制该资源的信号量

2、若此信号量的值为正,则进程可以使用该资源,进程将信号量值减1,表示它使用了一个资源单位

3、若此信号量的值为0,则进程进入睡眠状态,直至信号量值大于0。当进程被唤醒后,它返回至第1步。

当进程不再使用由一个信号量控制得共享资源时,该信号量值增1.如果有进程正在睡眠以等待此信号量,则唤醒他们,为了正确地实现信号量,信号量值得测试及减1操作应当是原子操作,为此,信号量通常是在内核中实现得。

常用的信号量一般初始值为1,只控制单个资源,有时也称互斥锁,但是,信号量得初值可以是任意一正值,该值说明有多少个共享资源单位可供共享应用,信号量有以下3个特性:

1、信号量并非是一个非负值,而必须将信号量定义为含有一个或多个信号量值得集合,当创建一个信号量时,要指定该集合中的各个值。(linux/sem.h   struct semid_ds)

struct sem

{

ushort_t   semvl;

short        sempid;

ushort      semncnt;

ushort      semzcnt;    

};

2、创建信号量对其赋初值分开,这是一个致命弱点,因为不能原子地创建一个信号量集合,并且对该集合中的所有值赋初值。

3、即使没有进程使用,但他们仍然存在,因此必须考虑在进程终止时有没有释放得信号量。

以上的三个特性就导致了信号使用的复杂性。

信号量的值通过PV原语来进行操作改变的。

     

Linux中,系统提供了信号量的操作函数,主要有以下函数:

◆key_t  ftok(char *pathname,  char proj);

  根据参数pathnameproj 来创建一个关键字,成功时返回与路径pathname相对应的一个键值,具有唯一性,失败时返回值为-1.

◆int  semget (key_t  key,  int  nsems ,  int  semflg);

  创建一个新信号量或者取得一个现有的信号量,key是一个关键字,可以是用ftok()函数创建的,也可以是IPC_PRIVATE/usr/include/bits$ vi ipc.h

),nsems表明创建的信号量个数,semflg是设置信号量的访问权限标志,函数调用成功时返回信号量ID,失败则返回-1.

◆int semop (int semid, struct sembuf *spos, int nspos);

  对信号量进行操作的函数,用于改变信号量的键值,semid是信号量的标志,spos是指向一个结构体数组的指针,表明要进行什么操作,nspos表明数组的元素个数,调用成功则返回0,失败则返回-1.

/usr/include/linux$ vi sem.h  (sembuf \ semun)

 Struct sembuf  (/usr/include/sys$ vi sem.h)

 {

Unsigned short sem_num; /*sem index in array*/

Short sem_op; /* sem operation */

Short sem_flg;/* operation flags */ sem_flg&IPC_RND 0

};

其中,如果sem_op大于0,那么操作值加入到信号量的值中,并唤醒等待信号增加的进程,如果sem_op为0,当信号量的值是0的时候,函数返回,否则阻塞直到信号量的值为0,如果sem_op小于0,函数判断信号量的值加上这个负值,如果结果为0唤醒等待信号量为0的进程,如果小于0函数阻塞,如果大于0,那么从信号量里面减去这个值并返回。

◆int semctl (int semid, int semnum, int cmd,  union semun arg);

该函数得作用是对信号量进行一系列得控制,semid是要操作得信号量标志,semnum是信号量得下标,cmd是操作的命令,经常用的两个命令是:SETVALIPC_RMIDarg用于设置或返回信号量信息。

 Union semun

{

int val;

struct semid_ds *buf;

unsigned short *array;

struct seminfo *__buf;

void *__pad;

}


消息队列

消息队列是将消息按队列的方式组织成的链表,每个消息都是其中的一个节点。

消息队列的运行方式与命名管道非常相似。欲与其他进程通信的进程只要将消息发送到消息队列中,目的进程就从消息队列中读取需要的消息。需要注意的是,消息队列的长度以及每个消息的大小都是有限制的。

Linux系统提供的消息队列操作函数主要有以下几个:

int msgget(key_t key,int msgflg);

int msgsnd(int msqid, const void *msgptr,  int msgsz,int msgflg);

int msgrcv(int msqid, void *msgptr, int msgsz, long msgtyp, int msgflg);

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

msgget()函数与信号量的semget()函数相似,作用是创建一个消息队列。参数key是一个键值,可由用户设定也可通过ftok()函数获得。Msgflg参数设置的是一些标志位,可以是IPC_CREATIPC_EXCLIPC_NOWAIT中的一个或者他们的组合。创建成功则返回消息队列ID;否则返回-1

Msgsnd()函数的作用是将消息发送到消息队列中去。Msqid为消息队列IDMsgptr是指想要发送的消息的指针,并且指向的缓冲区的第一个字段应为长整形,指定消息类型,消息内容存放在该缓冲区的紧跟消息类型字段的区域中。Msgsz是要发送的消息的长度。Msgflgmsgget()函数中的msgflg参数设置类似,设置当消息队列满等情况出现时的处理方式,如果msgflg设置为IPC_NOWAIT,则不发送消息并且立即返回-1;否则发送进程挂起等待。

如果msgsnd()函数调用成功,就会把消息复制到消息队列中去并返回0;否则返回-1

Msgrcv()函数的作用是从消息队列中读取一个消息。Msqid是消息队列的IDMsgptr保存从消息队列中读到的消息。Msgszmsgptr指向的消息的长度。Msgtyp指定要求的消息类型,见表

Msgrcv()函数msgtyp参数说明

Msgtyp取值

说明

大于0

接收消息队列中类型为msgtyp的第一个可用报文

等于0

接收消息队列中的第一个可用报文

小于0

接收消息队列中小于或等于msgtyp绝对值的第一个可用报文

Msgflg的设置与msgsnd()函数中 的参数msgflg设置类似,用于设置如何处理当前消息队列中没有满足条件的消息的情况。

如果msgrcv()函数调用成功,则返回读出的实际字节数;否则返回-1.

Msgctl()函数是消息队列的控制函数,类似于信号量的控制函数semctl()Msqid是消息队列的IDCmd是要采取的控制操作,有3个可取值,见表

参数值

说明

IPC_SET

设置消息队列的属性,将buf指向的结构体中的数值设置为消息队列的相关性

IPC_STAT

获取消息队列的属性信息并保存到buf指向的结构体中

IPC_RMID

移除IDmsqid的消息队列

 

 

共享内存

共享内存是系统创建的特殊地址空间,允许不相关的多个进程使用这个内存空间,即多个进程能够使用同一块内存中的数据。

共享内存与其他进程通信方式相比较,不需要复制数据,直接读写内存,是一种效率非常高的进程通信方案。但它本身不提供同步访问机制,需要我们自己控制。在LINUX中,只要把共享内存段连接到进程的地址空间中,这个进程就可以访问共享内存中的地址了。

LINUX系统提供的共享内存操作函数与信号量、消息队列等类似,主要有以下几个:

(1) int shmget(key_t key,int shmsz,int shmflg);

(2) void *shmat(int shmid,const void *shmaddr, int shmflg);

如果shmaddr0 则此段连接到由内核选择的第一个可用地址上,这是推荐的使用方式
如果shmaddr非零,并且没有指定SHM_RND,则此段链接到addr所指的地址上
如果shmaddr非零且指定SHM_RND,则此段链接到shmaddr - (addr mod ulus SHMLBA)所表示的地址上。SHM_RND的意思是低边界地址倍数,它总是2的乘方。该算式是将地址向下取最近的一个SHMLBA的倍数

(3) int shmdt (const void *shmaddr);

(4) int shmctl(int shmid, int cmd,struct shmid_ds *buf);

以上各函数含义如下:

l Shmget()函数分配一块新的共享内存。Shmsz指明共享内存的大小,以字节为单位,shmflg的设置与信号量的semget()函数中的参数semflg类似。

如果shmget()函数调用成功则返回共享内存的ID;否则返回-1.

l Shmat()函数的作用是连接共享内存与某个进程的地址空间。Shmidshmget()函数返回的共享内存IDShmaddr是共享内存连接到进程中的存放地址,一般设置为空指针,表示交由系统完成这个工作。Shmflg设置共享内存的控制选项,有两个可能取值:SHM_RND(shmaddr参数相关)SHM_RDONLY(只允许读)。如果shmat()函数调用成功则返回指向共享内存的指针;否则返回-1.

l Shmdt()函数用来解除进程与共享内存区域的关联,使当前进程不能继续访问共享内存。参数shmaddrshmat()函数返回的指针。如果操作成功则返回0;失败则返回-1.

l Shmctl()函数实现对共享内存区域的控制操作。其用法与消息队列的msgctl()函数类似。

 

sem. msg. shm 都是创建在/dev/shm目录下

ipcs

ipcs -s ipcs -q ipcs -m

ipcrm

 

ipcs -s -i sem_id