提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
提示:这里可以添加本文要记录的大概内容:
信号量主要用于进程间使用
信号量:分为 posix 和 systemV 信号量
posix信号量:
sem_open :打开/创建sem
sem_close :关闭sem
sem_unlink :删除sme
sem_post : P操作+1
sem_wait : V操作 -1,函数小于0的时候会阻塞
sem_getvalue :调试使用,存在竞争态,不使用
systemV信号量:
int semget(key_t key, int nsems, int semflg); * 功能:创建或访问一个信号量集。
int semop(int semid, struct sembuf sops, size_t nsops); * 功能:对信号量集执行操作,如增加或减少信号量的值。
int semctl(int semid, int semnum, int cmd, … / union semun arg */); * 功能:对信号量集执行控制操作,如初始化信号量、获取信号量信息或删除信号量集。
key_t ftok(const char *pathname, int proj_id); * 功能:用于生成一个唯一键值,用于semget()函数中创建或访问信号量集。
提示:以下是本篇文章正文内容,下面案例可供参考
一、信号量是什么?
信号量(Semaphore)是进程间通信(IPC,Inter-Process Communication)机制的一种,主要用于解决进程间的同步和互斥问题。信号量最早由荷兰计算机科学家 Edsger Dijkstra 提出,它是一种软件抽象,可以控制对共享资源的访问,防止多个进程或线程同时访问同一资源,从而避免竞态条件和死锁。
信号量的基本概念包括:
计数器:信号量本质上是一个计数器,用来记录可用资源的数量。计数器的值可以是正数、零或负数。
P操作(Wait操作):当一个进程想要访问共享资源时,它会对信号量执行 P 操作。P 操作会将信号量的值减1。如果信号量的值大于等于0,则进程可以继续执行并访问资源。如果信号量的值小于0,这意味着没有可用资源,进程会被阻塞,直到资源可用。
V操作(Signal操作):当一个进程完成对资源的使用时,它会对信号量执行 V 操作。V 操作会将信号量的值加1。如果在信号量的等待队列中有被阻塞的进程,其中一个会被唤醒,以访问资源。
二、代码示例
1.posix
代码如下(示例):
代码示例:父进程每3秒生产,子进程每1秒消费,生产不足时候子进程阻塞。
gcc posix_sem.c -o posix_sem -lrt -lpthread -g
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/wait.h>
#define SEMAPHORE_PATH "/example_semaphore"
sem_t* init_semaphore(const char* name) {
int val = 0;
sem_t* semaphore = sem_open(name, O_CREAT | O_EXCL, 0644, 1); // 信号量创建时值就为1
if (semaphore == SEM_FAILED) {
perror("sem_open");
exit(EXIT_FAILURE);
}
printf("sem_open val = %d\n", val);
return semaphore;
}
void cleanup_semaphore(sem_t* semaphore) {
if (sem_close(semaphore) == -1) {
perror("sem_close");
exit(EXIT_FAILURE);
}
if (sem_unlink(SEMAPHORE_PATH) == -1) {
perror("sem_unlink");
exit(EXIT_FAILURE);
}
}
void increment_semaphore(sem_t* semaphore) {
if (sem_post(semaphore) == -1) {
perror("sem_post");
exit(EXIT_FAILURE);
}
}
void decrement_semaphore(sem_t* semaphore) {
if (sem_wait(semaphore) == -1) {
perror("sem_wait");
exit(EXIT_FAILURE);
}
}
int main() {
sem_t* semaphore = init_semaphore(SEMAPHORE_PATH);
printf("semaphore = %d\n", semaphore);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid > 0) { // 父进程 - 生产者
int status;
for (int i = 0; i < 5; ++i) {
printf("Producer incremented semaphore.\n");
increment_semaphore(semaphore);
sleep(3);
}
pid_t child_pid = waitpid(pid, &status, 0);
cleanup_semaphore(semaphore);
} else { // 子进程 - 消费者
int val = 0;
for (int i = 0; i < 5; ++i) {
printf("Consumer decremented semaphore.\n");
decrement_semaphore(semaphore);
sem_getvalue(semaphore,&val);
printf("val = %d\n", val);
sleep(1);
}
}
return 0;
}
2.systemV
代码示例:生产者生产 0 1 sem,消费进程 cust 消费0,cust1 消费 1
要特别注意//op.sem_flg = SEM_UNDO; // 如果设置了SEM_UNDO标志,当线程结束时,系统会自动执行一个V操作(即sem_post),以恢复信号量的初始状态。producer先退出时候会导致consumer1异常, SEM_UNDO是系统推荐设置,尽量设置
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
void producer(int sem_id);
void consumer(int sem_id);
void consumer1(int sem_id);
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
int main() {
int sem_id;
union semun arg;
arg.val = 1; // 初始化信号量为1
key_t key = ftok("/tmp/semaphore_key", 65);
if (key == (key_t)-1) {
perror("ftok failed");
exit(-1);
}
/*
// 创建一个信号量组,有两个信号量
if ((sem_id = semget(key, 2, 0666 | IPC_CREAT)) == -1) {
perror("semget");
exit(1);
}
semctl(sem_id, 0, IPC_RMID, 0); // 删除信号量集
sleep(2);
*/
//重新创建
if ((sem_id = semget(/*key*/ 432, 2, 0666 | IPC_CREAT)) == -1) {
perror("semget");
exit(1);
}
//初始化 0 sem值为1
if (semctl(sem_id, 0, SETVAL, arg) == -1) {
perror("semctl");
exit(1);
}
//初始化 1 sem值为1
if (semctl(sem_id, 1, SETVAL, arg) == -1) {
perror("semctl");
exit(1);
}
pid_t pid;
if ((pid = fork()) == 0) { // 子进程1作为消费者
printf("consumer start\n");
int i = 5;
while(i)
{
printf("consumer enter i = %d\n", i);
consumer(sem_id);
i--;
}
printf("consumer end\n");
} else if ((pid = fork()) == 0) { // 子进程2作为生产者
int i = 4;
while(i)
{
printf("producer start i = %d\n", i);
producer(sem_id);
i--;
}
printf("producer end\n");
//sleep(10);
} else if ((pid = fork()) == 0) { // 子进程3作为消费者
printf("consumer1 start\n");
int i = 5;
while(i)
{
printf("consumer1 enter i = %d\n", i);
consumer1(sem_id);
i--;
}
printf("consumer1 end\n");
}else {
wait(NULL); // 父进程等待子进程完成
wait(NULL);
wait(NULL);
semctl(sem_id, 0, IPC_RMID, 0); // 删除信号量集
printf("Semaphore removed.\n");
}
return 0;
}
void consumer(int sem_id) {
int ret = -1;
struct sembuf op;
op.sem_num = 0;
op.sem_op = -1; // 减少信号量的值
op.sem_flg = SEM_UNDO;
union semun arg;
while (ret != 0) {
if (semctl(sem_id, 0, GETVAL, arg) == -1) {
perror("semctl GETVAL");
return;
}
//printf("consumer Semaphore value is %d\n", arg.val);
ret = semop(sem_id, &op, 1);
if (ret == -1) {
perror("semop");
exit(1);
}
//printf("Consumer decremented semaphore value.\n");
sleep(2);
}
}
void consumer1(int sem_id) {
int ret = -1;
struct sembuf op;
op.sem_num = 1;
op.sem_op = -1; // 减少信号量的值
op.sem_flg = SEM_UNDO;
union semun arg;
while (ret != 0) {
if (semctl(sem_id, 1, GETVAL, arg) == -1) {
perror("semctl GETVAL");
return;
}
//printf("consumer1 Semaphore value is %d\n", arg.val);
ret = semop(sem_id, &op, 1);
if (ret == -1) {
perror("semop");
exit(1);
}
//printf("Consumer1 decremented semaphore value.\n");
sleep(3);
}
}
void producer(int sem_id) {
int ret = -1;
struct sembuf op;
struct sembuf op1;
op.sem_num = 0;
op.sem_op = 1; // 增加信号量的值
//op.sem_flg = SEM_UNDO; // 如果设置了SEM_UNDO标志,当线程结束时,系统会自动执行一个V操作(即sem_post),以恢复信号量的初始状态。producer先退出时候会导致consumer1异常,系统推荐设置
union semun arg;
while (ret != 0) {
ret = semop(sem_id, &op, 1);
if (ret == -1) {
perror("semop");
exit(1);
}
printf("Producer incremented semaphore 0 value.\n");
if (semctl(sem_id, 0, GETVAL, arg) == -1) {
perror("semctl GETVAL");
return;
}
//printf("producer Semaphore value 0 is %d\n", arg.val);
sleep(1);
}
ret = -1;
op1.sem_num = 1;
op1.sem_op = 1; // 增加信号量的值
//op1.sem_flg = SEM_UNDO;
while (ret != 0) {
ret = semop(sem_id, &op1, 1);
if (ret == -1) {
perror("semop");
exit(1);
}
printf("Producer incremented semaphore 1 value.\n");
if (semctl(sem_id, 1, GETVAL, arg) == -1) {
perror("semctl GETVAL");
return;
}
//printf("producer Semaphore value 1 is %d\n", arg.val);
sleep(1);
}
}
总结
linux下 posix 和 systemV 信号量代码示例,可以运行