linux c编程:Posix信号量
POSIX信号量接口,意在解决XSI信号量接口的几个不足之处:
POSIX信号量接口相比于XSI信号量接口,允许更高性能的实现。
POSIX信号量接口简单易用:没有信号量集,其中一些接口模仿了我们熟悉的文件系统操作。
POSIX信号量删除时的处理更加合理。XSI信号量被删除后,使用该信号量标识符的操作将会出错返回,并将errno设置为EIDRM。而对于POSIX信号量,操作可以继续正常执行,直到对该信号量的最后一个引用被释放。
POSIX信号量有两种形式可供选用:有名和无名。它们的区别在于,如何被创建和销毁,其他方面则完全相同。无名信号量只存在于内存中,并且规定能够访问该内存的进程才能够使用该内存中的信号量。这就意味着,无名信号量只能被这样两种线程使用:(1)来自同一进程的各个线程(2)来自不同进程的各个线程,但是这些进程映射了相同的内存范围到自己的地址空间。相反,有名信号量则是通过名字访问,因此,来自于任何进程的线程,只要知道该有名信号量的名字都可以访问。
Posix信号量工作原理:
如果信号量的非负整形变量S大于零,wait就将其减1,如果S
等于0,wait
就将调用线程阻塞。对于signal
操作,如果有线程在信号量上阻塞(此时S
等于0),signal就会解除对某个等待线程的阻塞,使其从wait
中返回,如果没有线程阻塞在信号量上,signal
就将S加1。
由此可见,S
可以被理解为一种资源的数量,信号量即是通过控制这种资源的分配来实现互斥和同步的。如果把S
设为1,信号量即可实现互斥量的功能。如果S
的值大于1,那么信号量即可使多个线程并发运行。另外,信号量不仅允许使用者申请和释放资源,而且还允许使用者创造资源,这就赋予了信号量实现同步的功能。可见,信号量的功能要比互斥量丰富许多。
Posix信号量API:
一 sem_open创一个新的有名信号量
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag, ... /* mode_t mode, unsigned int value */ );
返回值:若成功则返回指向信号量的指针,若出错则返回SEM_FAILED
如果使用一个现存的有名信号量,我们只需指定两个参数:信号量名和oflag(oflag取0)。把oflag设置为O_CREAT标志时,如果指定的信号量不存在则新建一个有名信号量;如果指定的信号量已经存在,那么打开使用,无其他额外操作发生
如果我们想要确保我们在创建一个新的信号量,可以把oflag参数设置为:O_CREAT|O_EXCL。如果信号量已经存在的话,这会导致sem_open调用失败。
为了提高移植性,我们在选择信号量名字的时候,必须遵循一定的约定:
名字的首字符必须是斜杠(/)。
除首字符外,名字中不能再包含其他斜杠(/)。
名字的最长长度由实现定义,不应超过_POSIX_NAME_MAX个字符。
二:sem_close关闭一个信号量
#include <semaphore.h>
int sem_close(sem_t *sem);
返回值:若成功则返回0,出错返回-1
如果进程还没有调用sem_close就已经退出,那么内核会自动关闭该进程打开的所有信号量。注意,这并不会影响信号量值的状态——例如,如果我们增加了信号量的值,我们退出后这个值不会改变。同样,如果我们调用了sem_close,信号量值也不会受到影响。POSIX信号量机制中并没有如同XSI信号量中的SEM_UNDO标志。
三
sem_unlink销毁一个有名信号量
#include <semaphore.h>
int sem_unlink(const char *name);
返回值:若成功则返回0,出错则返回-1
sem_unlink函数移除信号量的名字。如果当前没有打开的对该信号量的引用,那么就销毁它。否则,销毁被推迟到最后一个打开的引用被关闭。
与XSI信号量不同,我们只能对POSIX信号量的值进行加1或减1。对信号量值减1,就类似于对一个二值信号量加锁或请求一个与计数信号量相关的资源。
注意,POSIX信号量并没有区分信号量类型。使用二值信号量还是计数信号量,取决于我们如果对信号进行初始化和使用。如果信号量值只能取0和1,那么它就是一个二值信号量。当一个二值信号量值为1,我们则说它未加锁;若它的值为0,则说它已加锁。
四
调用sem_wait或sem_trywait函数,请求一个信号量(对信号量值执行减1操作)。
#include <semaphore.h>
int sem_trywait(sem_t *sem);
int sem_wait(sem_t *sem);
两个函数返回值:若成功则返回0,出错则返回-1
如果信号量计数为0,这时如果调用sem_wait函数,将会阻塞。直到成功对信号量计数减1或被一个信号中断,sem_wait函数才会返回。我们可以使用sem_trywait函数以避免阻塞。当我们调用sem_trywait函数时,如果信号量计数为0,sem_trywait会返回-1,并将errno设置为EAGAIN。
还可以阻塞一段有限的时间,这时可以使用sem_timedwait函数。
#include <semaphore.h>
#include <time.h> int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict tsptr);
返回值:若成功则返回0,出错则返回-1
tsptr参数指定了希望等待的绝对时间。如果信号量可以被立即减1,那么超时也无所谓,即使你指定了一个已经过去的时间,试图对信号量减1的操作也会成功。如果直到超时,还不能对信号量计数减1,那么sem_timedwait函数将会返回-1,并将errno设置为ETIMEDOUT。
五 调用sem_post函数对信号量值加1。这类似于对一个二值信号量解锁或释放一个与计数信号量有关的资源。
#include <semaphore.h>
int sem_post(sem_t *sem);
返回值:若成功则返回0,出错则返回-1
当我们调用sem_post的时,如果此时有因为调用sem_wait或sem_timedwait而阻塞的进程,那么该进程将被唤醒,并且刚刚被sem_post加1的信号量计数紧接着又被sem_wait或sem_timedwait减1。
六 如果想在一个单一内进程使用POSIX信号量,那么就使用无名信号量会更加简单。无名信号量只是创建和销毁有所改变,其他完全和有名信号量一样。我们调用sem_init函数创建一个无名信号量。
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
返回值:若成功则返回0,出错返回-1
pshared参数指示我们是否要在多进程之间使用该无名信号量。如果要在多个进程之间使用,则将pshared设置为非0值。value参数指定信号量的初始值。
另外,我们需要声明一个sem_t类型的变量,并把它的地址传给sem_init,以便对该变量进行初始化。如果我们要在两个进程之间使用该无名信号量,我们需要确保sem参数指向这两个进程共享的内存范围内。
七 sem_destroy来销毁无名信号量
#include <semaphore.h>
int sem_destroy(sem_t *sem);
返回值:若成功则返回0,出错则返回-1
调用sem_destroy后我们将不能再以sem为参数调用任何信号量函数,除非我们再次使用sem_init对sem进行初始化
八 sem_getvalue函数来获取信号量
#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *restrict valp);
返回值:若成功则返回0,出错则返回-1
如果sem_getvalue执行成功,信号量的值将存入valp指向的整型变量中。但是,需要小心,我们刚读出来的信号量值可能会改变(因为我们随时可能会使用该信号量值)。如果不采取额外的同步机制的话,sem_getvalue函数仅仅用来调试。 还是以生产者消费者的模型来看下实现:
#include<semaphore.h>
#include<unistd.h>
#include<stdio.h>
#include<pthread.h> static int buff[1]={0};
sem_t put,get;
void signal_init()
{
sem_init(&put,0,1);
sem_init(&get,0,0); } void signal_destroy()
{
sem_destroy(&put);
sem_destroy(&get);
} void *producer_signal(void *arg)
{
int i;
for(i=1;i<=10;i++)
{
sem_wait(&put);
buff[1]=i;
printf("value put int the buffer=%d\n",i);
sem_post(&get);
}
pthread_exit((void *)"pthread producer exist\n");
} void *customer_signal(void *arg)
{
int i;
int value;
for(i=1;i<=10;i++)
{
sem_wait(&get);
value=buff[1];
printf("value get from the buffer=%d\n",value);
sem_post(&put);
}
pthread_exit((void *)"thread customer exist\n");
}
int main()
{
void *ret;
int ret_value;
signal_init();
pthread_t tid_producer,tid_customer;
ret_value=pthread_create(&tid_producer,NULL,producer_signal,NULL);
pthread_create(&tid_customer,NULL,customer_signal,NULL);
pthread_join(tid_producer,&ret);
pthread_join(tid_customer,&ret);
printf("tid_producer=%d\n,ret_value=%d",tid_producer,ret_value);
signal_destroy();
return 0;
}
1 首先申明一个初始量为1的信号put。同时申明一个初始量为0的信号get。
2 生产者每次都等到put信号大于0,在生产完成之后,同时调用sem_post(get)使得get信号大于0
3 消费者每次都等待get信号大于0,然后取出数据,同时将put信号大于0;
运行结果:
linux c编程:Posix信号量的更多相关文章
- linux网络编程-posix信号量与互斥锁(39)
-posix信号量信号量 是打开一个有名的信号量 sem_init是打开一个无名的信号量,无名信号量的销毁用sem_destroy sem_wait和sem_post是对信号量进行pv操作,既可以使用 ...
- Linux进程同步之POSIX信号量
POSIX信号量是属于POSIX标准系统接口定义的实时扩展部分.在SUS(Single UNIX Specification)单一规范中,定义的XSI IPC中也同样定义了人们通常称为System V ...
- linux网络编程-posix条件变量(40)
举一个列子来说明条件变量: 假设有两个线程同时访问全局变量n,初始化值是0, 一个线程进入临界区,进行互斥操作,线程当n大于0的时候才执行下面的操作,如果n不大于0,该线程就一直等待. 另外一个线程也 ...
- linux POSIX 信号量介绍
信号量一.什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)使用.多线程可以同时运行多个线程函数完成功能,但是对于共享数据如果不加以锁定,随意改变共享数据的值会发生 ...
- Linux多线程编程-信号量
在Linux中.信号量API有两组.一组是多进程编程中的System V IPC信号量.另外一组是我们要讨论的POSIX信号量. 这两组接口类似,但不保证互换.POSIX信号量函数都已sem_开头,并 ...
- Linux进程间通信IPC学习笔记之同步二(Posix 信号量)
Linux进程间通信IPC学习笔记之同步二(Posix 信号量)
- linux Posix 信号量 二
一.Posix信号量 1.Posix信号量分为两种: 1. 有名信号量:使用Posix IPC名字标识(有名信号量总是既可用于线程间的同步,又可以用于进程间的同步) 2. 内存信号量:存放在共 ...
- linux Posix 信号量 一
信号量是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语. linux提供两种信号量,“内核信号量”和“用户态进程信号量”,“用户态信号量”又分为“Posix”,“System V”信号 ...
- linux系统编程之(一) 信号量
信号量 一.什么是信号量 信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有. 信号量的值为正的时候,说明它空闲.所测试的线程可以锁定而使用它.若为0,说明 它被占用,测 ...
随机推荐
- JDK配置之坑
JKD的配置我这里就不隆重介绍了,引用一篇百度经验,足够让大家去学习 JDK配置:https://jingyan.baidu.com/article/3c343ff70bc6ea0d377963df. ...
- Makefile 13——理解make的解析行为
make是以从上到下的顺序读入Makefile中的内容的.然而,处理Makefile中的语句却并非完全从上到下. 大体上,make处理一个Makefile分为两个阶段.第一个阶段包含: 1.make读 ...
- Sqlserver 批量数据更改
insert into AAA select GETDATE(),name from BBB where aid=0 update a set a = b.a,b=b.b,c=b.c from t a ...
- 类加载器与Web容器
在关于类加载器中已经介绍了Jvm的类加载机制,然而对于运行在Java EE容器中的Web应用来说,类加载器的实现方式与一般的Java应用有所不同.不同的Web容器的实现方式也会有所不同. Tomcat ...
- nginx-1.14.0安装
1.百度搜索Nginx,点击Nginx news官网,点击nginx-1.13.10进入下载网页,选择Stable version的版本之后下载. 2.进入根目录,cd / 3.在根目录下创建soft ...
- Elasticsearch增、删、改、查操作深入详解
引言: 对于刚接触ES的童鞋,经常搞不明白ES的各个概念的含义.尤其对“索引”二字更是与关系型数据库混淆的不行.本文通过对比关系型数据库,将ES中常见的增.删.改.查操作进行图文呈现.能加深你对ES的 ...
- Ubuntu14.04中安装Sublime_Text_3
Sublime Text 简介 Sublime Text 是一款流行的文本编辑器软件,有点类似于TextMate,跨平台,可运行在Linux.Windows和Mac OS X.也是许多程序员喜欢使用的 ...
- .NET开发笔记--对config文件的操作(3)
1.添加新节点前进行判断看是否已存在相同的属性值,若存在进行更新,不存在则进行添加操作. protected bool AddPizza() { //初始化id int newId; string f ...
- mfc小工具开发之定时闹钟之---二十四小时时区和时间段
1.凌晨0:00-6:00时显示凌晨,上午6:00-12:00显示上午,中午12:00-14:00显示中午,下午14:00-显示下午,晚上18:00-24:00显示晚上 2. 早上:6-8:上午8-1 ...
- js阻止表单提交
<!DOCTYPE html><html><head> <title>Simple Login Form</title> < ...