【网络编程】多进程编程--信号量


  对信号量的操作也就是PV操作。两者含义如下:

  • P(SV),如果SV的值大于0,就将它减1;如果SV的值为0,则挂起进程的执行。
  • V(SV),如果有其他进程因为等待SV而挂起,则唤醒;如果没有,则SV加1。

  接下来的三个系统调用都是操作一组信号量,也就是信号集,而不是单个信号量。

一、semget系统调用

  semget系统调用创建一个新的信号量集,或者获取一个已经存在的信号量集。

#include <sys/sem.h>
int semget(key_t key, int num_sems, int sem_flags);

  其中参数如下:

参数含义
key用来标识一个全局唯一的信号量集(就像文件名全局唯一表示一个文件一样),要通过信号量通信的进行需要使用相同的键值来创建/获取信号量
num_sems指定要创建/获取的信号量集中信号量的数目。如果创建信号量集,则该值必须指定,要获取已经存在的信号量集的大小,则可以为0
sem_flags指定一组标志,其低端的9个比特是该信号量的权限

  semget不会因为信号量已经存在产生错误,但是如果我们使用IPC_CREAT和IPC_EXCL来确保创建一组新的唯一的信号量集时,可能会产生错误并设置errno为EEXIST。semget成功时返回信号量集的标识符,失败是返回-1并设置errno。

  使用semget创建信号量集,则与之关联的内核数据结构体semid_ds也会被创建并初始化。其定义如下:

#include <sem.h>
struct ipc_perm{
	key_t key;  /*键值*/
	uid_t uid;  /*所有者的有效用户ID*/
	gid_t gid;  /*所有者的有效组ID*/
	uid_t cuid; /*创建者的有效用户ID*/
	gid_t cgid; /*创建者的有效组ID*/
	mode_t mode;/*访问权限*/
};
struct semid_ds{
	struct ipc_perm sem_perm;    /*信号量的操作权限*/
	unsigned long int sem_nsems; /*该信号集中的信号量数目*/
	time_t sem_otime;            /*最后一次调用semop的时间*/
	time_t sem_ctime;            /*最后一次调用semctl的时间*/
};

  semget对semid_ds结构体的初始化包括:

  • 将sem_perm.suid和sem_perm.uid设置为调用进程的有效用户ID。
  • 将sem_perm.cgid和sem_perm.gid设置为调用进程的有效组ID。
  • 将sem_perm.mode的最低9位设置为sem_flags参数的最低9位。
  • 将sem_nsems设置为num_sems。
  • 将sem_otime设置为0。
  • 将sem_ctime设置为当前系统时间。

二、semop系统调用

  semop系统调用改变信号量的值,即执行PV操作。
  首先下面是一些与每个信号量关联的一些重要的内核变量:

unsigned short semval;  /*信号量的值*/
unsigned short semzcnt; /*等待信号量值变为0的进程数量*/
unsigned short semncnt; /*等待信号量值增加的进程数量*/
pid_t sempid;           /*最后一次执行semop操作的进程ID*/

  semop的定义如下:

#include <sys/sem.h>
int semop(int sem_id, struct sembuf* sem_ops, size_t num_sem_ops);
  • 1、sem_id是由semget调用返回的信号量集标识符,用于指定被操作的目标信号量集。
  • 2、sem_ops指向一个sembuf类型的数组,其定义为:
struct sembuf{
	unsigned short int sem_num;
	/*sem_num指的是信号量集中信号量的编号,0代表第一个信号量*/
	short int sem_op;
	/*sem_op指定操作类型,可选值为正整数,0,负整数*/
	short int sem_flg;
	/*sem_flg可选值为IPC_NOWAIT和IPC_UNDO*/
};

  sem_op和sem_flg相互影响。具体来说如下:

  • 如果sem_op大于0,则semop将被操作的信号量semval增加sem_op。该操作要求调用进程对被操作信号量集拥有写权限

    • 此时若设置了SEM_UNDO标志,则系统将更新进程的semadj变量。
  • 如果sem_op等于0,表示这是一个等到”0“的操作。该操作要求进程对被操作信号量拥有读权限

    • 如果此时信号量为0,则调用立即成功返回。
    • 如果此时信号量不是0,则semop失败返回或者阻塞进程以等待信号量变成0
      • 在这种情况下,当IPC_NOWAIT标志被指定时,semop立即返回一个错误,并设置errno为EAGAIN。
      • 在这种情况下,当IPC_NOWAIT标志未被指定时,信号量的semzcnt值加1,进程被投入睡眠直到以下三种情况发生:
        • 信号量的值semval变为0,此时系统将该信号量的semzcnt减1,
        • 被操作信号量所在的信号量集被进程移除,此时semop调用失败返回,errno被设置为EIDRM
        • 调用被信号中断,此时semop调用失败返回,errno被设置为EINTR,同时将该信号量的semzcnt减1
  • 如果sem_op小于0,则表示对信号量进行减操作。该操作要求调用进程对被操作信号量拥有写权限

    • 如果信号量的值semval大于等于sem_op的绝对值,则semop成功,调用进程立即获得信号量,并且系统将该信号量的semval的值将去sem_op的绝对值。此时如果设置了SEM_UNDO标志,则系统将更新semadj变量。
    • 如果信号量的值semval小于sem_op的绝对值,则semop失败返回或者阻塞以等待信号量可用。
      • 在这种情况下,当IPC_NOWAIT标志被指定时,semop立即返回一个错误,并设置errno为EAGAIN,
      • 在这种情况下,如果未指定IPC_NOWAIT标志,则信号量的semncnt值加1,进程被投入睡眠直到以下三种情况发生:
        • 信号量的值semval变得大于等于sem_op的绝对值,同时SEM_UNDO标志被设置,系统更新semadj变量,
        • 被操作信号量所在信号集被进程移除,此时semop调用失败返回,errno被设置为EIDRM
        • 调用被信号中断,此时semop调用失败返回,errno被设置为EINTR,同时系统将该信号量的semncnt减1
  • 3、num_sem_ops指定要执行操作的个数,即sem_ops数组中元素的个数。semop对数组sem_ops中的每个成员按照数组顺序依次执行操作,并且该过程为原子操作,主要是为了避免出现竞态条件。

  semop成功时返回0,失败返回-1并设置errno,失败的时候,sem_ops数组中指定的所有操作都不被执行。

三、semctl系统调用

  semctl系统调用允许调用者对信号量进行直接控制,定义如下:

#include <sys/sem.h>
int semctl(int sem_id, int sem_num, int command, ...);
  • sem_id是由semget调用返回的信号量集标识符,用以被指定的信号量集。
  • sem_num指定被操作的信号量在信号量中的编号。
  • command参数指定要执行的命令,command参数在P248。
  • 有的命令需要第四个参数,sys/sem.h推荐的格式如下:
union semun{
	int val;                /*用于SETVAL命令*/
	struct semid_ds* buf;   /*用于IPC_STAT和IPC_SET命令*/
	unsigned short* array;  /*用于GETALL和SETALL命令*/
	struct seminfo* __buf;  /*用于IPC_INFO命令*/
};
struct seminfo{
	int semmap;/*Linux内核没有使用*/
	int semmni;/*系统最多可以拥有的信号量集数目*/
	int semmns;/*系统最多可以拥有的信号量数目*/
	int semmnu;/*Linux内核没有使用*/
	int semmsl;/*一个信号量集最多允许包含的信号量数目*/
	int semopm;/*semop一次最多能执行的sem_op操作数目*/
	int semume;/*Linux内核没有使用*/
	int semusz;/*sem_undo结构体的大小*/
	int semvmx;/*最大允许的信号量值*/
	/*最多允许的UNDO次数(带SEM_UNDO标志的semop操作的次数)*/
	int semaem;
};

四、特殊键值IPC_PREVATE

  semget的调用者可以给其key参数传递一个特殊的键值IPC_PRIVATE(值为0),这样无论该信号量是否已经存在,semget都将创建一个新的信号量,注意的是,使用该键值创建的信号量并非是私有的。


《Linux高性能服务器编程》学习笔记

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rockict_z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值