最全面的Linux信号量学习
操作系统信号量与PV操作

操作系统信号量与PV操作操作系统中的信号量是一种并发控制机制,用于对进程间共享的资源进行同步和互斥操作。
PV操作(也称为P操作和V操作)是信号量的两个基本操作,用于实现对信号量的减操作和增操作。
下面将详细介绍信号量和PV操作的概念、原理和应用。
一、信号量的概念:信号量是一种用于进程间通信和同步的工具,通过对信号量的操作来实现对共享资源的控制。
信号量的初值为非负整数,可以看作是一个计数器。
信号量的值表示可用资源的数量,当值大于0时表示有可用资源,当值为0时表示没有可用资源,当值小于0时表示有进程等待资源。
二、PV操作的原理:PV操作是对信号量进行加减操作,具体含义如下:1. P操作(wait操作):当进程需要使用一个资源时,首先执行P 操作。
P操作将信号量的值减1,如果值小于0,则进程被阻塞,等待资源的释放。
2. V操作(signal操作):当进程使用完一个资源后,需要释放资源,此时执行V操作。
V操作将信号量的值加1,如果值小于等于0,则唤醒等待资源的进程。
三、应用场景:信号量和PV操作在许多操作系统中被广泛应用,常见的应用场景如下:1.进程同步:信号量用于控制多个进程的执行顺序和互斥访问共享资源,确保进程间的顺序执行和资源的正确访问。
例如多个进程需要按照一定的顺序执行,可以使用信号量控制进程的执行顺序;多个进程需要互斥地访问一些共享资源,可以使用信号量进行同步。
2.互斥锁:信号量可以用于实现互斥锁,防止多个进程同时访问临界区。
通过将信号量初值设为1,并在进程需要访问临界区时执行P操作,实现对临界区的互斥访问。
3.生产者-消费者问题:信号量可以用于解决生产者-消费者问题,其中生产者和消费者共享一个有限大小的缓冲区。
通过定义两个信号量,一个表示空缓冲区的数量,一个表示满缓冲区的数量,可以实现生产者和消费者的同步和互斥访问。
4.读者-写者问题:信号量可以用于解决读者-写者问题,其中多个读者可以同时读取共享资源,但只有一个写者能够写入共享资源。
信号量底层原理

信号量底层原理
信号量是一种线程同步工具,用于控制多个线程间的并发访问。
信号量底层原理一般基于原子操作和操作系统的原语实现。
常见的实现方式有基于计数器的二进制信号量和计数信号量。
1. 二进制信号量:
二进制信号量只有两个状态,通常用0表示锁定状态,用1表示解锁状态。
其底层原理一般基于原子操作实现,在多线程访问时,先检查信号量的状态,如果为锁定状态则等待,直到状态被解锁,则将状态设置为锁定状态,并进行相应的操作。
2. 计数信号量:
计数信号量的状态是一个整数,通常表示资源的可用数量。
其底层原理也基于原子操作,它包括两个主要操作:P操作和V
操作。
- P操作(也称为wait操作):当一个线程想要访问一个资源时,首先检查计数信号量的状态,如果计数大于0,则将计数
减1,并继续执行该线程的任务,否则线程被阻塞直到计数大
于0。
- V操作(也称为signal操作):当一个线程使用完一个资源后,它会通过V操作来释放资源,将计数信号量的计数加1,表示该资源可用。
在操作系统中,信号量的实现通常依赖于硬件的原子操作或者操作系统提供的原语,例如使用特定的机器指令或系统调用来
保证原子性。
不同的操作系统和编程语言可能提供不同的信号量实现方式,但底层的原理类似。
Linux之信号量,比较全面,个人总结。

信号量一.什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
信号量的值为正的时候,说明它空闲。
所测试的线程可以锁定而使用它。
若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。
二.信号量的分类在学习信号量之前,我们必须先知道——Linux提供两种信号量:POSIX信号量又分为有名信号量和无名信号量。
有名信号量,其值保存在文件中, 所以它可以用于线程也可以用于进程间的同步。
无名信号量,其值保存在内存中。
倘若对信号量没有以上的全面认识的话,你就会很快发现自己在信号量的森林里迷失了方向。
三.内核信号量1.内核信号量的构成内核信号量类似于自旋锁,因为当锁关闭着时,它不允许内核控制路径继续进行。
然而,当内核控制路径试图获取内核信号量锁保护的忙资源时,相应的进程就被挂起。
只有在资源被释放时,进程才再次变为可运行。
只有可以睡眠的函数才能获取内核信号量;中断处理程序和可延迟函数都不能使用内核信号量。
count:相当于信号量的值,大于0,资源空闲;等于0,资源忙,但没有进程等待这个保护的资源;小于0,资源不可用,并至少有一个进程等待资源。
wait:存放等待队列链表的地址,当前等待资源的所有睡眠进程都会放在这个链表中。
sleepers:存放一个标志,表示是否有一些进程在信号量上睡眠。
2.内核信号量中的等待队列(删除,没有联系)上面已经提到了内核信号量使用了等待队列wait_queue来实现阻塞操作。
当某任务由于没有某种条件没有得到满足时,它就被挂到等待队列中睡眠。
当条件得到满足时,该任务就被移出等待队列,此时并不意味着该任务就被马上执行,因为它又被移进工作队列中等待CPU资源,在适当的时机被调度。
内核信号量是在内部使用等待队列的,也就是说该等待队列对用户是隐藏的,无须用户干涉。
由用户真正使用的等待队列我们将在另外的篇章进行详解。
3.内核信号量的相关函数(2)申请内核信号量所保护的资源:4.内核信号量的使用例程在驱动程序中,当多个线程同时访问相同的资源时(驱动中的全局变量时一种典型的共享资源),可能会引发“竞态“,因此我们必须对共享资源进行并发控制。
linux sem用法

linux sem用法Linux系统中的sem(信号量)是一种用于进程间通信和同步的机制。
它提供了一种多个进程之间能够互斥地访问共享资源的方法。
在本文中,我将详细介绍sem的用法,并一步一步地回答您的问题。
第一步:什么是sem?Sem是由Linux内核提供的一种进程同步原语。
它允许进程在对共享资源的访问上进行同步,以避免竞争条件的发生。
它可以用于实现互斥访问共享资源的目的。
第二步:sem的创建和初始化在Linux中,可以使用semget()函数创建一个新的信号量,或者使用semget()函数打开一个已经存在的信号量。
创建新的信号量时,需要指定一个键值,这个键值标识了信号量的唯一性。
创建一个新的信号量的示例代码如下:cint semid = semget(key, 1, IPC_CREAT IPC_EXCL 0666);初始化信号量的值可以使用semctl()函数,其中第二个参数是信号量的编号,第三个参数是命令,第四个参数是信号量的初始值。
初始化信号量的示例代码如下:cunsigned short sem_array[1] = {1}; 初始信号量为1union semun arg;arg.array = sem_array;semctl(semid, 0, SETALL, arg);第三步:sem的P操作和V操作一旦创建和初始化了信号量,就可以使用semop()函数对信号量进行P操作(减法操作)和V操作(加法操作)。
P操作会将信号量的值减1,如果信号量的值为0,则进程将会被阻塞,直到信号量的值大于0为止。
V操作会将信号量的值加1。
如果有多个进程在等待该信号量,则会选择其中一个进行唤醒。
P操作的示例代码如下:cstruct sembuf sb;sb.sem_num = 0;sb.sem_op = -1;sb.sem_flg = SEM_UNDO; semop(semid, &sb, 1);V操作的示例代码如下:cstruct sembuf sb;sb.sem_num = 0;sb.sem_op = 1;sb.sem_flg = SEM_UNDO; semop(semid, &sb, 1);第四步:sem的销毁当信号量不再需要时,可以使用semctl()函数进行销毁。
linuxc语言哲学家进餐---信号量PV方法一

linuxc语⾔哲学家进餐---信号量PV⽅法⼀1、实验原理 由Dijkstra提出并解决的哲学家进餐问题(The Dinning Philosophers Problem)是典型的同步问题。
该问题是描述有五个哲学家共⽤⼀张圆桌,分别坐在周围的五张椅⼦上,在圆桌上有五个碗和五只筷⼦,他们的⽣活⽅式是交替地进⾏思考和进餐。
平时,⼀个哲学家进⾏思考,饥饿时便试图取⽤其左右最靠近他的筷⼦,只有在他拿到两只筷⼦时才能进餐。
进餐完毕,放下筷⼦继续思考。
2.实验内容:显⽰出每个哲学家的⼯作状态,如吃饭,思考。
连续运⾏30次以上都未出现死锁现象。
3.分析解决⽅案⼀:现在引⼊问题的关键:这些哲学家很穷,只买得起五根筷⼦。
他们坐成⼀圈,两个⼈的中间放⼀根筷⼦。
哲学家吃饭的时候必须同时得到左⼿边和右⼿边的筷⼦。
如果他⾝边的任何⼀位正在使⽤筷⼦,那他只有等着。
所以我们就假设最多只有4民哲学家在进餐这样就能保证没有死锁的情况。
代码如下:1 #include<unistd.h>2#define NUM 53int ID[NUM]={0,1,2,3,4};4 sem_t sem_chopsticks[NUM];5 sem_t sem_eaters;6int eaters_num=0;//记录已经吃过饭的次数78//初始化信号量函数9void sem_signal_init(){10int i;11for(i=0;i<NUM;i++){12if(sem_init(&sem_chopsticks[i],0,1)==-1){13 perror("oops:em_init error!");14 exit(1);15 }16 }17if(sem_init(&sem_eaters,0,NUM-1)==-1){18 perror("oops:em_init error!");19 exit(1);20 }212223 }24252627//执⾏函数,每个线程相当于⼀个哲学家来进⾏28void * philosopher(void *ptid){29int pthread_id=*(int *)ptid%NUM;30 printf("%d philosopher is thinking...\n",(int)pthread_id);31 sem_wait(&sem_eaters);32//申请左筷⼦33 sem_wait(&sem_chopsticks[pthread_id]);34 printf("%d philosopher takes chopstick %d...\n",(int)pthread_id,(int)pthread_id);35//申请右筷⼦36 sem_wait(&sem_chopsticks[(pthread_id+1)%NUM]);37 printf("%d philosopher takes chopstick %d...\n",(int)pthread_id,((int)pthread_id+1)%NUM);38 printf("%d philosopher is eating, %d philosopher had already dined.\n",(int)pthread_id,eaters_num);39 sem_post(&sem_chopsticks[(pthread_id+1)%NUM]) ;40 sem_post(&sem_chopsticks[pthread_id]);41 sem_post(&sem_eaters);42 eaters_num++;//吃过⼀次的⼈加加43 printf("%d philosopher had dined, by now %d philosopher had already dined.\n",(int)pthread_id,eaters_num); 4445 }4647484950int main(int argc,char *argv[]){51int i,l,j,k;52//循环五个线程多少次53for(l=0;l<1;++l){54 printf("*******%d times try *******",l+1);55 pthread_t philosopher_threads[NUM];56 sem_signal_init();57//循环创建五个线程并执⾏58for(i=0;i<NUM;i++){5960 printf("%d times\n",i);61if(pthread_create(&philosopher_threads[i],NULL,philosopher,&ID[i])!=0){62 perror("oops:pthread_create error!");63 exit(1);6465 }666768 }6970//⽗线程等待⼦线程都结束才继续执⾏71for(j=0;j<NUM;j++){72 pthread_join(philosopher_threads[j],NULL);73 }74//结束销毁信号量75 sem_destroy(&sem_eaters);76for(k=0;k<NUM;++k){77 sem_destroy(&sem_chopsticks[k]);78 }79 eaters_num=0;80 sleep(2);818283 }84858687return0;88 }运⾏结果如下:1 philosopher is thinking...0 philosopher takes chopstick 0...3 times1 philosopher takes chopstick 1...2 philosopher is thinking...4 times1 philosopher takes chopstick 2...1 philosopher is eating, 0 philosopher had already dined.3 philosopher is thinking...4 philosopher is thinking...1 philosopher had dined, by now 1 philosopher had already dined.2 philosopher takes chopstick 2...0 philosopher takes chopstick 1...0 philosopher is eating, 1 philosopher had already dined.4 philosopher takes chopstick 4...3 philosopher takes chopstick 3...0 philosopher had dined, by now 2 philosopher had already dined.4 philosopher takes chopstick 0...4 philosopher is eating, 2 philosopher had already dined.4 philosopher had dined, by now 3 philosopher had already dined.3 philosopher takes chopstick 4...3 philosopher is eating, 3 philosopher had already dined.3 philosopher had dined, by now4 philosopher had already dined.2 philosopher takes chopstick 3...2 philosopher is eating, 4 philosopher had already dined.2 philosopher had dined, by now 5 philosopher had already dined.--------------------------------Process exited after 2.127 seconds with return value 0请按任意键继续. . .后续⽅案将继续更新请关注。
linux信号量实现原理

linux信号量实现原理【导言】Linux作为开源操作系统,在程序设计和开发方面广泛应用。
而信号量Semaphore作为进程同步和互斥的一种机制,在Linux系统中也广泛使用,特别是在多进程编程中。
本文将从概念、原理、实现等方面为读者深入解析Linux信号量的实现原理。
【正文】一、信号量的概念信号量是一种进程同步机制,用于解决多个并发进程或线程的访问共享资源带来的问题。
它是由E.W. Dijkstra在发明了PV操作之后,发明的一种机制,意味着操作系统的发展。
二、信号量的原理Semaphore本身是一个计数器,用于记录可用资源的数量,资源数量非0即1。
在Linux系统中,信号量一般由一个整数和两个操作——PV操作组成。
P操作,称为down操作,表示试图获取资源,如果可用资源的数量大于0,则占用该资源并将可用资源的数量减1;否则阻塞等待。
V操作,称为up操作,表示释放资源,增加可用资源的数量。
信号量使用可有效避免多个进程、线程对共享资源的相互影响,实现了多个进程之间的同步和互斥,从而保证了系统的稳定性和性能。
三、信号量的实现Semaphore实现主要分为两种:System V IPC信号量和POSIX IPC信号量。
System V IPC信号量是最早开发实现的方法,主要是使用semget、semctl、semop三个函数实现。
而POSIX IPC信号量则相对较新,主要是使用sem_init、sem_destroy、sem_wait、sem_post四个函数实现。
System V IPC信号量的实现需要操作系统调用内核,在一定程度上增加了系统的负担,从而影响了系统的性能和稳定性。
而POSIX IPC信号量则更加灵活、高效、可移植。
四、应用实例Semaphore的应用广泛,可以在多进程、多线程编程、操作系统调度、交通管制等方面使用。
在Linux系统中,Semaphore常常用于控制多个进程对共享文件、共享内存的读写访问,避免产生竞争条件,提高了程序执行效率。
信号量的作用与使用方法

信号量的作用与使用方法信号量是操作系统中一种重要的同步机制,用于控制对共享资源的访问。
它是由信号量的值来表示资源的可用数量,通过对信号量进行操作来实现资源的互斥访问和同步操作。
信号量的作用和使用方法对于理解操作系统的并发控制和同步机制有着重要的意义。
一、信号量的作用1. 控制共享资源的访问:当多个进程需要访问共享资源时,通过信号量可以控制对资源的互斥访问,从而避免进程之间发生冲突,保证数据的一致性和完整性。
2. 进程间的同步操作:通过信号量可以实现多个进程之间的同步操作,例如进程等待某个事件发生,等待某个资源可用,或者通知其他进程某个事件已经发生等。
3. 防止死锁的发生:通过恰当地设置信号量的初值和合理地对其进行操作,可以有效地避免进程间的死锁情况的发生,提高系统的可靠性和稳定性。
4. 实现生产者-消费者问题:信号量可以很好地实现生产者-消费者问题,保证生产者和消费者之间的协调和同步。
二、信号量的使用方法1. 初始化信号量:在使用信号量之前,需要对信号量进行初始化,设置其初值。
可以使用系统提供的函数进行初始化,一般情况下将信号量初值设为资源的可用数量。
2. 调用P操作(等待操作):当进程需要访问资源时,首先需要调用P操作对信号量进行减1操作,表示资源的数量减少一个。
如果此时资源不可用,则阻塞当前进程,直到资源可用。
3. 调用V操作(发信号操作):当进程使用完资源后,需要调用V操作对信号量进行加1操作,表示资源的数量增加一个。
如果有其他进程在等待资源,V操作可以释放资源,唤醒等待的进程。
4. 处理信号量对进程访问资源的控制和同步,保证了系统的安全性和稳定性。
通过以上的作用和使用方法,可以看出信号量在操作系统中的重要性和必要性。
在实际的软件开发过程中,合理地应用信号量可以解决多进程并发访问共享资源的问题,保证系统的正确性和高效性。
同时也需要注意使用信号量的时机和方式,避免出现死锁等问题。
通过深入理解信号量的作用和使用方法,可以更好地掌握操作系统的同步机制和并发控制,提高软件开发的质量和效率。
linux信号量底层原理

linux信号量底层原理信号量是Linux中一种重要的同步机制,用于控制多个进程之间的访问权限,避免竞争条件和死锁的发生。
在Linux中,信号量是一个由内核维护的整数,它可以被多个进程共享,并通过原子操作来实现进程间的同步和互斥。
信号的分类在Linux系统中,信号可以分为三类:实时信号、标准信号和POSIX信号。
实时信号是用来通知进程发生了某种事件或异常的一种机制,可以被发送给进程、进程组或者特定的线程。
标准信号是UNIX系统最早引入的信号类型,用于通知进程发生了某种异常或事件。
POSIX信号是一种与POSIX标准相关的信号类型,用于实现更加标准化的信号机制。
信号量的分类在Linux系统中,信号量可以分为两类:二进制信号量和计数信号量。
二进制信号量是一种只能取0或1两个值的信号量,用于实现互斥操作;计数信号量是一种可以取多个值的信号量,用于实现进程间的计数和同步。
信号量的实现在Linux系统中,信号量是通过内核提供的系统调用函数来实现的。
系统调用函数是由内核提供的接口,用于实现用户空间和内核空间之间的通信和数据交换。
在Linux系统中,信号量的实现主要分为以下几个步骤:1. 创建信号量:首先,一个进程需要通过内核提供的系统调用函数来创建一个信号量。
在创建信号量时,进程需要指定信号量的初始值和权限等参数。
2. 操作信号量:进程可以通过系统调用函数对信号量进行P操作(原语操作)和V操作(释放信号量)。
P操作用于获取信号量,并将其减1;V操作用于释放信号量,并将其加1。
3. 销毁信号量:当一个进程不再需要使用信号量时,可以通过系统调用函数来销毁信号量。
在销毁信号量时,进程需要确保所有使用该信号量的进程都已经释放了该信号量。
信号量的应用信号量是Linux系统中一个非常重要的同步机制,广泛应用于进程间的通信和同步。
以下是信号量的一些常见应用场景:1. 进程间的互斥操作:信号量可以用于控制多个进程对共享资源的访问权限,避免竞争条件和死锁的发生。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
信号量一.什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
信号量的值为正的时候,说明它空闲。
所测试的线程可以锁定而使用它。
若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。
二.信号量的分类LinuxPOSIX信号量又分为有名信号量和无名信号量。
有名信号量,其值保存在文件中, 所以它可以用于线程也可以用于进程间的同步。
无名信号量,其值保存在内存中。
倘若对信号量没有以上的全面认识的话,你就会很快发现自己在信号量的森林里迷失了方向。
三.内核信号量1.内核信号量的构成内核信号量类似于自旋锁,因为当锁关闭着时,它不允许内核控制路径继续进行。
然而,当内核控制路径试图获取内核信号量锁保护的忙资源时,相应的进程就被挂起。
只有在资源被释放时,进程才再次变为可运行。
只有可以睡眠的函数才能获取内核信号量;中断处理程序和可延迟函数都不能使用内核信号量。
内核信号量是struct semaphore类型的对象,它在<asm/semaphore.h>中定义:count:相当于信号量的值,大于0,资源空闲;等于0,资源忙,但没有进程等待这个保护的资源;小于0,资源不可用,并至少有一个进程等待资源。
wait:存放等待队列链表的地址,当前等待资源的所有睡眠进程都会放在这个链表中。
sleepers:存放一个标志,表示是否有一些进程在信号量上睡眠。
2.内核信号量中的等待队列(删除,没有联系)上面已经提到了内核信号量使用了等待队列wait_queue来实现阻塞操作。
当某任务由于没有某种条件没有得到满足时,它就被挂到等待队列中睡眠。
当条件得到满足时,该任务就被移出等待队列,此时并不意味着该任务就被马上执行,因为它又被移进工作队列中等待CPU资源,在适当的时机被调度。
内核信号量是在内部使用等待队列的,也就是说该等待队列对用户是隐藏的,无须用户干涉。
由用户真正使用的等待队列我们将在另外的篇章进行详解。
3.内核信号量的相关函数(1)初始化:2()释放内核信号量所保护的资源:4.内核信号量的使用例程在驱动程序中,当多个线程同时访问相同的资源时(驱动中的全局变量时一种典型的共享资源),可能会引发“竞态“,因此我们必须对共享资源进行并发控制。
Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)。
四.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操作就很非常方便。
五.POSIX信号量详解1.无名信号量无名信号量的创建就像声明一般的变量一样简单,例如:sem_t sem_id。
然后再初始化该无名信号量,之后就可以放心使用了。
无名信号量常用于多线程间的同步,同时也用于相关进程间的同步。
也就是说,无名信号量必须是多个进程(线程)的共享变量,无名信号量要保护的变量也必须是多个进程(线程)的共享变量,这两个条件是缺一不可的。
常见的无名信号量相关函数:sem_destroy注意:在这些函数中,只有sem_post是信号安全的函数,它是可重入函数(a)无名信号量在多线程间的同步无名信号量的常见用法是将要保护的变量放在sem_wait和sem_post中间所形成的上面的例程,到底哪个线程先申请到信号量资源,这是随机的。
如果想要某个特定的顺序的话,可以用2个信号量来实现。
例如下面的例程是线程1先执行完,然后线程2才继续执行,直至结束。
(b)无名信号量在相关进程间的同步说是相关进程,是因为本程序中共有2个进程,其中一个是另外一个的子进程(由fork产生)的。
本来对于fork来说,子进程只继承了父进程的代码副本,mutex理应在父子进程中是相互独立的两个变量,但由于在初始化mutex的时候,由pshared=1指定了mutex处于共享内存区域,所以此时mutex变成了父子进程共享的一个变2.有名信号量有名信号量的特点是把信号量的值保存在文件中。
这决定了它的用途非常广:既可以用于线程,也可以用于相关进程间,甚至是不相关进程。
(a)有名信号量能在进程间共享的原因由于有名信号量的值是保存在文件中的,所以对于相关进程来说,子进程是继承了父进程的文件描述符,那么子进程所继承的文件描述符所指向的文件是和父进程一样的,当然文件里面保存的有名信号量值就共享了。
(b)有名信号量相关函数说明有名信号量在使用的时候,和无名信号量共享sem_wait和sem_post函数。
区别是有名信号量使用sem_open代替sem_init,另外在结束的时候要像关闭文件一样去关闭这个有名信号量。
(1)打开一个已存在的有名信号量,或创建并初始化一个有名信号量。
一个单一的调用就完成了信号量的创建、初始化和权限的设置。
这里的name不能写成/tmp/aaa.sem这样的格式,因为在linux下,sem都是创建在/dev/shm目录下。
你可以将name写成“/mysem”或“mysem”,创建出来的文件都是“/dev/shm/sem.mysem”,千万不要写路径。
也千万不要写“/tmp/mysem”之类的。
当oflag = O_CREAT时,若name指定的信号量不存在时,则会创建一个,而且后面的mode和value参数必须有效。
若name指定的信号量已存在,则直接打开该信号量,同时忽略mode和value参数。
当oflag = O_CREAT|O_EXCL时,若name指定的信号量已存在,该函数会直接返回error。
(2) 一旦你使用了一信号量,销毁它们就变得很重要。
在做这个之前,要确定所有对这个有名信号量的引用都已经通过sem_close()函数关闭了,然后只需在退出或是退出处理函数中调用sem_unlink()去删除系统中的信号量,注意如果有任何的处理器或是线程引用这个信号量,sem_unlink()函数不会起到任何的作用。
也就是说,必须是最后一个使用该信号量的进程来执行sem_unlick才有效。
因为每个信号灯有一个引用计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把name所指的信号灯从文件系统中删除。
也就是要等待最后一个sem_close发生。
(c)有名信号量在无相关进程间的同步前面已经说过,有名信号量是位于共享内存区的,那么它要保护的资源也必须是位于共享内存区,只有这样才能被无相关的进程所共享。
在下面这个例子中,服务进程和客户进程都使用shmget和shmat来获取得一块共享内存资源。
然后利用有名信号量来对这块共享内存资源进行互斥保护。
六.SYSTEM V信号量这是信号量值的集合,而不是单个信号量。
相关的信号量操作函数由<sys/ipc.h>引用。
1.信号量结构体<sys/sem.h>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)创建和打开信号量semget函数执行成功后,就产生了一个由内核维持的类型为semid_ds结构体的信号量集,返回semid就是指向该信号量集的引索。
(c)关键字的获取有多种方法使客户机和服务器在同一IPC结构上会合:(1) 服务器可以指定关键字IPC_PRIV ATE创建一个新IPC结构,将返回的标识符存放在某处(例如一个文件)以便客户机取用。
关键字IPC_PRIV ATE保证服务器创建一个新IPC结构。
这种技术的缺点是:服务器要将整型标识符写到文件中,然后客户机在此后又要读文件取得此标识符。
IPC_PRIV ATE关键字也可用于父、子关系进程。
父进程指定 IPC_PRIV ATE创建一个新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操作)到这里,读者肯定有个疑惑:semop希望改变的semval到底在哪里?我们怎么没看到有它的痕迹?其实,前面已经说明了,当使用semget时,就产生了一个由内核维护的信号量集(当然每个信号量值即semval也是只由内核才能看得到了),用户能看到的就是返回的semid。
内核通过semop函数的参数,知道应该去改变semid所指向的信号量的哪个semval。