Linux系统编程学习笔记——进程间的同步:信号量、互斥锁、信号

信号量

信号量不是用来在进程间传输数据的,而是用来同步进程的动作

一个信号量是一个由内核维护的整数,其值被限制为大于或等于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不允许修改。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值