Linux 信号量使用方法

Posted by KalosAner on January 23, 2025

一、引言

在多线程编程中,经常会使用信号量来控制访问临界资源。

二、线程级信号量

初始化信号量

1
2
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);

sem:信号量对象指针

pshared:共享标志(0 表示线程间共享,非 0 表示进程间共享)

value:信号量初始值(1 表示二元信号量)

返回值:成功返回 0, 失败返回 -1。

等待信号量

1
2
int sem_wait(sem_t *sem);   // 阻塞等待
int sem_trywait(sem_t *sem); // 非阻塞尝试

释放信号量

1
int sem_post(sem_t *sem);

销毁信号量

1
int sem_destroy(sem_t *sem);

销毁信号量必须在没有线程等待该信号量时可以。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

void * read(void * arg);
void * accu(void * arg);
static sem_t sem_one;
static sem_t sem_two;
static int num;

int main(int argc, char *argv[])
{
	pthread_t id_t1, id_t2;
	sem_init(&sem_one, 0, 0);
	sem_init(&sem_two, 0, 1);

	pthread_create(&id_t1, NULL, read, NULL);
	pthread_create(&id_t2, NULL, accu, NULL);

	pthread_join(id_t1, NULL);
	pthread_join(id_t2, NULL);

	sem_destroy(&sem_one);
	sem_destroy(&sem_two);
	return 0;
}

void * read(void * arg)
{
	int i;
	for(i=0; i<5; i++)
	{
		fputs("Input num: ", stdout);

		sem_wait(&sem_two);
		scanf("%d", &num);
		sem_post(&sem_one);
	}
	return NULL;	
}
void * accu(void * arg)
{
	int sum=0, i;
	for(i=0; i<5; i++)
	{
		sem_wait(&sem_one);
		sum+=num;
		sem_post(&sem_two);
	}
	printf("Result: %d \n", sum);
	return NULL;
}

三、系统级信号量

创建信号量集

1
2
3
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);

key:信号量键值(通常由 ftok 生成或使用 IPC_PRIVATE

nsems:信号量集中信号量的个数

semflg:标志位(如 IPC_CREAT 表示不存在则创建,0644 设置权限)

返回值:成功返回信号量集 ID,失败返回 -1

信号量控制

1
int semctl(int semid, int semnum, int cmd, union semun arg);

semidsemget 返回的信号量集 ID。

semnum:信号量在集合中的索引(0 表示第一个信号量)。

cmd:操作命令(如 SETVAL 初始化值,IPC_RMID删除信号量)。

arg:联合体 semun,用于传递初始值或获取状态。

返回值:根据 cmd 不同返回结果(如 GETVAL 返回信号量值)。

信号量操作

1
2
3
4
5
6
int semop(int semid, struct sembuf *sops, unsigned nsops);
struct sembuf {
    unsigned short sem_num;  // 信号量索引
    short sem_op;            // 操作值(-1 等待锁,+1 释放锁)
    short sem_flg;           // 标志(如 `SEM_UNDO` 防止死锁)
};

semid:信号量集 ID。

sops:操作结构体数组指针,定义对信号量的原子操作。

nsops:操作结构体数量

返回值:成功返回 0,失败返回 -1