信号量(semaphore),也和互斥锁一样提供了线程间或者进程间的同步功能。

信号量有三种:

信号量比互斥锁高级,互斥锁只允许一个线程访问临界区,信号量可以多个,可以把信号量看作成互斥锁的升级版,但是如果能用互斥锁解决,就用互斥锁,互斥锁比信号量节省资源。

这篇文章只介绍Posix有名字的信号量

1,创建有名字的信号量,创建成功后,会在ubuntu的/dev/shm目录下,生成一个文件,名字为【sem.name】。【sem】是固定的,【name】是函数sem_open的第一个参数。

名字的信号量的生命周期和内核一样,只要系统不重启,它就一直存在。

#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);
  • name:任意名字,当不能包含【/】
  • oflag:和open函数一样,O_RDWR,O_CREAT,O_EXCL等
  • mode:和open函数一样,比如0664
  • value:可以同时访问临界区的线程或者进程的数量。如果设置为1,功能就和互斥锁一样了。
  • 返回值:成功0;失败:SEM_FAILED(这个宏的实际值是-1)。

2,删除有名字的信号量,并删除文件。

#include <semaphore.h>
int sem_unlink(const char *name);
  • 返回值:成功0

    • 失败:-1,设置errno

      EACCES:没有权限访问这个信号量对应的文件
      ENAMETOOLONG:信号量的名字长了
      ENOENT:信号量不存在

3,取得信号量的value值

#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);
  • sem:信号量指针
  • sval:返回的信号量的value值
  • 返回值:成功0;失败:-1,设置errno。(EINVAL :不是一个有效的信号量)。

4,如果信号量的value值大于0,把信号量的value值-1;如果信号量的value值小于1,阻塞等待,直到信号量的value值大于0。注意:在ubuntu下,如果sem_wait执行前,value值为0,sem_wait执行后,value也不会变成-1,再次执行sem_wait,value还是0。但是有的unix系统value会变成负数。即使value不变成负数,内核也会准确记录它的值。

#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
  • sem:信号量指针

  • 返回值:成功0;失败:

    • EINTR :阻塞的过程种被信号终止了。
    • EINVAL:不是一个有效的信号量

    sem_trywait():不阻塞等待。

    • EAGAIN: 信号量的value值为0。

    sem_timedwait():

    • EINVAL: Tabs_timeout.tv_nsecs 小于0,或者大于等于1000毫秒。
    • ETIMEDOUT: 超时了。

5,把信号量的value值+1。

#include <semaphore.h>
int sem_post(sem_t *sem);
  • 返回值:成功0;失败:

    • EINVAL:不是一个有效的信号量
    • EOVERFLOW:超过了信号量的value。

用下面5个程序观察有名字信号量的特性。

semcreate.c

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h> int main(int argc, char** argv){
int c, flags;
unsigned int val = 1;
sem_t* sem; flags = O_RDWR | O_CREAT; while((c = getopt(argc, argv, "ei:")) != -1){
switch(c){
case 'e':
flags |= O_EXCL;
break;
case 'i':
val = atoi(optarg);
break;
}
} if(optind != argc - 1){
printf("usage error\n");
return -1;
} sem = sem_open(argv[optind], flags, 0664, val);
if(sem == SEM_FAILED){
perror("sem");
return -1;
}
sem_close(sem);
exit(0);
}

semunlink.c

#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h> int main(int argc, char** argv){
if(argc != 2){
printf("usage err\n");
exit(1);
} if(sem_unlink(argv[1]) == -1){
perror("sem_unlink");
}
}

semgetvalue.c

#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h> int main(int argc, char** argv){
sem_t* sem;
int val; if(argc != 2){
printf("usage error\n");
exit(1);
} sem = sem_open(argv[1], 0);
sem_getvalue(sem, &val);
printf("value = %d\n", val);
exit(0);
}

semwait.c

#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h> int main(int argc, char** argv){
sem_t* sem;
int val; if(argc != 2){
printf("usage error\n");
exit(1);
} sem = sem_open(argv[1], 0);
sem_wait(sem);
sem_getvalue(sem, &val);
printf("pid %ld has semaphore, value = %d\n", (long) getpid(), val); pause();
exit(0);
}

sempost.c

#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h> int main(int argc, char** argv){
sem_t* sem;
int val; if(argc != 2){
printf("usage error\n");
exit(1);
} sem = sem_open(argv[1], 0);
sem_post(sem);
sem_getvalue(sem, &val);
printf("value = %d\n", val); exit(0);
}

步骤1:创建有名字信号量。发现在/dev/shm/下生成了sem.test1,mode是0664,但是umask是0022,所以sem.test1mode是0644

ubuntu$ ./semcreate test1
ubuntu$ ls -l /dev/shm/
total 4
-rw-r--r-- 1 ys ys 32 6月 21 16:25 sem.test1

步骤2:查看有名字信号量的value值

ubuntu$ ./semgetvalue test1
value = 1

步骤3:等待信号量,发现执行完sem_wait函数后,信号量的value值变更为0了。

ubuntu$ ./semwait test1
pid 2995 has semaphore, value = 0
^C
ubuntu$

步骤4:查看有名字信号量的value值,信号量的value值还是0。

ubuntu$ ./semgetvalue test1
value = 0

步骤5:等待信号量2次,由于执行前value值已经是0了,按理来说执行了2次sem_wait后,应该变成-2,但是发现还行0。不要惊慌,内核是记住了value的值为-2的。

ubuntu$ ./semwait test1 &
[8] 3000
ubuntu$ ./semgetvalue test1
value = 0
ubuntu$ ./semwait test1 &
[9] 3002
ubuntu$ ./semgetvalue test1
value = 0

步骤6:把信号量的value值+1

ubuntu$ ./sempost test1
pid 3000 has semaphore, value = 0 //来之第一个sem_wait程序的输出
value = 0

步骤7:把信号量的value值+1

ubuntu$ ./sempost test1
pid 3002 has semaphore, value = 0 //来之第二个sem_wait程序的输出
value = 0

步骤8:把信号量的value值+1,发现内核正确的记录的value的值。

ubuntu$ ./sempost test1
value = 1

用信号量实现生产者和消费者

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h> #define NBUFF 10
#define SEM_MUTEX "mutex"
#define SEM_NEMPTY "nempty"
#define SEM_NSTORED "nstored" int nitems;
struct {
int buff[NBUFF];
sem_t *mutex, *nempty, *nstored;
} shared; void* produce(void *args){
int i;
for(i = 0; i < nitems; ++i){
sem_wait(shared.nempty);
sem_wait(shared.mutex);
shared.buff[i % NBUFF] = i;
sem_post(shared.mutex);
sem_post(shared.nstored);
} return NULL;
} void* consume(void* args){
int i;
for(i = 0; i < nitems; ++i){
sem_wait(shared.nstored);
sem_wait(shared.mutex);
shared.buff[i % NBUFF] = i;
sem_post(shared.mutex);
sem_post(shared.nempty);
} return NULL;
} int main(int argc, char** argv){ pthread_t tid_produce, tid_consume; if(argc != 2){
printf("usage error\n");
exit(1);
} nitems = atoi(argv[1]); //create 3 semaphore
shared.mutex = sem_open(SEM_MUTEX, O_CREAT | O_EXCL, 0664, 1);
shared.nempty = sem_open(SEM_NEMPTY, O_CREAT | O_EXCL, 0664, NBUFF);
shared.nstored = sem_open(SEM_NSTORED, O_CREAT | O_EXCL, 0664, 0); //create produce and consume thread
pthread_create(&tid_produce, NULL, produce, NULL);
pthread_create(&tid_consume, NULL, consume, NULL); //wait for 2 thread
pthread_join(tid_produce, NULL);
pthread_join(tid_consume, NULL); sem_unlink(SEM_MUTEX);
sem_unlink(SEM_NEMPTY);
sem_unlink(SEM_NSTORED);
exit(0);
}

c/c++ 学习互助QQ群:877684253

本人微信:xiaoshitou5854

Linux 信号量之Posix有名字的信号量的更多相关文章

  1. Linux 信号量之Posix基于内存的信号量

    信号量(semaphore),也和互斥锁一样提供了线程间或者进程间的同步功能. 信号量有三种: Posix有名字的信号量 Posix基于内存的信号量 System V信号量 信号量比互斥锁高级,互斥锁 ...

  2. Linux进程同步之POSIX信号量

    POSIX信号量是属于POSIX标准系统接口定义的实时扩展部分.在SUS(Single UNIX Specification)单一规范中,定义的XSI IPC中也同样定义了人们通常称为System V ...

  3. linux网络编程-posix信号量与互斥锁(39)

    -posix信号量信号量 是打开一个有名的信号量 sem_init是打开一个无名的信号量,无名信号量的销毁用sem_destroy sem_wait和sem_post是对信号量进行pv操作,既可以使用 ...

  4. system V信号量和Posix信号量

    一.函数上的区别 信号量有两种实现:传统的System V信号量和新的POSIX信号量.它们所提供的函数很容易被区分:对于所有System V信号量函数,在它们的名字里面没有下划线.例如,应该是sem ...

  5. Linux 内核同步之自旋锁与信号量的异同【转】

    转自:http://blog.csdn.net/liuxd3000/article/details/8567070 Linux 设备驱动中必须解决的一个问题是多个进程对共享资源的并发访问,并发访问会导 ...

  6. linux系统编程之(一) 信号量

    信号量 一.什么是信号量 信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有. 信号量的值为正的时候,说明它空闲.所测试的线程可以锁定而使用它.若为0,说明 它被占用,测 ...

  7. Linux互斥锁、条件变量和信号量

    Linux互斥锁.条件变量和信号量  来自http://kongweile.iteye.com/blog/1155490 http://www.cnblogs.com/qingxia/archive/ ...

  8. 信号量(Posix)

    Posix信号量分为有名信号量和无名信号量 1. Posix有名信号量 有名信号量既可以用于线程间的同步也可以用于进程间的同步 sem都是创建在/dev/shm目录下,名字格式sem.xxx,只需要指 ...

  9. linux第11天 共享内存和信号量

    今天主要学习了共享内存和信号量 在此之前,有个管道问题 ls | grep a 整句话的意思是将ls输出到管道的写端,而流通到另一端的读端,grep a则是从管道的读端读取相关数据,再做筛选 共享内存 ...

随机推荐

  1. windows宿主机和docker容器设置挂载共享文件夹

    docker容器内的程序经常需要访问.调用宿主机目录中的数据,每次都要导入导出非常麻烦费力. 接下来,一步步实现将宿主机的指定文件夹挂载到docker容器中. 1. 打开Oracle VM Vitua ...

  2. 3. Vue - 指令系统

    一.vue指令 (1) v-if // 条件判断 如果条件成立就在页面上生成一个标签并显示出来 (2) v-show //DOM中都存在只是显示与否 (3) v-for //注意 v-bind:key ...

  3. 201871010110-李华《面向对象程序设计(java)》第十七周学习总结

    博文正文开头格式:(2分) 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.co ...

  4. ubuntu下的mv命令

    移动文件 代码: mv file1 dir1 #因为dir1目录是存在的,可以不加 / 直接放入 mv file1 dir1/ #把temp文件移动到myfiles目录中 移动目录 代码: mv di ...

  5. Centos7 yum安装MySQL5.7.25

    1 下载并安装MySQL官方的 Yum Repository[root@localhost ~]# wget -i -c http://dev.mysql.com/get/mysql57-commun ...

  6. CF-weekly4 F. Kyoya and Colored Balls

    https://codeforces.com/gym/253910/problem/F F. Kyoya and Colored Balls time limit per test 2 seconds ...

  7. CSP-S 2019 AFO记

    DAY -1 上午并没有改出题,然而调出了动态$dp$,于是顺便$AC$了保卫王国,于是就很愉悦. 下午考前自闭赛,把会的题和原题写了出来,然后就$rank1$了,感觉自己$rp--$. 晚上发现$T ...

  8. Go 程序编译成 DLL 供 C# 调用。

    Go 程序编译成 DLL 供 C# 调用. C# 结合 Golang 开发   1. 实现方式与语法形式 基本方式:将 Go 程序编译成 DLL 供 C# 调用. 1.1 Go代码 注意:代码中 ex ...

  9. 用 PHP 函数变量数组改变代码结构

    项目越做越大,代码越来越乱,维护困难.原因很多吧.起初为了实现功能,并没有注重代码的结构,外包公司嘛.虽然公司的项目负责人一直考虑复用.封装,但是我觉得基本上没有达到想要的效果.因为整个代码中没有用到 ...

  10. JeeSite | 数据权限应用

    中午吃饭时看了一下陆毅版的<三国>,刚好看的是蜀军缺粮,诸葛亮让王平去劫司马懿的粮.司马懿看蜀军用木牛流马运量很方便,就抢了蜀军的木牛流马仿制了一批,结果司马懿用它运粮时,被王平冒充司马懿 ...