linux进程通信之SYSTEM V信号量

信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。

一、信号量的分类:

在学习信号量之前,我们必须先知道——Linux提供两种信号量:

(1) 内核信号量,由内核控制路径使用。

(2) 用户态进程使用的信号量,这种信号量又分为POSIX信号量和SYSTEM V信号量。

POSIX信号量又分为有名信号量和无名信号量。有名信号量,其值保存在文件中, 所以它可以用于线程也可以用于进程间的同步。无名信号量,其值保存在内存中。

二、POSIX 信号量与SYSTEM V信号量的比较

1. 对POSIX来说,信号量是个非负整数。常用于线程间同步。而SYSTEM V信号量则是一个或多个信号量的集合,它对应的是一个信号量结构体,

这个结构体是为SYSTEM V IPC服务的,信号量只不过是它的一部分。常用于进程间同步。

2.POSIX信号量的引用头文件是“<semaphore.h>”,而SYSTEM V信号量的引用头文件是“<sys/sem.h>”。

3.从使用的角度,System V信号量是复杂的,而Posix信号量相对简单。比如,POSIX信号量的创建和初始化或PV操作就很非常方便。

三、SYSTEM V信号量

这是信号量值的集合,而不是单个信号量。相关的信号量操作函数由<sys/ipc.h>引用。

1.信号量结构体

内核为每个信号量集维护一个信号量结构体,可在<sys/sem.h>找到该定义:

struct semid_ds {

struct ipc_perm sem_perm; /* 信号量集的操作许可权限 */

struct sem *sem_base; /* 某个信号量sem结构数组的指针,当前信号量集

中的每个信号量对应其中一个数组元素 */

ushort sem_nsems; /* sem_base 数组的个数 */

time_t sem_otime; /* 最后一次成功修改信号量数组的时间 */

time_t sem_ctime; /* 成功创建时间 */

};

struct sem {

ushort semval; /* 信号量的当前值 */

short sempid; /* 最后一次返回该信号量的进程ID 号 */

ushort semncnt; /* 等待semval大于当前值的进程个数 */

ushort semzcnt; /* 等待semval变成0的进程个数 */

};

2.常见的SYSTEM V信号量函数

(a)关键字和描述符

SYSTEM V信号量是SYSTEM V IPC(即SYSTEM V进程间通信)的组成部分,其他的有SYSTEM V消息队列,SYSTEM
V共享内存。而关键字和IPC描述符无疑是它们的共同点,也使用它们,就不得不先对它们进行熟悉。这里只对SYSTEM V信号量进行讨论。

IPC描述符相当于引用ID号,要想使用SYSTEM V信号量(或MSG、SHM),就必须用IPC描述符来调用信号量。而IPC描述符是内核动态提供的(通过semget来获取),用户无法让服务器和客户事先认可共同使用哪个描述符,所以有时候就需要到关键字KEY来定位描述符。

某个KEY只会固定对应一个描述符(这项转换工作由内核完成),这样假如服务器和客户事先认可共同使用某个KEY,那么大家就都能定位到同一个描述符,也就能定位到同一个信号量,这样就达到了SYSTEM
V信号量在进程间共享的目的。

(b)创建和打开信号量

int semget(key_t key, int nsems, int oflag)

(1) nsems>0 : 创建一个信的信号量集,指定集合中信号量的数量,一旦创建就不能更改。

(2) nsems==0 : 访问一个已存在的集合

(3) 返回的是一个称为信号量标识符的整数,semop和semctl函数将使用它。

(4) 创建成功后信号量结构被设置:

.sem_perm 的uid和gid成员被设置成的调用进程的有效用户ID和有效组ID

.oflag 参数中的读写权限位存入sem_perm.mode

.sem_otime 被置为0,sem_ctime被设置为当前时间

.sem_nsems 被置为nsems参数的值

该集合中的每个信号量不初始化,这些结构是在semctl,用参数SET_VAL,SETALL初始化的。

semget函数执行成功后,就产生了一个由内核维持的类型为semid_ds结构体的信号量集,返回semid就是指向该信号量集的索引。

(c)关键字的获取

有多种方法使客户机和服务器在同一IPC结构上会合:

(1) 服务器可以指定关键字IPC_PRIVATE创建一个新IPC结构,将返回的标识符存放在某处(例如一个文件)以便客户机取用。关键字
IPC_PRIVATE保证服务器创建一个新IPC结构。这种技术的缺点是:服务器要将整型标识符写到文件中,然后客户机在此后又要读文件取得此标识符。

IPC_PRIVATE关键字也可用于父、子关系进程。父进程指定 IPC_PRIVATE创建一个新IPC结构,所返回的标识符在fork后可由子进程使用。子进程可将此标识符作为exec函数的一个参数传给一个新程序。

(2) 在一个公用头文件中定义一个客户机和服务器都认可的关键字。然后服务器指定此关键字创建一个新的IPC结构。这种方法的问题是该关键字可能已与一个 IPC结构相结合,在此情况下,get函数(msgget、semget或shmget)出错返回。服务器必须处理这一错误,删除已存在的IPC结构,然后试着再创建它。当然,这个关键字不能被别的程序所占用。

(3) 客户机和服务器认同一个路径名和课题I D(课题I D是0 ~ 2 5 5之间的字符值),然后调用函数ftok将这两个值变换为一个关键字。这样就避免了使用一个已被占用的关键字的问题。

使用ftok并非高枕无忧。有这样一种例外:服务器使用ftok获取得一个关键字后,该文件就被删除了,然后重建。此时客户端以此重建后的文件来ftok所获取的关键字就和服务器的关键字不一样了。所以一般商用的软件都不怎么用ftok。

一般来说,客户机和服务器至少共享一个头文件,所以一个比较简单的方法是避免使用ftok,而只是在该头文件中存放一个大家都知道的关键字。

(d)设置信号量的值(PV操作)

int semop(int semid, struct sembuf *opsptr, size_t nops);

(1) semid: 是semget返回的semid

(2) opsptr: 指向信号量操作结构数组

(3) nops : opsptr所指向的数组中的sembuf结构体的个数

struct sembuf {

short sem_num; // 要操作的信号量在信号量集里的编号,

short sem_op; // 信号量操作

short sem_flg; // 操作表示符

};

(4) 若sem_op 是正数,其值就加到semval上,即释放信号量控制的资源

若sem_op 是0,那么调用者希望等到semval变为0,如果semval是0就返回;

若sem_op 是负数,那么调用者希望等待semval变为大于或等于sem_op的绝对值

例如,当前semval为2,而sem_op = -3,那么怎么办?

注意:semval是指semid_ds中的信号量集中的某个信号量的值。

(5) sem_flg

SEM_UNDO 由进程自动释放信号量

IPC_NOWAIT 不阻塞

到这里,读者肯定有个疑惑:semop希望改变的semval到底在哪里?我们怎么没看到有它的痕迹?其实,前面已经说明了,当使用semget时,就产生了一个由内核维护的信号量集(当然每个信号量值即semval也是只由内核才能看得到了),用户能看到的就是返回的semid。内核通过semop
函数的参数,知道应该去改变semid 所指向的信号量的哪个semval。

(e)对信号集实行控制操作(semval的赋值等)

int semctl(int semid, int semum, int cmd, ../* union semun arg */);

semid是信号量集合;

semnum是信号在集合中的序号;

semun是一个必须由用户自定义的结构体,在这里我们务必弄清楚该结构体的组成:

union semun

{

int val; // cmd == SETVAL

struct semid_ds *buf // cmd == IPC_SET或者 cmd == IPC_STAT

ushort *array; // cmd == SETALL,或 cmd = GETALL

};

val只有cmd ==SETVAL时才有用,此时指定的semval = arg.val。

注意:当cmd == GETVAL时,semctl函数返回的值就是我们想要的semval。千万不要以为指定的semval被返回到arg.val中。

array指向一个数组,当cmd==SETALL时,就根据arg.array来将信号量集的所有值都赋值;当cmd ==GETALL时,就将信号量集的所有值返回到arg.array指定的数组中。buf
指针只在cmd==IPC_STAT 或IPC_SET 时有用,作用是semid 所指向的信号量集(semid_ds结构体)。一般情况下不常用,这里不做谈论。

另外,cmd == IPC_RMID还是比较有用的。

一个简单的实例如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
void main()
{
key_t unique_key;
int id,rslt;
struct sembuf lock_it; 

union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}options;

//union semun options;
unsigned short array[80];
int i;

options.array=array;

unique_key = ftok(".", 'a');
/* 创建一个新的信号量集合*/
id = semget(unique_key, 1, IPC_CREAT | IPC_EXCL | 0666);
printf("semaphore id=%d\n", id);
/*获取信号量的原始值*/
rslt = semctl(id, 0, GETVAL);
printf("val = %d\n",rslt);

options.val = 1; /*同一时间只允许一个占有者*/
semctl(id, 0, SETVAL, options); /*设置索引0的信号量*/

/*打印出信号量的值*/
i = semctl(id, 0, GETVAL, 0);
printf("new val =  %d\n", i);

/*下面重新设置信号量*/
lock_it.sem_num = 0; /*设置哪个信号量*/
lock_it.sem_op = -1; /*定义操作*/
lock_it.sem_flg = IPC_NOWAIT; /*操作方式*/
if (semop(id, &lock_it, 1) == -1) {//申请信号量,尝试锁定
printf("can not lock semaphore.\n");
exit(1);
}

i = semctl(id, 0, GETVAL, 0);
printf("value of semaphore at index 0 is %d\n", i);

/*清除信号量*/
semctl(id, 0, IPC_RMID, 0);
}

linux进程通信之SYSTEM V信号量

时间: 2024-11-08 01:47:42

linux进程通信之SYSTEM V信号量的相关文章

linux进程间通讯-System V IPC 信号量

进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信--使用信号.下面就进入信号量的讲解. 一.什么是信号量 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域.临界区域是指执行数据更新的代码需要独占式地执行.而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在

Linux 进程通信之 ——信号和信号量总结

如今最经常使用的进程间通信的方式有:信号,信号量,消息队列,共享内存.       所谓进程通信,就是不同进程之间进行一些"接触",这种接触有简单,也有复杂.机制不同,复杂度也不一样.通信是一个广义上的意义,不仅仅指传递一些massege.他们的用法是基本相同的,所以仅仅要掌握了一种的用法,然后记住其他的用法就能够了. 1. 信号       在我学习的内容中,主要接触了信号来实现同步的机制,据说信号也能够用来做其他的事      情,可是我还不知道做什么.       信号和信号量是

Linux IPC实践(11) --System V信号量(1)

信号量API #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg); int semctl(int semid, int semnum, int cmd, ...); int semop(int semid, struct sembuf *sops, unsigned nsops); semget int

Linux IPC实践(12) --System V信号量(2)

实践1:信号量实现进程互斥 父子进程执行流程如下: 父进程 子进程 P P O(print) X(print) sleep sleep O(print) X(print) V V sleep sleep 从图中可以看出, O或X总是成对出现的, 要么两个O, 要么两个X; /**P,V原语实现父子进程互斥使用终端**/ // 程序代码 int main(int argc,char *argv[]) { int semid = sem_create(IPC_PRIVATE); sem_setval

Linux程序设计学习笔记----System V进程通信(共享内存)

转载请注明出处:http://blog.csdn.net/suool/article/details/38515863 共享内存可以被描述成内存一个区域(段)的映射,这个区域可以被更多的进程所共享.这是IPC机制中最快的一种形式,因为它不需要中间环节,而是把信息直接从一个内存段映射到调用进程的地址空间. 一个段可以直接由一个进程创建,随后,可以有任意多的进程对其读和写.但是,一旦内存被共享之后,对共享内存的访问同步需要由其他 IPC 机制,例如信号量来实现.象所有的System V IPC 对象

Linux程序设计学习笔记----System V进程通信之消息队列

一个或多个进程可向消息队列写入消息,而一个或多个进程可从消息队列中读取消息,这种进程间通讯机制通常使用在客户/服务器模型中,客户向服务器发送请求消息,服务器读取消息并执行相应请求.在许多微内核结构的操作系统中,内核和各组件之间的基本通讯方式就是消息队列.例如,在 MINIX 操作系统中,内核.I/O 任务.服务器进程和用户进程之间就是通过消息队列实现通讯的. Linux中的消息可以被描述成在内核地址空间的一个内部链表,每一个消息队列由一个IPC的标识号唯一的标识.Linux 为系统中所有的消息队

Linux程序设计学习笔记----System V进程间通信(信号量)

关于System V Unix System V,是Unix操作系统众多版本中的一支.它最初由AT&T开发,在1983年第一次发布,因此也被称为AT&T System V.一共发行了4个System V的主要版本:版本1.2.3和4.System V Release 4,或者称为SVR4,是最成功的版本,成为一些UNIX共同特性的源头,例如"SysV 初始化脚本"(/etc/init.d),用来控制系统启动和关闭,System V Interface Definitio

Linux系统编程——进程同步与互斥:System V 信号量

信号量概述 信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问. 编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞.PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1. 在实际应用中两个进程间通信可能会使用多个信号量,因此 System V 的信号量以集合的概念来管理,具体操作和Posix 信号量大同小异,详情请点此链接:http://blog.cs

Linux互斥与同步应用(五):system V信号量的互斥与同步

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet 或 .../gentleliu,文章仅供学习交流,请勿用于商业用途] system V信号量操作类似于posix信号量,但system V信号量的操作要复杂得多,posix信号量使用步骤为sem_init(sem_open)-->sem_wait(sem_post) --> sem_close详见上一节,system V使用不同的函数. 1. 创建和打开信号量函数:semget(). #include <