System V IPC 之信号量

System V IPC 之信号量,第1张

概述本文继《System V IPC 之共享内存》之后接着介绍 System V IPC 的信号量编程。在开始正式的内容前让我们先概要的了解一下 Linux 中信号量的分类。信号量的分类在学习 IPC 信号量之前,让我们先来了解一下 Linux 提供两类信号量:内核信号量,由内核控制路径使用。用户态进程使用的信号量,这种信号量又分为 POSIX 信号量和 System V 信号量。POSIX 信号量与 System V 信号量的区别如下:对 POSIX 来说,信号量是个非负整数,常用于线程间同步。而 System V 信号量则是一个或多个信号量的集合,它对应的是一个信号量结构体,这个结构体是为 System V IPC 服务的,信号量只不过是它的一部分,常用于进程间同步。POSIX 信号量的引用头文件是 "<semaphore.h>",而 System V 信号量的引用头文件是 "<sys/sem.h>"。从使用的角度,System V 信号量的使用比较复杂,而 POSIX 信号量使用起来相对简单。本文介绍 System V 信号量编程的基本内容。System V IPC 信号量信号量是一种用于对多个进程访问共享资源进行控制的机制。共享资源通常可以分为两大类:互斥共享资源,即任一时刻只允许一个进程访问该资源同步共享资源,即同一时刻允许多个进程访问该资源信号量是为了解决互斥共享资源的同步问题而引入的机制。信号量的实质是整数计数器,其中记录了可供访问的共享资源的单元个数。本文接下来提到的信号量都特指 System V IPC 信号量。当有进程要求使用某一资源时,系统首先要检测该资源的信号量,如果该资源的信号量的值大于 0,则进程可以使用这一资源,同时信号量的值减 1。进程对资源访问结束时,信号量的值加 1。如果该资源信号量的值等于 0,则进程休眠,直至信号量的值大于 0 时进程被唤醒,访问该资源。信号量中一种常见的形式是双态信号量。双态信号量对应于只有一个可供访问单元的互斥共享资源,它的初始值被设置为 1,任一时刻至多只允许一个进程对资源进行访问。信号量用于实现对任意资源的锁定机制。它可以用来同步对任何共享资源的访问。相关数据结构System V 子系统提供的信号量机制是比较复杂的。我们不能单独定义一个信号量,而只能定义一个信号量集,其中包括一组信号量,同一信号量集中的信号量可以使用同一 ID 引用。每个信号量集都有一个与其相对应的结构,其中包含了信号量集的各种信息,该结构的声明如下:struct semid_ds{struct ipc_perm sem_perm;struct sem *sem_base;ushort sem_nsems;time_t sem_otime;time_t sem_ctime;};下面简单介绍一下 semid_ds 结构中字段的含义。sem_perm:对应于该信号量集的 ipc_perm 结构(该结构的详情请参考《System V IPC 之内存共享》)指针。sem_base:sem 结构指针,指向信号量集中第一个信号量的 sem 结构。sem_nsems:信号量集中信号量的个数。sem_otime:最近一次调用 semop 函数的时间。sem_ctime:最近一次改变该信号量集的时间。sem 结构记录了一个信号量的信息,其声明如下:struct sem{ushort semval; /* 信号量的值 */pid_t sempid; /* 最后一次返回该信号量的进程ID 号 */ushort semncnt; /* 等待可利用资源出现的进程数 */ushort semzcnt; /* 等待全部资源可被独占的进程数 */};与信号量相关的函数信号量集的创建与打开要使用信号量,首先要创建一个信号量集,创建信号量集的函数声明如下:#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);函数 semget 用于创建一个新的信号量集或打开一个已存在的信号量集。其中参数 key 表示所创建或打开的信号量集的键。参数 nsems 表示创建的信号量集中信号量的个数,此参数只在创建一个新的信号量集时有效。参数 semflg 表示调用函数的 *** 作类型,也可用于设置信号量集的访问权限。所以调用函数 semget 的作用由参数 key 和 semflg 决定。当函数调用成功时,返回值为信号量的引用标识符,调用失败时,返回值为 -1。当调用 semget 函数创建一个信号量时,它相应的 semid_ds 数据结构被初始化。ipc_perm 中的各个字段被设置为相应的值,sem_nsems 被设置为 nsems 所表示的值,sem_otime 被设置为 0,sem_ctime 被设置为当前时间。对信号量集的 *** 作对信号量集 *** 作的函数声明如下:#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semop(int semid, struct sembuf *sops, size_t nsops);参数 semid 为信号量集的引用标识符。sops 为指向 sembuf 类型的数组的指针,sembuf 结构用于指定调用 semop 函数所做的 *** 作。sembuf 结构的定义如下:struct sembuf{short sem_num; // 要 *** 作的信号量在信号量集里的编号short sem_op;short sem_flag;};其中,sem_num 指定要 *** 作的信号量。sem_flag 为 *** 作标记,与此函数相关的有 IPC_NOWAIT 和 SEM_UNDO。sem_op 用于表示所要执行的 *** 作,相应的取值和含义如下:sem_op > 0:表示进程对资源使用完毕,交回该资源。此时信号量集的 semid_ds 结构的 sem_base.semval 将加上 sem_op 的值。若此时设置了 SEM_UNDO 位,则信号量的调整值将减去 sem_op 的绝对值。sem_op = 0:表示进程要等待,直至 sem_base.semval 变为 0。sem_op < 0:表示进程希望使用资源。此时将比较 sem_base.semval 和 sem_op 的绝对值大小。如果 sem_base.semval 大于等于 sem_op 的绝对值,说明资源足够分配给此进程,则 sem_base.semval 将减去 sem_op 的绝对值。若此时设置了 SEM_UNDO 位,则信号量的调整值将加上 sem_op 的绝对值。如果 sem_base.semval 小于 sem_op 的绝对值,表示资源不足。若设置了 IPC_NOWAIT 位,则函数出错返回,否则 semid_ds 结构中的 sem_base.semncnt 加 1,进程等待直至 sem_base.semval 大于等于 sem_op 的绝对值或该信号量被删除。sops 指向的数组中的每个元素表示一个 *** 作,由于此函数是一个原子 *** 作,一旦执行就将执行数组中所有的 *** 作。信号量的控制对信号量的具体控制 *** 作是通过函数 semctl 来实现的,其声明如下:#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, union semun arg);参数 semid 为信号量集的引用标识符。参数 semnum 用于指明某个特定的信号量。参数 cmd 表示调用该函数希望执行的 *** 作。参数 arg 是一个用户自定义的联合体:union semun{int val;struct semid_ds *buf;ushort *array; // cmd == SETALL,或 cmd = GETALL};此联合中各个字段的使用情况与参数 cmd 的设置有关。具体的说明如下:GETALL:获得 semid 所表示的信号量集中信号量的个数,并将该值存放在无符号短整型数组 array 中。GETNCNT:获得 semid 所表示的信号量集中的等待给定信号量锁的进程数目,即 semid_ds 结构中 sem.semncnt 的值。GETPID:获得 semid 所表示的信号量集中最后一个使用 semop 函数的进程 ID,即 semid_ds 结构中的 sem.sempid 的值。GETVAL:获得 semid 所表示的信号量集中 semunm 所指定信号量的值。GETZCNT:获得 semid 所表示的信号量集中的等待信号量成为 0 的进程数目,即 semid_ds 结构中的 sem.semncnt 的值。IPC_RMID:删除该信号量。IPC_SET:按参数 arg.buf 指向的结构中的值设置该信号量对应的 semid_ds 结构。只有有效用户 ID 和信号量的所有者 ID 或创建者 ID 相同的用户进程,以及超级用户进程可以执行这一 *** 作。IPC_STAT:获得该信号量的 semid_ds 结构,保存在 arg.buf 指向的缓冲区。SETALL:以 arg.array 中的值设置 semid 所表示的信号量集中信号量的个数。SETVAL:设置 semid 所表示的信号量集中 semnum 所指定信号量的值。应用信号量的 demo下面我们通过一个 demo 来看看如何在程序中使用信号量。这是一个通过共享内存进行进行进程间通信的例子:#include <sys/types.h>#include <sys/sem.h>#include <sys/shm.h>#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <string.h>#include <signal.h>#define SHMDATASIZE 1000#define SN_EMPTY 0#define SN_FULL 1int deleteSemid = 0;void server(void);void client(int shmid, int semid);void delete(void);void sigdelete(int signum);void locksem(int signum, int semnum);void unlocksem(int semid, int semnum);void clientwrite(int shmid, int semid, char *buffer);union semun{int val;struct semid_ds *buf;ushort *array;};int safesemget(key_t key, int nssems, int semflg);int safesemctl(int semid, int semunm, int cmd, union semun arg);int safesemop(int semid, struct sembuf *sops, unsigned nsops);int safeshmget(key_t key, int size, int shmflg);void *safeshmat(int shmid, const void *shmaddr, int shmflg);int safeshmctl(int shmid, int cmd, struct shmid_ds *buf);int main(int argc, char *argv[ ]){if(argc < 3){server();}else{client(atoi

",而 System V 信号量的引用头文件是 ""。

而引入的机制。信号量的实质是整数计数器,其中记录了可供访问的共享资源的单元个数。本文接下来提到的信号量都特指 System V IPC 信号量。

sem *

semval; semncnt; semzcnt;

#include

<span >int semget(key_t key,<span >int nsems,<span >int semflg);

#include

<span >int semop(<span >int semid,<span >struct sembuf *sops,size_t nsops);

sem_num;

0:表示进程对资源使用完毕,交回该资源。此时信号量集的 semid_ds 结构的 sem_base.semval 将加上 sem_op 的值。若此时设置了 SEM_UNDO 位,则信号量的调整值将减去 sem_op 的绝对值。

#include

<span >int semctl(<span >int semid,<span >int semnum,<span >int cmd,union semun arg);

semid_ds * *array; };

#include <.h> SHMDATASIZE 1000 SN_EMPTY 0 SN_FULL 1 deleteSemid = <span >void server(<span >void<span >);
<span >void
client(<span >int
shmid,<span >int
<span > semid);
<span >void
delete(<span >void
<span >);
<span >void
sigdelete(<span >int
<span > signum);
<span >void
locksem(<span >int
signum,<span >int
<span > semnum);
<span >void
unlocksem(<span >int
semid,<span >int
<span > semnum);
<span >void
clientwrite(<span >int
shmid,<span >int
semid,<span >char
<span >buffer);
union semun
{
<span >int
<span > val;
<span >struct
semid_ds <span >buf;
<span >ushort
<span >array;
};
<span >int
safesemget(key_t key,<span >int
nssems,<span >int
<span > semflg);
<span >int
safesemctl(<span >int
semid,<span >int
semunm,<span >int
<span > cmd,union semun arg);
<span >int
safesemop(<span >int
semid,<span >struct
sembuf <span >sops,unsigned nsops);
<span >int
safeshmget(key_t key,<span >int
size,<span >int
<span > shmflg);
<span >void
safeshmat(<span >int
shmid,<span >const
<span >void
shmaddr,<span >int
<span > shmflg);
<span >int
safeshmctl(<span >int
shmid,<span >struct
shmid_ds *<span >buf);

<span >int main(<span >int argc,<span >char *<span >argv)
{
<span >if(argc < <span >3<span >){
server();
}
<span >else<span >{
client(atoi(argv[<span >1]),atoi(argv[<span >2<span >]));
}
<span >return <span >0<span >;
}

<span >void server(<span >void<span >)
{
union semun sunion;
<span >int<span > semid,shmid;
<span >char *<span >buffer;
semid = safesemget(IPC_PRIVATE,<span >2,SHM_R|<span >SHM_W);
deleteSemid =<span > semid;
<span >//<span > 在服务器端程序退出时删除掉信号量集。
atexit(&<span >delete);
signal(SIGINT,&<span >sigdelete);
<span >//<span > 把第一个信号量设置为 1,第二个信号量设置为 0,<span >//<span > 这样来控制:必须在客户端程序把数据写入共享内存后服务器端程序才能去读共享内存
sunion.val = <span >1<span >;
safesemctl(semid,SN_EMPTY,SETVAL,sunion);
sunion.val = <span >0<span >;
safesemctl(semid,SN_FULL,sunion);
shmid = safeshmget(IPC_PRIVATE,SHMDATASIZE,IPC_CREAT|SHM_R|<span >SHM_W);
buffer = safeshmat(shmid,<span >0,<span >0<span >);
safeshmctl(shmid,IPC_RMID,NULL);
<span >//<span > 打印共享内存 ID 和 信号量集 ID,客户端程序需要用它们作为参数
printf(<span >"<span >Server is running with SHM id %d\n<span >"<span >,shmid);
printf(<span >"<span >Server is running with SEM id %d\n<span >"<span >,semid);
<span >while(<span >1<span >)
{
printf(<span >"<span >Waiting until full...<span >"<span >);
fflush(stdout);
locksem(semid,SN_FULL);
printf(<span >"<span >done.\n<span >"<span >);
printf(<span >"<span >Message received: %s.\n<span >"<span >,buffer);
unlocksem(semid,SN_EMPTY);
}
}

<span >void client(<span >int shmid,<span >int<span > semid)
{
<span >char *<span >buffer;
buffer = safeshmat(shmid,<span >0<span >);
printf(<span >"<span >Client operational: shm id is %d,sem id is %d\n<span >"<span >,shmid,semid);
<span >while(<span >1<span >)
{
<span >char input[<span >3<span >];
printf(<span >"<span >\n\nMenu\n1.Send a message\n<span >"<span >);
printf(<span >"<span >2.Exit\n<span >"<span >);
fgets(input,<span >sizeof<span >(input),stdin);
<span >switch(input[<span >0<span >])
{
<span >case <span >'<span >1<span >'<span >:
clientwrite(shmid,semid,buffer);
<span >break<span >;
<span >case <span >'<span >2<span >'<span >:
exit(<span >0<span >);
<span >break<span >;
}
}
}

<span >void locksem(<span >int semid,<span >int<span > semnum)
{
<span >struct<span > sembuf sb;
sb.sem_num =<span > semnum;
sb.sem_op = -<span >1<span >;
sb.sem_flg =<span > SEM_UNDO;
safesemop(semid,&sb,<span >1<span >);
}

<span >void unlocksem(<span >int semid,<span >int<span > semnum)
{
<span >struct<span > sembuf sb;
sb.sem_num =<span > semnum;
sb.sem_op = <span >1<span >;
sb.sem_flg =<span > SEM_UNDO;
safesemop(semid,<span >1<span >);
}

$ -Wall sem.c -o sem_demo

$ ./sem_demo

$ ./sem_demo

$ ipcs -s

总结

以上是内存溢出为你收集整理的System V IPC 之信号量全部内容,希望文章能够帮你解决System V IPC 之信号量所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/1050914.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存