未命名信号量(存在于内存中)
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
初始化一个信号量
pshared:非零,进程间共享,并且变量sem需放在共享内存中
:为零,线程间共享,并且变量sem的内存位置需要所有线程都可以访问到,如全局变量,堆内存
value :变量sem的初始值(value为0则表示上锁,此时在执行sem_wait则会阻塞,直到sem为1)
以下三个函数均对sem做减1操作,即上锁
int sem_wait(sem_t *sem);(线程安全)
阻塞函数,直到sem大于0返回
int sem_trywait(sem_t *sem);(线程安全)
非阻塞函数,成功减1返回0,如果此时sem为0,则立即返回-1,并且errno设置为EAGAIN
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);(线程安全)
有时间限制的减1操作,如果sem为0,则等待abs_timeout个时间,倘若等待中sem大于0,则对其减1后返回,
否则设置errno为ETIMEDOUT并返回
struct timespec {
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds [0 .. 999999999] */
};
int sem_post(sem_t *sem);(线程安全)
对sem进行+1操作,即解锁
成功返回0,失败返回-1,并且设置errno
int sem_getvalue(sem_t *sem, int *sval);
获取sem信号量的值,并赋值为sval;
成功返回0,失败返回-1,并且设置errno
*******************************************************************************
命名信号量(存在于文件系统,有一个路径且名字只能是以/开始,后面不能在带/,长度不能超过251,参考man sem_overview)
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
功能:用来创建一个新的命名信号量或者使用一个现有的信号量
返回值:此函数会返回一个信号量指针,用于传递到其他信号量函数上来对信号量进行操作,失败返回SEM_FAILED
name:信号量的名字
oflag:使用O_CREAT(如果信号量已经存在,则O_CREAT什么都不做函数也不出错)。
如果使用O_CREAT|O_EXCL(表示如果信号量已存在,则创建失败,sem_open函数出错返回)
mode:表示谁可以访问信号量
S_IRUSR 用户读 S_IWUSR 用户写 S_IXUSR 用户执行
S_IRGRP 组读 S_IWGRP 组写 S_IXGRP 组执行
S_IROTH 其他读 S_IWOTH 其他写 S_IXOTH 其他执行
value:指定信号量的初始值。取值范围是0~SEM_VALUE_MAX
int sem_close(sem_t *sem);
功能:用来释放信号量相关的资源。释放并不等于销毁,可以再/dev/shm目录下找到这个信号量
返回值:成功返回0,失败返回-1,并且设置errno
int sem_unlink(const char *name);
功能:销毁名为name的信号量,/dev/shm下不在有name这个信号量
命名信号量的加锁解锁同样采用上述函数
命名信号量是随内核持续的,所以如果如果我们不调用sem_unlink来删除它,它将一直存在,直到内核重启。
命名信号量是在共享内存中的,所以可以允许已知它名字的进程使用
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <error.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <pthread.h>
#include <sys/mman.h>
int msleep(uint32_t ms)
{
struct timeval time;
int second = ms / 1000;
int us = (ms - second * 1000) * 1000;
time.tv_sec = ms / 1000;
time.tv_usec = us;
return select(0, nullptr, nullptr, nullptr, &time);
}
int num = 0;
sem_t sem;
void *thread(void *arg)
{
while (num < 10000)
{
sem_wait(&sem);
printf("pid = %d tid = %ld, locked\n", getpid(), pthread_self());
num++;
sem_post(&sem);
printf("tid = %ld, unlock\n", pthread_self());
msleep(100);
}
}
// 测试无名信号量
void testNonNamedSem()
{
sem_init(&sem, 0, 1);
pthread_attr_t detach;
pthread_attr_init(&detach);
pthread_attr_setdetachstate(&detach, PTHREAD_CREATE_DETACHED);
pthread_t tid[2];
pthread_create(&tid[0], &detach, thread, nullptr);
pthread_create(&tid[1], &detach, thread, nullptr);
}
// 测试命名信号量
void testNamedSem()
{
// 创建信号量
const char *semPath = "/testsem";
sem_t *pSem = sem_open(semPath, O_CREAT, S_IRUSR|S_IWUSR, 1);
if(pSem == SEM_FAILED)
{
printf("%s\n", strerror(errno));
return;
}
// 创建内存映射
int *number = (int *)mmap(nullptr, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
if(number == MAP_FAILED)
{
sem_close(pSem);
sem_unlink(semPath);
return;
}
*number = 0;
pid_t pid = fork();
if(pid == 0)
{
//子进程
while(*number < 50)
{
sem_wait(pSem);
printf("pid = %d, locked ++number = %d\n", getpid(), ++(*number));
sem_post(pSem);
printf("pid = %d, unlock ++\n", getpid());
msleep(400);
}
}
if(pid > 0)
{
// 父进程
printf("child process id is %d, parent pid is %d\n", pid, getpid());
while(*number < 50)
{
sem_wait(pSem);
printf("pid = %d, locked --number = %d\n", getpid(), --(*number));
sem_post(pSem);
printf("pid = %d, unlock --\n", getpid());
msleep(800);
}
}
sem_close(pSem);
sem_unlink(semPath);
}
// 编译:g++ sem.cpp -o sem -lpthread -std=c++11
int main()
{
testNamedSem();
return 0;
}
执行完程序后,则在/dev/shm/下看不到testsem