a:focus {
outline: thin dotted #333;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
a:hover {
outline: 0;
}
a:active {
outline: 0;
}
a:hover {
color: #005580;
text-decoration: underline;
}
blockquote small:before {
content: ‘\2014 \00A0‘;
}
q:before {
content: "";
}
q:after {
content: "";
}
blockquote:before {
content: "";
}
blockquote:after {
content: "";
}
semctl semget semop 函数系列构成的 信号量
linux服务器编程
信号量原语
简介
信号量是由 Dijkstra 提出的解决多进程沟通和并发编程问题的特殊变量,这种变量只能取自然数,而且只支持两种操作: wait & signal 。它主要处理多个进程访问资源的问题,通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域,而临界区域是指执行数据更新的代码需要独占式地执行。
实现方式
首先创建一个信号量 SV,并且使用一个整数唯一的标识信号量(这个标识量是跨越进程存在的,即在不同的进程中同一个信号量也能唯一的标识该事件)。只有两种 一种是 wait 操作,一种是 signal 操作,不过为了避免和linux的概念重复,又被称为 P 操作 和 V 操作
- P(SV) :如果 SV 大于 0,那么 SV 的值 减一,这里的值指的是信号量所带的值,而不是标识信号量的整数,就和结构体名和结构体的内的一个变量一样,减的是变量。如果是SV==0,那么就挂起该进程的执行
- V(SV):如果有其他进程由 因等待SV 挂起,那么 激活给他,如果没有 那么就 SV 加 一
上面很难理解对吧,举个例子吧。
我有两个进程 A & B,他们分想访问资源 C (访问过程的代码为关键代码),利用 信号量 来保证同时只有唯一的一个。比如
- 开始时首先设置 SV 的值 为 1
- 假设 A 优先 走到 关键代码区域,它会执行 P(SV) ,根据上面的操作,他会减 1 ,同时执行关键的代码。
- 在 A 执行关键代码的时候,B运行到了关键代码出,执行 P(SV
),它会则会挂起该进程,直到 A 执行完关键代码,并且 执行了 V(SV)
但必须强调的是 P(SV),V(SV ),都必须是原子操作。是由
sys/sem.h 的定义的函数实现的。
有些分类方式业在这提一下啊
信号量按其用途可分为两种:
公用信号量:联系一组并发进程,相关的进程均可在此信号量上执行P 和V操作。初值常常为1,用于实现进程互斥。
私有信号量:联系一组并发进程,仅允许此信号量拥有的进程执行P 操作,而其他相关进程可在其上施行V 操作。初值常常为0 或正整数,多用于并发进程同步。
信号量按其取值可分为两种:
二元信号量:仅允许取值为0 和1,主要用于解决进程互斥问题。
一般信号量:允许取值为非负整数,主要用于解决进程同步问题。
函数集
创建or 获得
int semget(key_t key, int num_sems, int sem_flags);
第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。
第二个参数num_sems指定信号集中信号的个数
具体的值是根据你使用信号量的目的,如果你想要实现进程之间的互斥操作,设置为1 ,而如果想要实现多进程的同步问题,那么至少得大于一。在为零的情况下,是获取该信号量。
第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。
semget函数成功返回一个相应信号标识符(非零),失败返回-1.
信号量操作
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
它用来实现信号量的 P,V 操作,利用是的结stuct sembuf
来指示操作内容
struct sembuf{
short sem_num;//除非使用一组信号量,否则它为0
short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
//一个是+1,即V(发送信号)操作。
short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,
//并在进程没有释放该信号量而终止时,操作系统释放信号量
};
结构体中的变量含义为
sem_num : 指定操作信号量集中的第 sem_num+1 个信号量,一般设置为 0,表示操作第一个
sem_flag:
IPC_NOWAIT :设置信号量操作不等待
SEM_UNDO : 选项会让内核记录一个与调用进程相关的UNDO记录,如果该进程崩溃,则根据这个进程的UNDO记录自动恢复相应信号量的计数值
sem_op : 表示 操作信号量的方式,实现 P | V 的操作
sem_op | 意义 |
---|---|
大于0 | 将 sem_val + opt ,但要求有写权限 |
等于 0 | 在没有设置 IPC_NOWAIT 的情况下,会挂起 等待 sem_val==0的情况发生,发生则立即返。但设置的话,就会立即返回错误信息EAGAIN |
小于 0 | 在没有设置 IPC_NOWAIT的情况下,会等待 sem_op的绝对值小于 等于sem_val的值,小于的话,就减去它,并且放回,设置的话,与 等于0 的情况基本一致 |
等于 0 一般被称为 等待 0 ,而小于 0 则可以称为 等待可减
信号量设置
int semctl(int sem_id, int sem_num, int command, ...);
该函数用于对信号量直接设置
sem_id 为信号量级标识
sem_um 指定被操作信号量在信号量集中的编号
command 为操作命令
而... 代表可扩展参数,推荐使用一下啊的结构体填充
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
而该结构体中的 semid_ds 和 seminfo 的结构体,seminfo 表示信号量系统资源配置信息,semid_ds则是一个内核结构体,在 semget
函数创建信号量时,被初始化和关联
的具体信息可以去man 一下 该函数,