Linux之信号量,比较全面,个人总结。
- 格式:doc
- 大小:150.50 KB
- 文档页数:18
Linux信号量信号在Linux中是⼀个⽐较常见的概念,例如我们按Ctrl+C中断前台进程,通过Kill命令结束进程都是通过信号实现的。
下⾯就以Ctrl+C为例简单的说明信号的处理流程:⽤户按下Ctrl-C,这个键盘输⼊产⽣⼀个硬件中断。
该进程的⽤户空间代码暂停执⾏,CPU从⽤户态切换到内核态处理硬件中断。
终端驱动程序将Ctrl-C解释成⼀个SIGINT信号,记在该进程的PCB中(也可以说发送了⼀个SIGINT信号给该进程)。
当内核返回到该进程的⽤户空间代码继续执⾏之前,⾸先处理PCB中记录的信号,发现有⼀个SIGINT信号待处理,⽽这个信号的默认处理动作是终⽌进程,所以直接终⽌进程⽽不再返回它的⽤户空间代码执⾏。
kill -l 命令可看到Linux⽀持的信号列表:1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR111) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+338) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+843) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+1348) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-1253) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-758) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-263) SIGRTMAX-1 64) SIGRTMAX列表中,编号为1 ~ 31的信号为传统UNIX⽀持的信号,是不可靠信号(⾮实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。
Linux⾼级调试与优化——信号量机制与应⽤程序崩溃背景介绍 Linux分为内核态和⽤户态,⽤户态通过进⼊内核态执⾏。
⽤户空间的glibc库将Linux内核系统调⽤封装成GNU C Library库⽂件(兼容ANSI & POSIX C语⾔标准),同时提供了其他特性的⽀持。
应⽤程序通常不是直接调⽤Linux内核的系统调⽤接⼝,⽽是通过glibc库封装的接⼝间接调⽤Linux内核系统调⽤。
信号量机制 关于Linux信号量机制的原理,建议阅读《Unix环境⾼级编程》第10章,本博客只是简单介绍其原理。
信号量是⼀种软中断,⽤来实现Linux内核和应⽤程序之间的异步通信。
每⼀个信号量由⼀个4字节整形数据表⽰。
可以通过man 7 signal查看所有信号量描述。
其中1~32号信号量是从Unix继承⽽来,33~64是Linux内核定义的信号量。
Linux内核为每个信号量设置了默认处理动作,如Term(终⽌执⾏)、Ign(忽略)、Core(终⽌执⾏并产⽣coredump)、Stop(停⽌运⾏)和Cont(继续运⾏),当应⽤程序接收到某个信号量时,则按照默认处理动作执⾏。
应⽤程序也可以通过sigaction()或者signal()函数修改默认的处理动作,⽐如屏蔽或者忽略某个信号量等。
应⽤程序信号量处理函数通常是链接glibc中默认的信号量处理函数,也可以⾃⼰编写和指定信号量处理函数。
但是需要注意的是,SIGKILL和SIGSTOP信号量不能被捕获(caught)、屏蔽(blocked)或者忽略(ignored)。
信号量触发情况有三种: 1)Linux内核检测到应⽤程序异常,发送特定的信号量给应⽤程序,应⽤程序捕获到信号量后,调⽤信号量处理函数; 2)Linux内核因为内部事件⽽给应⽤程序发送特定信号,通知应⽤程序发⽣了某个事件,如著名的segmentation fault,应⽤程序捕获到信号量后,调⽤信号量处理函数; 3)Linux内核检测到外部事件,如Ctrl+C,Ctrl+Z等,发送特定信号给应⽤程序,应⽤程序捕获到信号量后,调⽤信号量处理函数;Signal Value Action CommentSIGHUP1Term Hangup detected on controlling terminal or death of controlling processSIGINT2Term Interrupt from keyboardSIGQUIT3Core Quit from keyboardSIGILL4Core Illegal InstructionSIGTRAP5Core Trace/breakpoint trapSIGABRT6Core Abort signal from abort(3)SIGIOT6Core IOT trap. A synonym for SIGABRTSIGEMT7TermSIGFPE8Core Floating point exceptionSIGKILL9Term Kill signal, cannot be caught, blocked or ignored.SIGBUS10,7,10Core Bus error (bad memory access)SIGSEGV11Core Invalid memory referenceSIGPIPE13Term Broken pipe: write to pipe with no readersSIGALRM14Term Timer signal from alarm(2)SIGTERM15Term Termination signalSIGUSR130,10,16Term User-defined signal 1SIGUSR231,12,17Term User-defined signal 2SIGCHLD20,17,18Ign Child stopped or terminatedSIGCONT19,18,25Cont Continue if stoppedSIGSTOP17,19,23Stop Stop process, cannot be caught, blocked or ignored.SIGTSTP18,20,24Stop Stop typed at terminalSIGTTIN21,21,26Stop Terminal input for background processSIGTTOU22,22,27Stop Terminal output for background processSIGIO23,29,22Term I/O now possible (4.2BSD)SIGPOLL Term Pollable event (Sys V). Synonym for SIGIOSIGPROF27,27,29Term Profiling timer expiredSIGSYS12,31,12Core Bad argument to routine (SVr4)SIGURG16,23,21Ign Urgent condition on socket (4.2BSD)SIGVTALRM 26,26,28Term Virtual alarm clock (4.2BSD)SIGVTALRM 26,26,28Term Virtual alarm clock (4.2BSD)SIGXCPU24,24,30Core CPU time limit exceeded (4.2BSD)SIGXFSZ25,25,31Core File size limit exceeded (4.2BSD)SIGSTKFLT16Term Stack fault on coprocessor (unused)SIGCLD18Ign A synonym for SIGCHLDSIGPWR29,30,19Term Power failure (System V)SIGINFO29 A synonym for SIGPWR, on an alphaSIGLOST29Term File lock lost (unused), on a sparcSIGWINCH28,28,20Ign Window resize signal (4.3BSD, Sun)SIGUNUSED31Core Synonymous with SIGSYS信号量进阶 关于⾃定义信号量处理函数、屏蔽指定信号量等操作,参考 信号量处理函数实质上是应⽤程序发⽣异常时的⼀种修复或者调试机制,因为信号量处理不是正常的函数调⽤,因此它会复⽤⽗函数的栈,如果信号量处理函数中发⽣了异常,系统是没有办法处理的,因此,信号量处理函数必须是安全可靠的。
信号量一.什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
信号量的值为正的时候,说明它空闲。
所测试的线程可以锁定而使用它。
若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。
二.信号量的分类在学习信号量之前,我们必须先知道——Linux提供两种信号量:POSIX信号量又分为有名信号量和无名信号量。
有名信号量,其值保存在文件中, 所以它可以用于线程也可以用于进程间的同步。
无名信号量,其值保存在内存中。
倘若对信号量没有以上的全面认识的话,你就会很快发现自己在信号量的森林里迷失了方向。
三.内核信号量1.内核信号量的构成内核信号量类似于自旋锁,因为当锁关闭着时,它不允许内核控制路径继续进行。
然而,当内核控制路径试图获取内核信号量锁保护的忙资源时,相应的进程就被挂起。
只有在资源被释放时,进程才再次变为可运行。
只有可以睡眠的函数才能获取内核信号量;中断处理程序和可延迟函数都不能使用内核信号量。
count:相当于信号量的值,大于0,资源空闲;等于0,资源忙,但没有进程等待这个保护的资源;小于0,资源不可用,并至少有一个进程等待资源。
wait:存放等待队列链表的地址,当前等待资源的所有睡眠进程都会放在这个链表中。
sleepers:存放一个标志,表示是否有一些进程在信号量上睡眠。
2.内核信号量中的等待队列(删除,没有联系)上面已经提到了内核信号量使用了等待队列wait_queue来实现阻塞操作。
当某任务由于没有某种条件没有得到满足时,它就被挂到等待队列中睡眠。
当条件得到满足时,该任务就被移出等待队列,此时并不意味着该任务就被马上执行,因为它又被移进工作队列中等待CPU资源,在适当的时机被调度。
内核信号量是在内部使用等待队列的,也就是说该等待队列对用户是隐藏的,无须用户干涉。
由用户真正使用的等待队列我们将在另外的篇章进行详解。
3.内核信号量的相关函数(2)申请内核信号量所保护的资源:4.内核信号量的使用例程在驱动程序中,当多个线程同时访问相同的资源时(驱动中的全局变量时一种典型的共享资源),可能会引发“竞态“,因此我们必须对共享资源进行并发控制。
Linux编程之信号量信号量分 System V 信号量和 POSIX 信号量,这⾥仅介绍 POSIX 信号量。
1. 概述2. 命令信号量3. 信号量操作3.1 等待⼀个信号量sem_wait() 函数会递减(减⼩ 1)sem 引⽤的信号量的值。
#include <semaphore.h>int sem_wait(sem_t *sem);如果信号量的当前值⼤于 0,那么 sem_wait() 会⽴即返回。
如果信号量的当前值等于 0,那么 sem_wait() 会阻塞直到信号量的值⼤于 0 为⽌,当信号量值⼤于 0 时该信号量值就被递减并且sem_wait() 会返回。
如果⼀个阻塞的 sem_wait() 调⽤被⼀个信号处理器中断了,那么它就会失败并返回 EINTR 错误,不管在使⽤ sigaction() 建⽴这个信号处理器时是否采⽤了 SA_RESTART 标记。
(在其他⼀些 UNIX 实现上,SA_RESTART 会导致 sem_wait() ⾃动重启。
)3.1.1 sem_trywait()sem_trywait() 函数是 sem_wait() 的⼀个⾮阻塞版本。
#include <semaphore.h>int sem_trywait(sem_t *sem);如果递减操作⽆法⽴即被执⾏,那么 sem_trywait() 就会失败并返回 EAGAIN 错误。
3.1.2 sem_timedwait()sem_timedwait() 函数是 sem_wait() 的另⼀个变体,它允许调⽤者为调⽤被阻塞的时间量指定⼀个限制。
#define _XOPEN_SOURCE 600#include <semaphore.h>int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);如果 sem_timedwait() 调⽤因超时⽽⽆法递减信号量,那么这个调⽤就会失败并返回 ETIMEDOUT 错误。
linux信号量机制(semaphore)信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。
当公共资源增加时,调用函数sem_post()增加信号量。
只有当信号量值大于0时,才能使用公共资源,使用后,函数sem_wait()减少信号量。
函数sem_trywait()和函数pthread_ mutex_trylock()起同样的作用,它是函数sem_wait()的非阻塞版本。
它们都在头文件/usr/include/semaphore.h中定义。
信号量的数据类型为结构sem_t,它本质上是一个长整型的数。
函数sem_init()用来初始化一个信号量。
它的原型为:extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。
函数sem_post( sem_t *sem )用来增加信号量的值。
当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。
函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。
函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。
函数sem_destroy(sem_t *sem)用来释放信号量sem。
例1:使用信号量。
例子中一共有4个线程,其中两个线程负责从文件读取数据到公共的缓冲区,另两个线程从缓冲区读取数据作不同的处理(加和乘运算)。
/* File sem.c */#include <stdio.h>#include <pthread.h>#include <semaphore.h>#define MAXSTACK 100int stack[MAXSTACK][2];int size=0;sem_t sem;/* 从文件1.dat读取数据,每读一次,信号量加一*/void ReadData1(void){FILE *fp=fopen("1.dat","r");while(!feof(fp)){fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);sem_post(&sem);++size;}fclose(fp);}/*从文件2.dat读取数据*/void ReadData2(void){FILE *fp=fopen("2.dat","r");while(!feof(fp)){fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);sem_post(&sem);++size;}fclose(fp);}/*阻塞等待缓冲区有数据,读取数据后,释放空间,继续等待*/ void HandleData1(void){while(1){sem_wait(&sem);printf("Plus:%d+%d=%d\n",stack[size][0],stack[size][1],stack[size][0]+stack[size][1]);--size;}}void HandleData2(void){while(1){sem_wait(&sem);printf("Multiply:%d*%d=%d\n",stack[size][0],stack[size][1],stack[size][0]*stack[size][1]);--size;}}int main(void){pthread_t t1,t2,t3,t4;sem_init(&sem,0,0);pthread_create(&t1,NULL,(void *)HandleData1,NULL);pthread_create(&t2,NULL,(void *)HandleData2,NULL);pthread_create(&t3,NULL,(void *)ReadData1,NULL);pthread_create(&t4,NULL,(void *)ReadData2,NULL);/* 防止程序过早退出,让它在此无限期等待*/pthread_join(t1,NULL);}在Linux下,用命令gcc -lpthread sem.c -o sem生成可执行文件sem。
大作业---Linux信号量机制姓名:学号:班级:010914日期:2012年5月23日进程A、B、C,分别调用过程get、copy、put对缓冲区S和T进行操作。
其中get负责从文件F_in中把数据块读入并输出缓冲区S,copy负责从S中提取数据块并复制到缓冲区T中,put负责从缓冲区T中取出信息存入到本地文件F_out中。
实现get、copy、put的操作过程。
get copy put缓冲区S 缓冲区T功能需求:● 提供与用户交互的界面,用户可指定输入、输出文件以及缓冲区大小● 利用信号量实现互斥● 同时产生多个get、copy和put线程,利用信号量实现多个相同功能的线程间的通信,避免临界资源的非法访问,可参考读写者问题解决方案● 支持文件输入、输出非功能需求:● 程序应有较好的容错性(即能对用户输入的命令进行判断,并对错误的命令进行错误处理)过程需求:● 使用vi进行代码的编写● 使用make工具建立工程● 将实现不同类别功能的函数写到不同的.c文件中,并使用makefile链接编译。
2.1 结构设计进程1通过get函数将源文件载入缓冲区S;进程2通过copy函数将数据从缓冲区S复制到缓冲区T内;进程3通过put函数将缓冲区T内的数据输出到目标文件。
2.2 功能设计1.提供与用户交互的界面,用户可指定输入、输出文件以及缓冲区大小。
2.利用信号量实现互斥。
3.同时产生多个get、copy和put线程,利用信号量实现多个相同功能的线程间的通信,避免临界资源的非法访问。
3 测试和使用说明3.1 使用说明1、执行make命令,编译程序test;2、执行test;3、输入源文件的路径4、输入目标文件的文件名5、输入缓冲区大小6、程序执行完毕3.2 测试说明执行make命令,编译test。
输入文件fin内容如上图。
./fin运行可执行程序test,程序请求用户指定输入文件、输出文件和缓冲区大小。
程序获取用户输入后,根据设计要求将输入文件的内容复制到输出文件中,并显示执行过程和执行状态。
进程间通信方式总结——信号量2017/04/14 0 Linux/Unix系统IPC是各种进程间通信方式的统称,但是其中极少能在所有Linux/Unix系统实现中进行移植。
随着POSIX和Open Group (X/Open)标准化的推进呵护影响的扩大,情况虽已得到改善,但差别仍然存在。
一般来说,Linux/Unix常见的进程间通信方式有:管道、消息队列、信号、信号量、共享内存、套接字等。
博主将在《进程间通信方式总结》系列博文中和大家一起探讨学习进程间通信的方式,并对其进行总结,让我们共同度过这段学习的美好时光。
这里我们就以其中一种方式信号量展开探讨,为了防止出现因多个程序同时访问同一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。
临界区域是指执行数据更新的代码需要独占式地执行。
而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。
由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的: P(sv):如果sv的大于零,就给它减1;如果它的为零,就挂起该进程的执行 V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1。
Linux提供了一组精心设计的信号量接口来对信号进行操作,它们不只是针对二进制信号量,下面将会对这些函数进行介绍,但请注意,这些函数都是用来对成组的信号量进行操作的。
它们声明在头文件sys/sem.h中。
下面让我们一起来看看信号量的操作函数吧! 函数介绍semget函数原型:intsemget(key_tkey,intnum_sems,intsem_flags); key:当key 为0(IPC_PRIVATE),会建立新信号量集对象;当为大于0的32位整数,视参数flags来确定操作。
信号量线程控制(1)信号量说明在第8 章中已经讲到,信号量也就是操作系统中所用到的PV 原语,它广泛用于进程或线程间的同步与互斥。
信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。
这里先来简单复习一下PV原语的工作原理。
PV原语是对整数计数器信号量sem的操作。
一次P操作使sem减一,而一次V操作使《嵌入式Linux应用程序开发详解》——第9章、多线程编程sem 加一。
进程(或线程)根据信号量的值来判断是否对公共资源具有访问权限。
当信号量sem 的值大于等于零时,该进程(或线程)具有公共资源的访问权限;相反,当信号量sem 的值小于零时,该进程(或线程)就将阻塞直到信号量sem的值大于等于0 为止。
PV 原语主要用于进程或线程间的同步和互斥这两种典型情况。
若用于互斥,几个进程(或线程)往往只设置一个信号量sem,它们的操作流程如图9.2 所示。
当信号量用于同步操作时,往往会设置多个信号量,并安排不同的初始值来实现它们之间的顺序执行,它们的操作流程如图9.3所示。
图9.2 信号量互斥操作华清远见<嵌入式Linux应用开发班>培____________训教材图9.3 信号量同步操作(2)函数说明Linux 实现了POSIX 的无名信号量,主要用于线程间的互斥同步。
这里主要介绍几个常见函数。
• sem_init用于创建一个信号量,并能初始化它的值。
• sem_wait和sem_trywait相当于P操作,它们都能将信号量的值减一,两者的区别在于若信号量小于零时,sem_wait将会阻塞进程,而sem_trywait则会立即返回。
• sem_post相当于V操作,它将信号量的值加一同时发出信号唤醒等待的进程。
• sem_getvalue用于得到信号量的值。
• sem_destroy用于删除信号量。
(3)函数格式表9.11 列出了sem_init函数的语法要点。
表9.11 sem_init函数语法要点所需头文件#include <semaphore.h>《嵌入式Linux应用程序开发详解》——第9章、多线程编程函数原型int sem_init(sem_t *sem,int pshared,unsigned int value)sem:信号量pshared:决定信号量能否在几个进程间共享。
信号量一.什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
信号量的值为正的时候,说明它空闲。
所测试的线程可以锁定而使用它。
若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。
二.信号量的分类在学习信号量之前,我们必须先知道——Linux提供两种信号量:POSIX信号量又分为有名信号量和无名信号量。
有名信号量,其值保存在文件中, 所以它可以用于线程也可以用于进程间的同步。
无名信号量,其值保存在内存中。
倘若对信号量没有以上的全面认识的话,你就会很快发现自己在信号量的森林里迷失了方向。
三.内核信号量1.内核信号量的构成内核信号量类似于自旋锁,因为当锁关闭着时,它不允许内核控制路径继续进行。
然而,当内核控制路径试图获取内核信号量锁保护的忙资源时,相应的进程就被挂起。
只有在资源被释放时,进程才再次变为可运行。
只有可以睡眠的函数才能获取内核信号量;中断处理程序和可延迟函数都不能使用内核信号量。
count:相当于信号量的值,大于0,资源空闲;等于0,资源忙,但没有进程等待这个保护的资源;小于0,资源不可用,并至少有一个进程等待资源。
wait:存放等待队列链表的地址,当前等待资源的所有睡眠进程都会放在这个链表中。
sleepers:存放一个标志,表示是否有一些进程在信号量上睡眠。
2.内核信号量中的等待队列(删除,没有联系)上面已经提到了内核信号量使用了等待队列wait_queue来实现阻塞操作。
当某任务由于没有某种条件没有得到满足时,它就被挂到等待队列中睡眠。
当条件得到满足时,该任务就被移出等待队列,此时并不意味着该任务就被马上执行,因为它又被移进工作队列中等待CPU资源,在适当的时机被调度。
内核信号量是在内部使用等待队列的,也就是说该等待队列对用户是隐藏的,无须用户干涉。
由用户真正使用的等待队列我们将在另外的篇章进行详解。
3.内核信号量的相关函数(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_post是信号安全的函数,它是可重入函数(a)无名信号量在多线程间的同步无名信号量的常见用法是将要保护的变量放在sem_wait和sem_post中间所形成的上面的例程,到底哪个线程先申请到信号量资源,这是随机的。
如果想要某个特定的顺序的话,可以用2个信号量来实现。
例如下面的例程是线程1先执行完,然后线程2才继续执行,直至结束。
(b)无名信号量在相关进程间的同步说是相关进程,是因为本程序中共有2个进程,其中一个是另外一个的子进程(由fork 产生)的。
本来对于fork来说,子进程只继承了父进程的代码副本,mutex理应在父子进程中是相互独立的两个变量,但由于在初始化mutex的时候,由pshared = 1指定了mutex处于共享内存区域,所以此时mutex变成了父子进程共享的一个变量。
此//open a file and map it into memoryfd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU);write(fd,&zero,sizeof(int));ptr = mmap( NULL,sizeof(int),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );close(fd);/* create, initialize semaphore */if( sem_init(&mutex,1,1) < 0) //{perror("semaphore initilization");exit(0);}if (fork() == 0){ /* child process*/for (i = 0; i < nloop; i++){sem_wait(&mutex);printf("child: %d\n", (*ptr)++);sem_post(&mutex);}exit(0);}/* back to parent process */for (i = 0; i < nloop; i++){sem_wait(&mutex);printf("parent: %d\n", (*ptr)++);sem_post(&mutex);}exit(0);}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来获取得一块共享内#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>#include <stdio.h>#include <semaphore.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define SHMSZ 27char SEM_NAME[]= "vik";int main(){char ch;int shmid;key_t key;char *shm,*s;sem_t *mutex;//name the shared memory segmentkey = 1000;//create & initialize semaphoremutex = sem_open(SEM_NAME,O_CREAT,0644,1);if(mutex == SEM_FAILED){perror("unable to create semaphore");sem_unlink(SEM_NAME);exit(-1);}//create the shared memory segment with this key shmid = shmget(key,SHMSZ,IPC_CREAT|0666);if(shmid<0){perror("failure in shmget");exit(-1);}//attach this segment to virtual memoryshm = shmat(shmid,NULL,0);//start writing into memorys = shm;for(ch='A';ch<='Z';ch++){sem_wait(mutex);*s++ = ch;sem_post(mutex);}//the below loop could be replaced by binary semaphore while(*shm != '*'){sleep(1);}sem_close(mutex);sem_unlink(SEM_NAME);shmctl(shmid, IPC_RMID, 0);exit(0);}<u>File 2: client.c</u>#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>#include <stdio.h>#include <semaphore.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define SHMSZ 27char SEM_NAME[]= "vik";int main(){char ch;int shmid;key_t key;char *shm,*s;sem_t *mutex;//name the shared memory segmentkey = 1000;//create & initialize existing semaphoremutex = sem_open(SEM_NAME,0,0644,0);if(mutex == SEM_FAILED){perror("reader:unable to execute semaphore");sem_close(mutex);exit(-1);}//create the shared memory segment with this keyshmid = shmget(key,SHMSZ,0666);if(shmid<0){perror("reader:failure in shmget");exit(-1);}//attach this segment to virtual memoryshm = shmat(shmid,NULL,0);//start readings = shm;for(s=shm;*s!=NULL;s++){sem_wait(mutex);putchar(*s);sem_post(mutex);}//once done signal exiting of reader:This can be replaced by another semaphore*shm = '*';sem_close(mutex);shmctl(shmid, IPC_RMID, 0);exit(0);}六.SYSTEM V信号量这是信号量值的集合,而不是单个信号量。