- 浏览: 142524 次
文章分类
最新评论
在XSI IPC通信之信号量一节中提到了 XSI 标准的信号量。POSIX 信号量意在解决 XSI 信号量的以下几个缺陷。
1)POSIX 信号量考虑到了更高性能的实现。
2)POSIX 信号量接口使用更简单:没有信号量集。
3)POSIX 信号量在删除时表现更完美。当一个 XSI 信号量被删除时,使用这个信号量标识符的操作会失败,并将 errno 设置成 EIDRM。而使用 POSIX 信号量时,操作能继续正常工作,直到该信号量的最后一次引用被释放。
POSIX 信号量有命名的和未命名的两种。它们的差异在于创建和销毁的形式上。命名信号量可以被任何已知它们名字的进程中的线程使用。而未命名信号量只存在于内存中,这意味着它们只能应用在同一进程中的线程,或者不同进程中已经映射相同内存内容到它们的地址空间中的线程。
可以使用 sem_open 函数来创建一个新的命名信号量或者使用一个现有的信号量。该函数返回的信号量指针用来传递给其他信号量函数。当完成信号量操作时,可以调用 sem_close 函数来释放任何与信号量相关的资源。sme_unlink 函数则可以用来销毁一个命名信号量。
sem_open 函数中,当使用一个现有的命名信号量时,只需要指定前两个参数:信号量的名字 name 和 oflag 的 0 值。当 oflag 参数有 O_CREAT 标志时,如果命名信号量不存在,则会创建。如果已经存在,则会被使用,但不会有额外的初始化发生。在指定 O_CREAT 标志时,需要提供后两个额外的参数。其中 mode 参数指定信号量的使用权限,其取值同文件的权限位。赋值给信号量的权限可以被调用者的文件创建屏蔽字修改。另一个参数 value 则指定信号量的初始值,它的取值范围是:0 ~ SEM_VALUE_MAX(见unix限制一节)。如果想确保创建的是信号量,可以设置 oflag 参数为 O_CREAT|O_EXCL。这样如果信号量已经存在,会导致 sem_open 函数失败,并将 errno 置为 EEXIST。
为了增加可移植性,命名信号量时必须遵循以下规则。
1)名字的第一个字符应该为“/”,以便在 POSIX 信号量的实现使用了文件系统时消除名字的二义性。
2)名字不应包含多余斜杠以避免实现定义的行为。比如,如果使用了文件系统,那么 /mysem 和 //mysem 会被认为是同一个文件名,但如果没使用,则它们可以被认为不同。
3)信号量名字的最大长度是实现定义的,不应该长于 _POSIX_NAME_MAX 个字符,因为这是使用文件系统的实现能允许的最大名字长度限制。
如果进程没有调用 sem_close 函数而退出,那么内核将自动关闭任何打开的信号量。注意,这不会影响信号量值的状态——如果已经对它进行了增 1 操作,这不会因为退出而改变。类似地,信号量值也不会因为调用了 sem_close 函数而受到影响。
sem_unlink 函数会删除信号量的名字。如果没有打开的信号量引用,则立即销毁。否则,销毁将延迟到最后一个打开的引用关闭。
当想要在单个进程中使用 POSIX 信号量时,使用未命名信号量会更容易。相对于命名信号量,这仅仅需要改变创建和销毁信号量的方式。
可以调用 sem_init 函数来创建一个未命名的信号量。对未命名信号量的使用完成时,可以调用 sem_destroy 函数来丢弃它。
sem_init 函数中的 pshared 参数为非 0 值时表示可以在多个进程中使用该信号量。value 参数指定了信号量的初始值。sem 参数代表匿名信号量的地址。如果要在多个进程之间使用信号量,需要确保该参数在它们共享的内存范围内。
调用 sme_destroy 函数后,不能再使用任何带有 sem 的信号量函数,除非调用 sem_init 函数重新初始化它。
信号量创建好后,就可以利用下面这些函数来操作了。其中,sem_wait 或者 sem_trywait 函数可以用来实现信号量的减 1 操作。sem_timedwait 函数则可以选择阻塞一段确定的时间。sem_post 函数则可用来使信号量值增 1。sem_getvalue 函数可以用来检索信号量值(Mac OS X 10.6.8 不支持该函数)。
使用 sem_wait 函数时,如果信号量计数是 0 就会发生阻塞,直到成功使信号量减 1 或者被信号中断时才返回。使用 sem_trywait 可以避免阻塞:如果信号量是 0,则不会阻塞,而是立即返回 -1 并将 errno 置为 EAGAIN。sem_timedwait 函数中的 tsptr 参数可以指定绝对时间(基于 CLOCK_REALTIME 时钟)。如果超时到期并且信号量计数没能减 1,该函数将返回 -1,并将 errno 置为 ETIMEDOUT。
调用 sem_post 可以唤醒因调用 sem_wait 等函数而阻塞的其中一个进程,并且被 sem_post 增 1 的信号量计数会再次被 sem_wait 等函数减 1。
sem_getvalue 函数调用成功后,valp 参数就会包含信号量值。不过要注意,该值在读出来后信号量的值可能已经变了。除非使用额外的同步机制来避免这种竞争,否则该函数只能用于调试。
下面这段代码使用 POSIX 信号量来实现了一种锁,该锁能被一个线程加锁而被另一个线程解锁。
这里根据进程 ID 和计数器来创建名字。注意,这里没必要用互斥量来保护计数器,因为当两个竞争的线程同时调用 s_alloc 并以同一个名字结束时,在调用 sem_open 中使用 O_EXCL 标志将会使其中一个成功而另一个失败,失败的线程会将 errno 设置成 EEXIST,然后会再次尝试。另外,在 s_alloc 函数中打开一个信号量后又断开了它的连接,这销毁了名字,所以其他进程不能再次访问它,同时也简化了进程结束时的清理工作。
1)POSIX 信号量考虑到了更高性能的实现。
2)POSIX 信号量接口使用更简单:没有信号量集。
3)POSIX 信号量在删除时表现更完美。当一个 XSI 信号量被删除时,使用这个信号量标识符的操作会失败,并将 errno 设置成 EIDRM。而使用 POSIX 信号量时,操作能继续正常工作,直到该信号量的最后一次引用被释放。
POSIX 信号量有命名的和未命名的两种。它们的差异在于创建和销毁的形式上。命名信号量可以被任何已知它们名字的进程中的线程使用。而未命名信号量只存在于内存中,这意味着它们只能应用在同一进程中的线程,或者不同进程中已经映射相同内存内容到它们的地址空间中的线程。
可以使用 sem_open 函数来创建一个新的命名信号量或者使用一个现有的信号量。该函数返回的信号量指针用来传递给其他信号量函数。当完成信号量操作时,可以调用 sem_close 函数来释放任何与信号量相关的资源。sme_unlink 函数则可以用来销毁一个命名信号量。
#include <semaphore.h> sem_t *sem_open(const char *name, int oflag, ... /* mode_t mode, unsigned int value */); /* 返回值:若成功,返回指向信号量的指针;若出错,返回 SEM_FAILED */ int sem_close(sem_t *sem); int sem_unlink(const char *name); /* 两个函数的返回值:若成功,返回 0;否则,返回 -1 */
sem_open 函数中,当使用一个现有的命名信号量时,只需要指定前两个参数:信号量的名字 name 和 oflag 的 0 值。当 oflag 参数有 O_CREAT 标志时,如果命名信号量不存在,则会创建。如果已经存在,则会被使用,但不会有额外的初始化发生。在指定 O_CREAT 标志时,需要提供后两个额外的参数。其中 mode 参数指定信号量的使用权限,其取值同文件的权限位。赋值给信号量的权限可以被调用者的文件创建屏蔽字修改。另一个参数 value 则指定信号量的初始值,它的取值范围是:0 ~ SEM_VALUE_MAX(见unix限制一节)。如果想确保创建的是信号量,可以设置 oflag 参数为 O_CREAT|O_EXCL。这样如果信号量已经存在,会导致 sem_open 函数失败,并将 errno 置为 EEXIST。
为了增加可移植性,命名信号量时必须遵循以下规则。
1)名字的第一个字符应该为“/”,以便在 POSIX 信号量的实现使用了文件系统时消除名字的二义性。
2)名字不应包含多余斜杠以避免实现定义的行为。比如,如果使用了文件系统,那么 /mysem 和 //mysem 会被认为是同一个文件名,但如果没使用,则它们可以被认为不同。
3)信号量名字的最大长度是实现定义的,不应该长于 _POSIX_NAME_MAX 个字符,因为这是使用文件系统的实现能允许的最大名字长度限制。
如果进程没有调用 sem_close 函数而退出,那么内核将自动关闭任何打开的信号量。注意,这不会影响信号量值的状态——如果已经对它进行了增 1 操作,这不会因为退出而改变。类似地,信号量值也不会因为调用了 sem_close 函数而受到影响。
sem_unlink 函数会删除信号量的名字。如果没有打开的信号量引用,则立即销毁。否则,销毁将延迟到最后一个打开的引用关闭。
当想要在单个进程中使用 POSIX 信号量时,使用未命名信号量会更容易。相对于命名信号量,这仅仅需要改变创建和销毁信号量的方式。
可以调用 sem_init 函数来创建一个未命名的信号量。对未命名信号量的使用完成时,可以调用 sem_destroy 函数来丢弃它。
#include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value); int sem_destroy(sem_t *sem); /* 两个函数的返回值:若成功,返回 0;否则,返回 -1 */
sem_init 函数中的 pshared 参数为非 0 值时表示可以在多个进程中使用该信号量。value 参数指定了信号量的初始值。sem 参数代表匿名信号量的地址。如果要在多个进程之间使用信号量,需要确保该参数在它们共享的内存范围内。
调用 sme_destroy 函数后,不能再使用任何带有 sem 的信号量函数,除非调用 sem_init 函数重新初始化它。
信号量创建好后,就可以利用下面这些函数来操作了。其中,sem_wait 或者 sem_trywait 函数可以用来实现信号量的减 1 操作。sem_timedwait 函数则可以选择阻塞一段确定的时间。sem_post 函数则可用来使信号量值增 1。sem_getvalue 函数可以用来检索信号量值(Mac OS X 10.6.8 不支持该函数)。
#include <semaphore.h> int sem_wait(sem_t *sem); int sem_rywait(sem_t *sem); int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict tsptr); int sem_post(sem_t *sem); int sem_getvalue(sem_t *restrict sem, int *restrict valp); /* 几个函数的返回值:若成功,返回 0;否则,返回 -1 */
使用 sem_wait 函数时,如果信号量计数是 0 就会发生阻塞,直到成功使信号量减 1 或者被信号中断时才返回。使用 sem_trywait 可以避免阻塞:如果信号量是 0,则不会阻塞,而是立即返回 -1 并将 errno 置为 EAGAIN。sem_timedwait 函数中的 tsptr 参数可以指定绝对时间(基于 CLOCK_REALTIME 时钟)。如果超时到期并且信号量计数没能减 1,该函数将返回 -1,并将 errno 置为 ETIMEDOUT。
调用 sem_post 可以唤醒因调用 sem_wait 等函数而阻塞的其中一个进程,并且被 sem_post 增 1 的信号量计数会再次被 sem_wait 等函数减 1。
sem_getvalue 函数调用成功后,valp 参数就会包含信号量值。不过要注意,该值在读出来后信号量的值可能已经变了。除非使用额外的同步机制来避免这种竞争,否则该函数只能用于调试。
下面这段代码使用 POSIX 信号量来实现了一种锁,该锁能被一个线程加锁而被另一个线程解锁。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> struct slock{ sem_t *semp; char name[_POSIX_NAME_MAX]; }; struct slock* s_alloc(){ struct slock *sp; static int cnt; if((sp=malloc(sizeof(struct slock))) == NULL) return NULL; do{ snprintf(sp->name, sizeof(sp->name), "/%ld.%d", (long)getpid(), cnt++); sp->semp = sem_open(sp->name, O_CREAT|O_EXCL, S_IRWXU, 1); }while(sp->semp == SEM_FAILED && errno == EEXIST); if(sp->semp == SEM_FAILED){ free(sp); return NULL; } sem_unlink(sp->name); return sp; } void s_free(struct slock *sp){ sem_close(sp->semp); free(sp); } int s_lock(struct slock *sp){ return sem_wait(sp->semp); } int s_trylock(struct slock *sp){ return sem_trywait(sp->semp); } int s_unlock(struct slock *sp){ return sem_post(sp->semp); }
这里根据进程 ID 和计数器来创建名字。注意,这里没必要用互斥量来保护计数器,因为当两个竞争的线程同时调用 s_alloc 并以同一个名字结束时,在调用 sem_open 中使用 O_EXCL 标志将会使其中一个成功而另一个失败,失败的线程会将 errno 设置成 EEXIST,然后会再次尝试。另外,在 s_alloc 函数中打开一个信号量后又断开了它的连接,这销毁了名字,所以其他进程不能再次访问它,同时也简化了进程结束时的清理工作。
发表评论
-
打开伪终端设备
2018-07-09 20:50 1253在伪终端概述一节中已对 PTY进行了初步的介绍。尽管 ... -
伪终端概述
2018-06-02 11:05 1550伪终端就是指,一个应用程序看上去像一个终端,但事实上它 ... -
终端窗口大小和 termcap
2018-05-29 22:39 800多数 UNIX 系统都提供了一种跟踪当前终端窗口大小的 ... -
终端规范模式和非规范模式
2018-05-29 00:25 950终端规范模式很简单:发一个读请求,当一行已经输入后,终 ... -
终端标识
2018-05-23 11:18 569尽管控制终端的名字在多数 UNIX 系统上都是 /de ... -
波特率和行控制函数
2018-05-22 07:53 945虽然大多数终端设 ... -
终端属性和选项标志
2018-05-20 07:40 710tcgetattr 和 tcsetattr ... -
终端特殊输入字符
2018-05-17 06:33 815终端支持下表所示的特殊输入字符。 为了更改 ... -
终端 I/O 综述
2018-05-10 07:56 439终端设备可认为是由内核中的终端驱动程序控制的。每个终端 ... -
XSI IPC 通信之共享存储
2018-04-25 07:18 947在XSI IPC通信之消息队列和XSI IPC通信之信 ... -
XSI IPC通信之信号量
2018-04-17 23:38 618在XSI IPC通信之消 ... -
XSI IPC通信之消息队列
2018-04-15 10:54 498消息队列是消息的链接表,存储在内核中,由消息队列标识符 ... -
XSI IPC 相似特征介绍
2018-02-08 23:48 485有 3 种称作 XSI IPC ... -
IPC 通信之 FIFO
2018-02-06 22:55 421FIFO 也被称为命名管道,未命名的管道只能在两个相关 ... -
IPC 通信之管道
2018-01-30 22:22 389管道是 UNIX 系统 IPC 的最古老但也是最常用的 ... -
readv/writev 函数及存储映射 I/O
2018-01-19 00:57 891readv 和 writev 函数可用于在一次函数调用 ... -
POSIX 异步 I/O
2018-01-16 21:33 455POSIX 异步 I/O 接口为对不同类型的文件进行异 ... -
fcntl 记录锁
2018-01-06 23:48 618记录锁的功能是:当有进程正在读或修改文件的某个部分时, ... -
守护进程惯例
2018-01-06 23:52 439UNIX 系统中,守护进程遵循下列通用惯例。 ... -
守护进程编写规则与出错记录
2017-12-26 01:53 455在编写守护进程程 ...
相关推荐
适用于 Windows 的简单 POSIX 信号量 POSIX 信号量允许进程和线程同步它们的动作。 信号量是一个整数,其值决不允许低于零。 可以对信号量执行两种操作:将信号量值加一 [ sem_post ]; 并将信号量值减一 [ sem_...
生产者/消费者问题使用(POSIX 信号量和线程) 获取文件: 在实验室计算机上,使用以下命令: git clone git://github.com/cwru-eecs338/example_posix.git 课程: 创建和管理 POSIX 线程和信号量。 从多个 ...
posix信号量 安装 npm install posix-semaphore 例子 const Semaphore = require ( 'posix-semaphore' ) const sem = new Semaphore ( 'mySemaphore' ) sem . acquire ( ) /* my code using shared ressources :...
- **POSIX信号量**:分为有名信号量和无名信号量,前者可用于进程间通信,后者适用于线程间同步。 - **SYSTEMV信号量**:通常用于进程间通信,支持一组信号量的管理。 #### 三、内核信号量 **内核信号量**是...
POSIX信号量是一种在多线程或多进程环境中用于同步和互斥的机制,它在操作系统层面上提供了一种控制多个任务访问共享资源的方式。在MATLAB中,通过MEX文件(MATLAB编译的C/C++代码)可以实现与操作系统底层功能的...
其中,POSIX信号量又分为有名信号量(可在进程间共享,值存储在文件中)和无名信号量(值存储在内存中,仅限于线程间共享)。 **三、内核信号量** 1. **结构**:内核信号量由原子计数器`count`、等待进程计数`...
这是通过使用 POSIX 信号量和“隐藏”结构声明来实现的,以避免滥用提供的 API。 这个锁是reader-preferred ,这意味着一个 writer 必须等待每个 reader 完成才能获得锁。 有关更多信息,请参见。 我目前正在尝试使...
#### 三、POSIX信号量类型:无名信号量与命名信号量 在POSIX标准下,Linux提供了两种信号量实现方式:无名信号量和命名信号量。 - **无名信号量**主要用于同一进程内的线程间通信,它们共享相同的内存空间,因此...
- **SYSTEM V信号量**:不同于POSIX信号量,SYSTEM V信号量提供了更复杂的同步机制,如支持多对多的同步操作,允许多个进程共享同一组信号量。 **三、内核信号量的使用** - **初始化**:使用`sema_init()`、`init...
- **POSIX信号量**:分为有名信号量和无名信号量。 - **有名信号量**:其值存储在文件中,适用于进程间通信。 - **无名信号量**:其值位于内存中,主要用于线程间同步。 - **SYSTEM V信号量**:通常包含多个信号...
POSIX信号量(POSIX Semaphores)是实现这种同步的一种机制,它提供了计数信号量和二进制信号量两种类型。这个名为"4_posix_sem.tgz"的压缩包文件包含了与使用POSIX信号量相关的源代码示例,主要分为"4_posix_sem_...
POSIX信号量又分为有名(命名)和无名(匿名)两种,前者存储在文件系统中,可用于进程间同步,后者存储在内存中,适合线程间的同步。 3. **内核信号量的构成** - `atomic_t count`:信号量的值,表示资源状态。 ...
1. **POSIX 信号量**通常为非负整数,主要用于线程间同步,信号量的值直接表示资源的数量。 2. **SYSTEM V 信号量**更复杂,是一个信号量集,包含多个信号量,适用于进程间的同步,且具有更丰富的功能和更复杂的管理...
我的第二个操作系统项目使用 posix 信号量解决 H2O 问题 跑步: 制作./h2o N GH GO B N - 要创建的分子数GH - 氢原子的最大随机等待时间GO - 氧原子的最大随机等待时间B - 分子结合时的最大随机等待时间 例如: ./...
POSIX信号量包括有名信号量(可用于线程间或进程间同步)和无名信号量(仅限线程间同步)。SYSTEM V信号量是一组信号量的集合,常用于进程间同步。 【内核信号量的使用】 内核信号量通过初始化、申请和释放资源的...
4. **POSIX信号量**:与System V信号量相比,POSIX信号量提供了更高级别的接口,支持线程间的同步,同时也支持命名信号量,可以在不同进程间共享。 5. **死锁与避免**:讲解如何在使用信号量时防止死锁的发生,以及...
2. **用户态信号量**:专为用户态进程设计,进一步细分为POSIX信号量和SYSTEMV信号量。 - **POSIX信号量**:支持有名信号量和无名信号量,前者存储在文件中,便于跨进程间通信;后者存于内存,仅限于同一进程内的...
4. **POSIX信号量**:在并行编程中,线程同步是防止数据竞争和死锁的关键。POSIX信号量是Pthreads中的一种同步机制,用于控制对共享资源的访问。在文本处理器中,信号量可能用于确保同一时间只有一个线程可以访问...
本示例是使用C++实现的一个解决方案,结合了Posix信号量和互斥量来确保线程安全和同步。 生产者-消费者问题的基本概念是这样的:生产者线程生成数据并放入一个有限大小的缓冲区,而消费者线程则从这个缓冲区取出并...
虽然这个版本的内核不支持标准的POSIX信号量,但我们可以通过编写简单的函数来模拟其行为。`sem_open()`用于创建或打开信号量,`sem_wait()`和`sem_post()`分别对应P和V操作,`sem_unlink()`则用于删除信号量。 ...