linux IPC 通信 study 三:POSIX 信号量

相比于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;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值