信号量——POSIX 与 System V的接口对比分析
2016-02-20 21:37
162 查看
一 POSIX 标准
#include <semaphore.h>sem_t:信号量的数据结构
int sem_init (sem_t *sem, int pshared, unsigned int value)
无名信号量(也称为基于内存的信号量)sem初始化,设置共享选项pshared,并指定一个整数类型的初始值为value。pshared参数控制着信号量的类型。如果
pshared的值是0,就表示它是当前里程的局部信号量;否则,其它进程就能够共享这个信号量。
sem_t*sem_open(const char *name,int oflag,mode_t mode,unsigned int value);
有名信号量(返回值)的创建和初始化。出错时返回为SEM_FAILED。
intsem_destroy(sem_t *sem);
释放无名信号量。
int sem_close(sem_t *sem);
对应有名信号量的关闭,注意不是销毁,是关闭。
int sem_unlink(count char*name);
对应有名信号量的销毁,每个信号都有一个引用计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把name所指的信号灯从文件系统中删除。也就是要等待最后一个sem_close发生。
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
对应信号量的P操作。 sem_wait和sem_trywait的差别是:当所指定信号灯的值已是0时,后者并不将调用线程投入睡眠。相反,他返回一个EAGAIN错误。
int sem_post(sem_t *sem);
int sem_getvalue(sem_t *sem,int *valp);
对应信号量的V操作。
注意:posix基于内存的信号灯和posix有名信号灯有一些区别,我们必须注意到这些。
1.sem_open不需要类型与shared的参数,有名信号灯总是可以在不同进程间共享的。
2.sem_init不使用任何类似于O_CREAT标志的东西,也就是说,sem_init总是初始化信号灯的值。因此,对于一个给定的信号灯,我们必须小心保证只调用一次sem_init。
3.sem_open返回一个指向某个sem_t变量的指针,该变量由函数本身分配并初始化。但sem_init的第一个参数是一个指向某个sem_t变量的指针,该变量由调用者分配,然后由sem_init函数初始化。
4.posix有名信号灯是通过内核持续的,一个进程创建一个信号灯,另外的进程可以通过该信号灯的外部名(创建信号灯使用的文件名)来访问它。 posix基于内存的信号灯的持续性却是不定的,如果基于内存的信号灯是由单个进程内的各个线程共享的,那么该信号灯就是随进程持续的,当该进程终止时它也会消失。如果某个基于内存的信号灯是在不同进程间同步的,该信号灯必须存放在共享内存区中,这要只要该共享内存区存在,该信号灯就存在。
5.基于内存的信号灯应用于线程很麻烦,而有名信号灯却很方便,基于内存的信号灯比较适合应用于一个进程的多个线程。
二 System V接口
#include <sys/types.h>#include <sys/ipc.h>
#include <sys/sem.h>
sembuf结构体:
struct sembuf {
shortsem_num;
shortsem_op;
shortsem_flg;
};
sem_num是信号量的编号。
sem_op是信号量一次PV操作时加减的数值,一般只会用到两个值,一个是“-1”,也就是P操作,等待信号量变得可用;另一个是“+1”,也就是我们的V操作,发出信号量已经变得可用。
sem_flag的两个取值是IPC_NOWAIT或SEM_UNDO。
int semget(key_t key, int nsems, intsemflg);
用来创建和访问一个信号量集,key:信号集的名字,nsems:信号集中信号量的个数,semflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样。成功返回一个非负整数,即该信号集的标识码,失败返回-1。
int semctl(int semid, int semnum, int cmd,...);
信号集的控制操作,包括销毁、设置信号量的值、读出信号量的值。Semid:由semget返回的信号集标识码。Semnum:信号集中信号量的序号。Cmd:将要采取的动作(有三个可取值)。最后一个参数根据命令不同而不同。
int semop(int semid, struct sembuf *sops,unsigned nsops);
用来修改一个信号集,进行PV操作。关键在于理解sembuf结构体。Semid:是该信号量的标识码,也就是semget函数的返回值。Sops:是个指向一个结构数值的指针。Nsops:信号量的个数。
三 总结
在用法方面,SystemV更突出一个集合的概念,因此,可以同时操作多个信号量,例如需要等待2个信号都满足条件才允许运行临界代码时,使用System V的信号更简易,但这不代表POSIX的信号量不能实现。下面以哲学家就餐问题(需要同时等待左右叉子),分别使用两种方法实现,体会其中的差别。System V实现
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <stdbool.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/wait.h> #ifdef _SEM_SEMUN_UNDEFINED union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; #endif #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int wait_1fork(int no,int semid) { //int left = no; //int right = (no + 1) % 5; struct sembuf sb = {no,-1,0}; int ret; ret = semop(semid,&sb,1); if(ret < 0) { ERR_EXIT("semop"); } return ret; } int free_1fork(int no,int semid) { struct sembuf sb = {no,1,0}; int ret; ret = semop(semid,&sb,1); if(ret < 0) { ERR_EXIT("semop"); } return ret; } //这里表明叉子是一个临界资源 #define DELAY (rand() % 5 + 1) //相当于P操作 void wait_for_2fork(int no,int semid) { //哲学家左边的刀叉号数 int left = no; //右边的刀叉 int right = (no + 1) % 5; //刀叉值是两个 //注意第一个参数是编号 struct sembuf buf[2] = { {left,-1,0}, {right,-1,0} }; //信号集中有5个信号量,只是对其中的 //资源sembuf进行操作 semop(semid,buf,2); } //相当于V操作 void free_2fork(int no,int semid) { int left = no; int right = (no + 1) % 5; struct sembuf buf[2] = { {left,1,0}, {right,1,0} }; semop(semid,buf,2); } void philosophere(int no,int semid) { srand(getpid()); for(;;) { #if 1 //这里采取的措施是当两把刀叉都可用的时候 //哲学家才能吃饭,这样不相邻的哲学家就可 //吃上饭 printf("%d is thinking\n",no); sleep(DELAY); printf("%d is hungry\n",no); wait_for_2fork(no,semid);//拿到叉子才能吃饭 printf("%d is eating\n",no); sleep(DELAY); free_2fork(no,semid);//释放叉子 #else //这段代码可能会造成死锁 int left = no; int right = (no + 1) % 5; printf("%d is thinking\n",no); sleep(DELAY); printf("%d is hungry\n",no); wait_1fork(left,semid); sleep(DELAY); wait_1fork(right,semid); printf("%d is eating\n",no); sleep(DELAY); free_2fork(no,semid); #endif } } int main(int argc,char *argv[]) { int semid; //创建信号量 semid = semget(IPC_PRIVATE,5,IPC_CREAT | 0666); if(semid < 0) { ERR_EXIT("semid"); } union semun su; su.val = 1; int i; for(i = 0;i < 5;++i) { //注意第二个参数也是索引 semctl(semid,i,SETVAL,su); } //创建4个子进程 int num = 0; pid_t pid; for(i = 1;i < 5;++i) { pid = fork(); if(pid < 0) { ERR_EXIT("fork"); } if(0 == pid) { num = i; break; } } //这里就是哲学家要做的事情 philosophere(num,semid); return 0; }
POSIX实现(关键在于在尝试获得第二把叉子时,使用的是sem_trywait否则会造成死锁)
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #include <string.h> #define PEOPLE_NUM 5 #define THINKING 1 #define HUNGRY 2 #define EATING 3 sem_t chopsticks[PEOPLE_NUM]; pthread_mutex_t mutex; void *philosopher(void *arg){ int id = (int) arg; int state = TH d775 INKING; int right = (id + 1) % PEOPLE_NUM; int left = (id + PEOPLE_NUM - 1) % PEOPLE_NUM; char ptrState[32]; while(1){ switch(state){ case THINKING: usleep(300); state = HUNGRY; strcpy(ptrState,"Thinking before eat"); break; case HUNGRY: strcpy(ptrState,"Hungry"); if(sem_wait(&chopsticks[left]) == 0){//阻塞状态 if(sem_trywait(&chopsticks[right]) == 0){//非阻塞 strcpy(ptrState,"I will Eating"); state = EATING; }else{ state = THINKING; strcpy(ptrState,"I have not chopsticks"); sem_post(&chopsticks[left]);//释放请求的得到的left筷子 printf("Philosopher right chopsticks is busy,right=%d,thread id is %d\n",right,id); } }else{ printf("Philosopher left chopsticks is busy,left=%d,thread id is %d\n",left,id);//这句话由于上面被阻塞永远不会输出 } break; case EATING: printf("Philosopher fetch left and right chopsticks: (%d,%d), threadid is %d\n",left,right,id); sem_post(&chopsticks[left]); sem_post(&chopsticks[right]); printf("Philosopher release left and right chopsticks: (%d,%d), threadid is %d\n",left,right,id); usleep(500); state = THINKING; strcpy(ptrState,"Thinking after eat"); break; } pthread_mutex_lock(&mutex); printf("Philosopher is %s, thread id is %d\n",ptrState,id); pthread_mutex_unlock(&mutex); usleep(1000); } pthread_exit((void*)0); } int main(){ pthread_t tid[PEOPLE_NUM]; int i; pthread_mutex_init(&mutex,NULL); for(i = 0 ; i < PEOPLE_NUM ; i ++){ sem_init(&chopsticks[i],0,1); } for(i = 0 ; i < PEOPLE_NUM ; i ++){ pthread_create(&tid[i],NULL,philosopher,(void*)i); } for(i = 0 ; i < PEOPLE_NUM ; i ++){ pthread_join(tid[i],NULL); } return 0; }
另外,也给出使用POSIX 互斥量的方法解决(与POSIX信号量,有相似之处)
#include <stdio.h> #include <stdlib.h> #include <memory.h> #include <pthread.h> #include <errno.h> #include <math.h> //筷子作为mutex pthread_mutex_t chopstick[6] ; void *eat_think(void *arg) { char phi = *(char *)arg; int left,right; //左右筷子的编号 switch (phi){ case 'A': left = 5; right = 1; break; case 'B': left = 1; right = 2; break; case 'C': left = 2; right = 3; break; case 'D': left = 3; right = 4; break; case 'E': left = 4; right = 5; break; } int i; for(;;){ usleep(3); //思考 pthread_mutex_lock(&chopstick[left]); //拿起左手的筷子 printf("Philosopher %c fetches chopstick %d\n", phi, left); if (pthread_mutex_trylock(&chopstick[right]) == EBUSY){ //拿起右手的筷子 pthread_mutex_unlock(&chopstick[left]); //如果右边筷子被拿走放下左手的筷子 continue; } // pthread_mutex_lock(&chopstick[right]); //拿起右手的筷子,如果想观察死锁,把上一句if注释掉,再把这一句的注释去掉 printf("Philosopher %c fetches chopstick %d\n", phi, right); printf("Philosopher %c is eating.\n",phi); usleep(3); //吃饭 pthread_mutex_unlock(&chopstick[left]); //放下左手的筷子 printf("Philosopher %c release chopstick %d\n", phi, left); pthread_mutex_unlock(&chopstick[right]); //放下左手的筷子 printf("Philosopher %c release chopstick %d\n", phi, right); } } int main(){ pthread_t A,B,C,D,E; //5个哲学家 int i; for (i = 0; i < 5; i++) pthread_mutex_init(&chopstick[i],NULL); pthread_create(&A,NULL, eat_think, "A"); pthread_create(&B,NULL, eat_think, "B"); pthread_create(&C,NULL, eat_think, "C"); pthread_create(&D,NULL, eat_think, "D"); pthread_create(&E,NULL, eat_think, "E"); pthread_join(A,NULL); pthread_join(B,NULL); pthread_join(C,NULL); pthread_join(D,NULL); pthread_join(E,NULL); return 0; }
参考文献
http://blog.csdn.net/jasenwan88/article/details/7766808http://blog.csdn.net/acceptedxukai/article/details/8307247 http://www.oschina.net/code/snippet_724028_36857 http://blog.chinaunix.net/uid-26746982-id-3388652.html
相关文章推荐
- C# 多线程 lock 实例
- Maven依赖范围
- 编译android源码
- PHP学习笔记 - 进阶篇(7)
- day12
- Cocos2d-x ListView 的添加,删除,点击和滑动到头和尾监听
- PHP学习笔记 - 进阶篇(6)
- Java占据主要地位的原因
- Android存储_SharedPreferences
- Java中的4种代码块
- PHP学习笔记 - 进阶篇(5)
- PHP学习笔记 - 进阶篇(4)
- 如何查看ubuntu系统的位数
- 计算机网络8--分组交换
- PHP学习笔记 - 进阶篇(3)
- 【ASP.Net】使用自定义服务器控件
- CodeForces 282E Sausage Maximization
- Leetcode Odd Even Linked List
- PHP学习笔记 - 进阶篇(2)
- HTML--meta标签