`

POSIX 信号量

阅读更多
    在XSI IPC通信之信号量一节中提到了 XSI 标准的信号量。POSIX 信号量意在解决 XSI 信号量的以下几个缺陷。
    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 函数中打开一个信号量后又断开了它的连接,这销毁了名字,所以其他进程不能再次访问它,同时也简化了进程结束时的清理工作。
分享到:
评论

相关推荐

    Simple-Windows-Posix-Semaphore:一个简单的窗口 POSIX 信号量库

    适用于 Windows 的简单 POSIX 信号量 POSIX 信号量允许进程和线程同步它们的动作。 信号量是一个整数,其值决不允​​许低于零。 可以对信号量执行两种操作:将信号量值加一 [ sem_post ]; 并将信号量值减一 [ sem_...

    example_posix:ProducerConsumer 问题使用(POSIX 信号量和线程)

    生产者/消费者问题使用(POSIX 信号量和线程) 获取文件: 在实验室计算机上,使用以下命令: git clone git://github.com/cwru-eecs338/example_posix.git 课程: 创建和管理 POSIX 线程和信号量。 从多个 ...

    posix-semaphore:阻止 Node.js 的 POSIX 信号量

    posix信号量 安装 npm install posix-semaphore 例子 const Semaphore = require ( 'posix-semaphore' ) const sem = new Semaphore ( 'mySemaphore' ) sem . acquire ( ) /* my code using shared ressources :...

    Linux之信号量_很全面的分析_个人整理的

    - **POSIX信号量**:分为有名信号量和无名信号量,前者可用于进程间通信,后者适用于线程间同步。 - **SYSTEMV信号量**:通常用于进程间通信,支持一组信号量的管理。 #### 三、内核信号量 **内核信号量**是...

    semaphore:此 mex 文件提供了具有 POSIX 信号量功能的接口。-matlab开发

    POSIX信号量是一种在多线程或多进程环境中用于同步和互斥的机制,它在操作系统层面上提供了一种控制多个任务访问共享资源的方式。在MATLAB中,通过MEX文件(MATLAB编译的C/C++代码)可以实现与操作系统底层功能的...

    很全面的资料:Linux之信号量

    其中,POSIX信号量又分为有名信号量(可在进程间共享,值存储在文件中)和无名信号量(值存储在内存中,仅限于线程间共享)。 **三、内核信号量** 1. **结构**:内核信号量由原子计数器`count`、等待进程计数`...

    ReadWriteLock:使用 POSIX 信号量用 C 编写的读者首选读写锁

    这是通过使用 POSIX 信号量和“隐藏”结构声明来实现的,以避免滥用提供的 API。 这个锁是reader-preferred ,这意味着一个 writer 必须等待每个 reader 完成才能获得锁。 有关更多信息,请参见。 我目前正在尝试使...

    linux信号量详解

    #### 三、POSIX信号量类型:无名信号量与命名信号量 在POSIX标准下,Linux提供了两种信号量实现方式:无名信号量和命名信号量。 - **无名信号量**主要用于同一进程内的线程间通信,它们共享相同的内存空间,因此...

    最全面的Linux信号量学习.pdf

    - **SYSTEM V信号量**:不同于POSIX信号量,SYSTEM V信号量提供了更复杂的同步机制,如支持多对多的同步操作,允许多个进程共享同一组信号量。 **三、内核信号量的使用** - **初始化**:使用`sema_init()`、`init...

    4_posix_sem.tgz

    POSIX信号量(POSIX Semaphores)是实现这种同步的一种机制,它提供了计数信号量和二进制信号量两种类型。这个名为"4_posix_sem.tgz"的压缩包文件包含了与使用POSIX信号量相关的源代码示例,主要分为"4_posix_sem_...

    Linux之信号量-比较全面-个人总结。.doc

    POSIX信号量又分为有名(命名)和无名(匿名)两种,前者存储在文件系统中,可用于进程间同步,后者存储在内存中,适合线程间的同步。 3. **内核信号量的构成** - `atomic_t count`:信号量的值,表示资源状态。 ...

    Linux之信号量,比较全面,个人总结 .pdf

    1. **POSIX 信号量**通常为非负整数,主要用于线程间同步,信号量的值直接表示资源的数量。 2. **SYSTEM V 信号量**更复杂,是一个信号量集,包含多个信号量,适用于进程间的同步,且具有更丰富的功能和更复杂的管理...

    H2O_problem_solution:使用 posix 信号量解决 H2O 问题

    我的第二个操作系统项目使用 posix 信号量解决 H2O 问题 跑步: 制作./h2o N GH GO B N - 要创建的分子数GH - 氢原子的最大随机等待时间GO - 氧原子的最大随机等待时间B - 分子结合时的最大随机等待时间 例如: ./...

    信号量在Linux多线程机制中的应用.pdf

    POSIX信号量包括有名信号量(可用于线程间或进程间同步)和无名信号量(仅限线程间同步)。SYSTEM V信号量是一组信号量的集合,常用于进程间同步。 【内核信号量的使用】 内核信号量通过初始化、申请和释放资源的...

    14_linux信号量专题讲座-王保明.rar

    4. **POSIX信号量**:与System V信号量相比,POSIX信号量提供了更高级别的接口,支持线程间的同步,同时也支持命名信号量,可以在不同进程间共享。 5. **死锁与避免**:讲解如何在使用信号量时防止死锁的发生,以及...

    Distributed-and-parallel-text-processor:一个文本处理应用程序,它以分布式方式处理文本的各个段落,每个段落都是并行处理的。 分布式部分是使用MPI实现的,而并行部分是使用带有POSIX信号量的Phtreads进行线程同步的

    4. **POSIX信号量**:在并行编程中,线程同步是防止数据竞争和死锁的关键。POSIX信号量是Pthreads中的一种同步机制,用于控制对共享资源的访问。在文本处理器中,信号量可能用于确保同一时间只有一个线程可以访问...

    生产者消费者代码(C++版)

    本示例是使用C++实现的一个解决方案,结合了Posix信号量和互斥量来确保线程安全和同步。 生产者-消费者问题的基本概念是这样的:生产者线程生成数据并放入一个有限大小的缓冲区,而消费者线程则从这个缓冲区取出并...

    4.信号量的实现和应用1

    虽然这个版本的内核不支持标准的POSIX信号量,但我们可以通过编写简单的函数来模拟其行为。`sem_open()`用于创建或打开信号量,`sem_wait()`和`sem_post()`分别对应P和V操作,`sem_unlink()`则用于删除信号量。 ...

Global site tag (gtag.js) - Google Analytics