Linux线程同步之信号量

未命名信号量(存在于内存中)
#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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值