信号量
信号量不是用来在进程间传输数据的,而是用来同步进程的动作
一个信号量是一个由内核维护的整数,其值被限制为大于或等于0,在一个信号量上可以执行执行各种操作:
- 将信号量设置成一个绝对值
- 在信号量当前值的基础上加上一个数量
- 在信号量当前值的基础上减去一个数量
- 等待信号量的值等于0
后面两种操作可能会导致调用进程阻塞:
①当减小一个信号量的值时,内核会将所有试图将信号量值降低到0之下的操作阻塞。
②如果当前信号量的值不为0,那么等待信号量等于0的进程就会被阻塞。
简言之,一个进程阻塞时,另一个进程去修改信号量,阻塞的进程就可以继续执行。
未命名信号量
这种信号量没有名字,相反,它位于内存中一个预先商定的位置处。未命名信号量可以在进程之间或一组线程之间共享。当在进程之间共享时,信号量必须位于一个共享内存区域中(System V、POSIX 或 mmap())。当在线程之间共享时, 信号量可以位于被这些线程共享的一块内存区域中(如在堆上或在一个全局变量中)。
未命名信号量的操作
- 打开一个信号量
pshared如果为0,那么信号量将在线程中进行共享,如果不为0,那么信号量将在进程中共享。value为信号量的初始值。
- 等待一个信号量
- 发送一个信号量
如果sem_post()调用之前信号量的值是0,其它进程因为信号量被阻塞,则该进程被唤醒,去增加一个信号量。
未命名信号量在进程间的使用
如果我们想在进程之间使用信号量,那么两个进程必须去访问一个同一个信号量,简而言之,信号量这个变量必须放在一个共享内存上,这样两个进程就可以访问以及改变这个信号量。可以用mmap这个函数去映射一个共享内存。
- addr参数指定了映射被放置的虚拟地址,如果将addr指定为NULL,那么内核会为映射选择一个合适的地址。
- length制定了映射的字节数
- prot是一个位掩码,指定了映射内存的保护空间的属性,可读写即PROT_READ|PROT_WRITE
- flags是一个控制映射操作各个方面的选项的位掩码,共享映射且没有对应文件的匿名映射即MAP_SHARED|MAP_ANONYMOUS.
- fd和offset是用于文件映射的,匿名映射忽略两个参数,这里选择-1和0
- 返回值是新映射的起始地址。
以下程序信号量初始值为1,父进程等待信号量,发现为1,然后信号量递减为0,向下执行打印信息,并等待1s,同时子进程打印信息,等待5s,1s后,父进程继续等待信号量,这个时候,信号量为0,父进程发生阻塞,再过4s子进程发布信号量,此时信号量+1,父进程解除阻塞,如此往复执行。
命名信号量
这种信号量拥有一个名字。通过使用相同的名字调用 sem_open(),不相关的进程能够访问同一个信号量。
命名信号量的操作
- 打开一个信号量
oflag是一个位掩码,它确定是打开一个既有信号量还是创建并打开一个新信号量,如果为0打开一个信号量,为O_CREAT则创建一个信号量。此时剩余的两个参数必须配置,
mode配置信号量的权限
value配置信号量的初始值
- 关闭一个信号量
- 删除一个信号量
命名信号量在进程之间的使用
如下程序使用了sem_open()函数创建了一个名为“MY_SEM”的信号量,先执行进程1,因为信号量的初始值为0,所以进程1会一直阻塞,再打开另一个终端执行进程2,此时进程2发送一个信号量,进程1解除阻塞继续向下执行。
互斥锁
互斥锁简介
互斥锁类似于信号量也是用于任务之间同步的,可以理解为是只有加锁和解锁的两种状态的信号量。当执行一段代码或者访问一段共享区域时,如果该任务将该共享区域上锁,此时其它进程也想访问这块区域时,那么该进程则会被挂起,直至解锁,互斥锁更多应用于线程之间。
我们知道线程的优势是能够通过全局变量来共享信息,但是便捷的共享的代价就是,必须确保多个线程不会同时修改同一变量,所以为了避免线程更新变量时所出的问题,必须使用互斥量来确保仅有一个线程可以访问某共享资源。
互斥锁使用
参数mutex指定函数执行初始化操作的目标互斥锁,参数attr用于定义互斥锁的属性,如果将参数设置为NULL,则属性会取默认值。
以下程序定义了两个线程,线程2先延迟一秒,线程1开始上锁,开始每隔一秒打印信息,此时线程2也想上锁,此时线程2就会阻塞,直到线程1执行完for循环,解锁后,此时线程2上锁开始打印线程2信息,线程1此时又阻塞,如此往复。
信号
信号简介
信号是事件发生时对进程的通知机制,往往也被称为“软件中断”,进程收到信号,就意味着某一事件或异常情况的发生,内核、拥有其它权限的进程或进程本身均可向进程发送信号。在shall中可以通过kill命令向进程发送信号,进程收到信号,会采取以下动作之一:①忽略信号,②被信号杀死先挂起,③之后再被专用信号唤醒
信号的使用
以下程序创建了父子进程,执行时,子进程每隔1s打印一个信息,父进程延迟10秒后调用kill函数杀死子进程,此时子进程刚好打印十次。
以下程序创建了两个进程,同时定义了两个信号处理函数,第一个ctrl+c信号的修改,当进程1执行时,按下crtl+c,执行信号处理1函数,打印信息并退出进程。第二个是kill信号的修改,当进程1执行是,另一个终端执行并输入第一个进程的id,则第一个进程被杀死退出,但没有打印“Trig a SIGKILL signal”,说明SIGKILL不允许修改。