linux信号量semaphore的几种使用方法(semop函数的特性)

应用情景一:用信号量打造一个二值信号量(互斥量),也即:任何时刻只允许一个线程访问共享资源。P操作用于占用资源,V操作代表释放资源。

使用信号量,关键是要知道semop函数的特性:

① semop函数的第二形参sops可以以数组地址的形式输入多个动作(称为动作集),man手册上讲,semop函数会按照sops数组的顺序、原子性的执行动作集中的动作,要么一个都不执行,要么全部执行。手册上说的这句话我觉得是有点问题的,“要么全部执行”这句话实际上有个例外:如果动作集中某个动作设置的条件(如等待0)会使得线程堵塞在本函数中(或者本函数出错返回)的话,那么后面的动作就只能等解除堵塞之后才能被执行(堵塞时),或者得不到执行(semop出错返回时)。
② 如果在某时刻有多个线程都在等待互斥信号量的使用权,那么一旦占用该互斥量的线程把它释放后,这多个等待的线程中,只能有一个线程被解除堵塞

ps:当无法获得信号量资源时,semop到底是堵塞,还是设置错误并返回,取决于第四参数是否或了IPC_NOWAIT标志。


方法1:
步骤:
(1) 把信号量的值初始化为0(创建信号量之后默认值就是0,该步骤不做也行)
(2) P操作,用semop函数设置线程等待信号量的值semval为0,若不为0则堵塞或报错;然后用semop函数把信号量的值+1。本步骤中的两个动作,必须通过semop的实参一次把两个动作都输入进去,而不能分别调用两次semop来实现。
(3) V操作,用semop函数设置信号值-1,注意:只有信号量值≥abs(-1)时,才能够立即减1后立即返回,否则本线程又得等待,直到信号量值≥abs(-1)。当然,因为P操作已经把信号量值+1了,所以这里信号量值肯定是≥abs(-1)。

分析:①整个程序中首次执行P操作的时候,情况是怎样的?看步骤(2),设置本线程为等待semval变为0,因为semval被初始化为0了,所以semop会立即返回或者继续执行形参指定的下一个动作:把semval+1。+1这种动作永不堵塞,于是本线程将继续向下执行开始访问共享资源。
    ②当某个线程A执行P之后,尚未V之前,又有另个线程B开始执行P了,情况是怎样的?还是看步骤(2),设置本线程B为等待semval变为0,因为线程A已经把semval设为1了,于是线程B被堵塞或报错。
    ③ 为什么步骤(2)中不允许把等待0和+1这两个动作分别用两次semop来实现?试想这样一种情形:semval初始化为0,当进程A等待0时,发现确实是0,于是继续向下执行semop的+1(进而开始访问共享资源),这时发生了进程/线程调度,切入了线程B,线程B恰好也要执行P操作,等待semval变为0,也发现确实是0,于是继续向下执行semop的+1 (进而开始访问共享资源),显然,没有达到预想的互斥的效果。semop函数提供了一种机制,把多个动作(称为动作集)通过形参一次性传入进去之后,操作系统可以保证,这些动作要么一个也不执行,要么全部被执行(除非:如果某个动作设置的条件会堵塞线程时,等堵塞解除后,后面的动作才会执行),这就杜绝了这一问题。

方法2:
步骤:
① 用semctl或者semop把信号量值初始化为1

② P操作,用semop函数设置线程等待semval≥abs(-1)

③ V操作,用semop函数设置semval+1

分析:①整个程序中首次执行P操作的时候,情况是怎样的?看步骤(2),因为semval被初始化为1了,故本次P操作并不堵塞或出错,而是把semval-1后直接返回,P操作完成后semval就变成0了;
 ②当某个线程A执行P之后,尚未V之前,又有另个线程B开始执行P了,情况是怎样的?还是看步骤(2),设置本线程B为等待等待semval≥abs(-1),因为线程A的P已经把semval设为0了,于是线程B被堵塞或报错;

猜你喜欢

转载自blog.csdn.net/qq_31073871/article/details/80937148