相比于SYSTEMV信号量,POSIX信号量性能高(提高了94%,接近18倍的速度),使用方便。
POSIX信号量分为两种形式:命名信号量和匿名信号量。差别体现在创建和销毁的形式上,进程间访问还是线程间访问。
匿名信号量存在于内存中,并要求能使用信号量的进程必须能够访问内存,它只能应用于一个进程中的线程之间,或者不同进程中已经映射相同内存内容到他们的地址空间中的线程。
命名信号量可以通过名字访问,可以被任何知道它名字的进程访问。
命名信号量API介绍:
- sem_open
#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,表示信号量的名字,通常情况下第一个字符是/,并且名字中只有一个/,名字的长度有限制,跟具体实现有关系,_POSIX_NAME_MAX定义了最大长度。创建成功之后会在/dev/shm/文件夹下创建对应的文件sem.xxxx
参数oflag如果指定O_CREAT标志,信号量如果存在,则返回,如果不存在则创建一个新的,O_CREAT|O_EXCL组合使用测试一个信号量是否已经存在了,如果存在则返回失败。注意这里是O_CREAT不是IPC_CREAT
参数mode,如果指定了O_CREAT则要填写对应的访问权限
参数value表示信号值。
- sem_waith和sem_trywait
#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
函数执行成功返回0,失败返回-1
sem_wait如果信号量计数是0,便会阻塞直到成功使信号量减1(申请到信号资源)或者被信号中断时才返回;
sem_trywait如果信号量计数是0,不会阻塞,而是返回-1,并且errno等于EAGAIN
- sem_timedwait
#include<semaphore.h>
int sem_timedwait(sem_t * restrict sem, const struct timespec *restrict tsptr);
函数执行成功返回0,失败返回-1
加入时间限制,如果在指定的时间内还没有申请到信号资源则返回-1,并且errno设置为ETIMEOUT;
- sem_post
#include<semaphore.h>
int sem_post(sem_t *sem);
函数执行成功返回0,失败返回-1
使信号量值增1,相当于归还资源
调用sem_post时,如果进程在sem_wait发生阻塞,则进程会被唤醒,sem_post使信号量值增1,然后再被sem_wait阻塞,同时信号值继续减1。
简单说就是sem_post操作不受sem_wait的阻塞的影响。
- sem_close
#include<semaphore.h>
int sem_close(sem_t *sem);
函数执行成功,返回0,失败返回-1
- sem_unlink
#include<semaphore.h>
int sem_unlink(const char *name);
函数执行成功,返回0,失败返回-1
该函数删除信号量的名字,如果没有打开的信号量引用,信号量也会被销毁,否则销毁操作延迟到最后一个打开的引用关闭为止。
close和unlink的区别是啥呢?
sem_unlink会马上删除指定的信号量名,但要等到所有打开该信号量的进程关闭该信号量后才删除该信号。详细地说,当进程创建一个命名信号量,会在/dev/shm下生成一个sem.xxx的文件,所有打开该信号量的进程(包括创建它的进程)都会增加该文件的引用计数,并且这个计数由内核管理。当调用sem_unlink时,/dev/shm下的sem.xxx文件会马上被删除,但是信号量本身并没有被删除,所有已打开该信号量的进程仍能正常使用它。直到所有打开该信号量的进程关闭该信号量后,内核才真正删除信号量。
命名信号量一旦打开后,跟名字并没有多大关系,在打开并调用sem_unlink后可以当作无名信号量来使用,其他进程可以重复使用信号量名字,不过已经不是同一个信号量了。
sample code:
/*************************************************************************
************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <semaphore.h>
#define SEM_PATH "mysem55"
int server()
{
sem_t *p_sem = sem_open(SEM_PATH, O_CREAT, 0644, 10);
if (SEM_FAILED == p_sem) {
printf("server open posix semaphore failed\n");
printf("%s\n", strerror(errno));
return -1;
}
/*wait for client to */
int sem_val = 0;
int ret = 0;
int i = 0;
ret = sem_getvalue(p_sem, &sem_val);
while (sem_val) {
printf("server sem val = %d\n", sem_val);
ret = sem_getvalue(p_sem, &sem_val);
usleep(500*1000);
}
printf("server sem empty server post\n");
sem_post(p_sem);
ret = sem_getvalue(p_sem, &sem_val);
printf("server sem val = %d\n", sem_val);
sem_close(p_sem);
sem_unlink(SEM_PATH);
return 0;
}
int client()
{
int ret = 0;
int sem_val = 0;
int i = 0;
sem_t *p_sem = sem_open(SEM_PATH, 0);
if (SEM_FAILED == p_sem) {
printf("client open posix semaphore failed\n");
return -1;
}
ret = sem_getvalue(p_sem, &sem_val);
printf("\tclient get sem value = %d\n", sem_val);
for (i = 0; i < sem_val; i++) {
ret = sem_wait(p_sem);
if (!ret) {
int tmp_val = 0;
ret = sem_getvalue(p_sem, &tmp_val);
printf("\tclient request semaphore sucess current sem val = %d\n",
tmp_val);
}
sleep(1);
}
sem_close(p_sem);
return 0;
}
int main(int argc, char **argv)
{
pid_t pid;
pid = fork();
if (pid < 0) {
printf("fork failed\n");
return -1;
} else if (pid == 0) {
server();
} else {
/*ensure server run first*/
sleep(2);
client();
}
return 0;
}
线程间同步code
#include <stdio.h> #include <stdlib.h> #include <semaphore.h> #include <fcntl.h> #include <pthread.h> #include <errno.h> #define SEM_PATH "mysem" void *thread_func(void *arg) { int sem_val; int ret = 0; sem_t *p_sem = (sem_t *)arg; /*p operation*/ ret = sem_wait(p_sem); if (!ret) printf("thread_id : %ld get resource\n", pthread_self()); ret = sem_getvalue(p_sem, &sem_val); if (!ret) printf("cur semaphore value is %d\n", sem_val); /*sleep as work func*/ sleep(2); /*V operation*/ ret = sem_post(p_sem); if (!ret) printf("thread_id : %ld release resource\n", pthread_self()); } int main(int argc,char * argv[]) { int ret = 0; pthread_t thread_id; sem_t *p_sem = sem_open(SEM_PATH, O_CREAT, 0644, 5); if (SEM_FAILED == p_sem) { printf("open posix semaphore failed\n"); printf("%s\n", strerror(errno)); return -1; } int sem_val = 0; ret = sem_getvalue(p_sem, &sem_val); if (!ret) printf("total semaphore value is %d\n", sem_val); int i = 0; while (i++ < 10) { if((pthread_create(&thread_id, NULL, thread_func, p_sem)) != 0) { printf("can't create pthread.\n"); exit(0); } } pthread_join(thread_id, NULL); ret = sem_close(p_sem); ret = sem_unlink(SEM_PATH); return 0; }
匿名信号量,在单个进程中使用更方便(也可以用在多个进程中进行同步),创建信号量和销毁信号量用的API有所改变。
创建信号量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
函数执行成功返回0,失败返回-1
参数pshared表明是否在多个进程中使用信号量,如果是,设置一个非0值;
参数value表示初始信号量个数;
如果要在多个进程之间使用匿名信号量,需要确保sem参数指向多个进程之间共享的内存范围;
销毁信号量
#include <semaphore.h>
int sem_destroy(sem_t *sem);
sample:
#include <stdio.h> #include <stdlib.h> #include <semaphore.h> #include <fcntl.h> #include <pthread.h> #include <errno.h> void *thread_func(void *arg) { int sem_val; int ret = 0; sem_t *p_sem = (sem_t *)arg; /*p operation*/ ret = sem_wait(p_sem); if (!ret) printf("thread_id : %ld get resource\n", pthread_self()); ret = sem_getvalue(p_sem, &sem_val); if (!ret) printf("cur semaphore value is %d\n", sem_val); /*sleep as work func*/ sleep(2); /*V operation*/ ret = sem_post(p_sem); if (!ret) printf("thread_id : %ld release resource\n", pthread_self()); } int main(int argc,char * argv[]) { int ret = 0; pthread_t thread_id; sem_t sem; ret = sem_init(&sem, 0, 5); if (ret) { printf("open posix semaphore failed\n"); printf("%s\n", strerror(errno)); return -1; } int sem_val = 0; ret = sem_getvalue(&sem, &sem_val); if (!ret) printf("total semaphore value is %d\n", sem_val); int i = 0; while (i++ < 10) { if((pthread_create(&thread_id, NULL, thread_func, &sem)) != 0) { printf("can't create pthread.\n"); exit(0); } } pthread_join(thread_id, NULL); sem_destroy(&sem); return 0; }
互斥保护的一个例子
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <semaphore.h> #define _POSIX_NAME_MAX (128) struct slock { sem_t *sem; 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->sem = sem_open(sp->name, O_CREAT | O_EXCL, S_IRWXU, 1); } while ((sp->sem == SEM_FAILED) && (errno == EEXIST)); if (sp->sem == SEM_FAILED) { free(sp); return NULL; } /*so cliver*/ sem_unlink(sp->name); return (sp); } void s_free(struct slock *sp) { sem_close(sp->sem); free(sp); } int s_lock(struct slock *sp) { return (sem_wait(sp->sem)); } int s_trylock(struct slock *sp) { return (sem_trywait(sp->sem)); } int s_unlock(struct slock *sp) { return (sem_post(sp->sem)); } void *thread_func(void *arg) { int ret = 0; struct slock *sp = (struct slock *)arg; /*lock*/ ret = s_lock(sp); printf("thread_id : %ld get resource\n", pthread_self()); /*sleep as work func*/ sleep(2); /* unlock */ ret = s_unlock(sp); printf("thread_id : %ld release resource\n", pthread_self()); } int main(int argc,char * argv[]) { int ret = 0; pthread_t thread_id; struct slock *sp = s_alloc(); int i = 0; while (i++ < 10) { if((pthread_create(&thread_id, NULL, thread_func, sp)) != 0) { printf("can't create pthread.\n"); exit(0); } } pthread_join(thread_id, NULL); s_free(sp); return 0; }