- 专栏内容:linux下并发编程
- 个人主页:我的主页
- 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
目录
前言
概述
原理机制
接口说明
代码演示
结尾
前言
本专栏主要分享linux下并发编程相关知识,包括多进程,多线程,进程/线程间通信,并发同步控制,以及高并发下性能提升,请大家多多留言。
概述
信号量提供了多任务间的同步机制,生产者产生后,消费者才能消费,避免消费者提前消费的问题。
原理机制
信号量通过对信号量值的增,减操作来达到同步,也就是通常所说的P,V操作原语。一般P操作会消费信号量值,当信号量值不够消费时就会阻塞等待,V操作会增加信号量值,当信号量值达到上限时就是阻塞等待。
限制说明:
对于信号量,操作系统提供以下几个值来限制数量。
SEMMIN 整个操作系统,信号量id的最大数量
SEMMSL 每个信号量id下,最大的信号量个数
SEMMNS 整个操作系统中信号量的最大数量,当然也受上面两个值的约束
以上值可以查看 /proc/sys/kernel/sem
里面值的含义分别为:
SEMMSL 同上
SEMMNS 同上 , 大概是SEMMIN * SEMMNI
SEMOPM op操作中可以操作的,信号量数量的最大值
SEMMNI 同上
[senllang@localhost semphore]$ cat /proc/sys/kernel/sem
32000 1024000000 500 32000
接口说明
/* 头文件 */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
/* 创建信号量 */
int semget(key_t key, int nsems, int semflg);
key, 使用 IPC_PRIVATE, 或者调用ftok产生;
nsems, 获取或创建的数量,一般都为1;
semflg, 与其它IPC的flag类似,可以设置为IPC_CREAT若不存在时可以创建,如果或IPC_EXCL值,存在时就会报错。
/* 设置或删除信号量 */
int semctl(int semid, int semnum, int cmd, ...);
其中cmd可以取值 :
IPC_RMID,删除semid对应的信号量,同时唤醒所有semop等待
IPC_STAT,获取信号量信息,最后用以下结构作为最后一个参数进行传出。
struct semid_ds {
struct ipc_perm sem_perm; /* Ownership and permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned long sem_nsems; /* No. of semaphores in set */
};
IPC_SET,设置权限等信息,最一个参数用以下结构传入。
struct ipc_perm {
key_t __key; /* Key supplied to semget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};
/* 信号量操作 */
int semop(int semid, struct sembuf *sops, size_t nsops);
int semtimedop(int semid, struct sembuf *sops, size_t nsops,
const struct timespec *timeout);
有两种op操作接口,一种不带时间,一种可以设置超时时间。
struct sembuf有三个成员,分别为:
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
sem_flag 可以取值 :
IPC_NOWAIT,不会等待;
SEM_UNDO,在进程结束时,会回退op操作
sem_op, 当>0时会增加信号值;当=0时,会等零值时才返回;当<0时,会减少信号量值,不足时会等待;
代码演示
这个程序创建一个包含 1 个信号量的集合,初始化信号量的值为 1,然后创建一个子进程。子进程等待信号量,然后输出一条信息,最后增加信号量的值。父进程也等待信号量,输出一条信息,然后增加信号量的值;最后销毁信号量。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>
int main(int argc, char const *argv[])
{
int semid;
// 创建一个包含 1 个信号量的集合
semid = semget(IPC_PRIVATE, 1, 0666);
if (semid == -1) {
perror("semget");
exit(EXIT_FAILURE);
}
// 初始化信号量的值为 1
if (semctl(semid, 0, SETVAL, 1) == -1) {
perror("semctl");
exit(EXIT_FAILURE);
}
// 创建一个子进程
if (fork() == 0) {
struct sembuf sops;
// 子进程等待信号量
sops.sem_num = 0;
sops.sem_op = -1;
sops.sem_flg = 0;
if (semop(semid, &sops, 1) == -1) {
perror("semop");
exit(EXIT_FAILURE);
}
printf("Child process is working\n");
// 子进程增加信号量的值
sops.sem_num = 0;
sops.sem_op = 1;
sops.sem_flg = 0;
if (semop(semid, &sops, 1) == -1) {
perror("semop");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
} else {
struct sembuf sops;
// 父进程等待信号量
sops.sem_num = 0;
sops.sem_op = -1;
sops.sem_flg = 0;
if (semop(semid, &sops, 1) == -1) {
perror("semop");
exit(EXIT_FAILURE);
}
printf("Parent process is working\n");
// 父进程增加信号量的值
sops.sem_num = 0;
sops.sem_op = 1;
sops.sem_flg = 0;
if (semop(semid, &sops, 1) == -1) {
perror("semop");
exit(EXIT_FAILURE);
}
// 销毁信号量
if (semctl(semid, 0, IPC_RMID, 0) == -1) {
perror("semctl");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
return 0;
}
输出结果如下:
[senllang@localhost semphore]$ gcc ex01_sem.c
[senllang@localhost semphore]$ ./a.out
Parent process is working
Child process is working
semop: Invalid argument
结尾
作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。另外有什么想要了解的内容,也可以给我发邮件,互相谈讨,定知无不言。
注:未经同意,不得转载!