Chinaunix首页 | 论坛 | 博客
  • 博客访问: 98543
  • 博文数量: 21
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 140
  • 用 户 组: 普通用户
  • 注册时间: 2014-08-30 12:58
个人简介

路漫漫其修远兮,吾将上下而求索!

文章分类

全部博文(21)

文章存档

2016年(3)

2015年(18)

我的朋友

分类: 嵌入式

2015-06-10 12:26:39

一、信号量概念
        在《Advance Programming in the Unix Environment》中定义如下,A semaphore is a counter used to provide access to a shared data object for multiple process.信号量是在多进程环境下使用的一种设施,它负责协调各个进程,以确保它们能够正确合理的使用公共资源。

二、信号量的工作机制

        信号量就是就是用来保证两个或多个关键代码段(公共资源)不被并发调用,就像火车上的厕所一样,不允许多余1个人同时使用,厕所就相当于公共资源,当一个人进入厕所后,该公共资源(厕所)即被占用,除非那个人从厕所出来(释放公共资源),别的人(进程)才可以使用厕所(公共资源)。
        信号量是一个非负整数,所有通过它的进程、线程都会将该整数减1,当该整数值为0时,所有试图通过它的进程都将处于等待状态,在信号量上我们定义两种操作,wait(个人理解为申请比较好)和Release(释放),当一个进程调用wait操作时,它要么得到资源后将信号量减1,要么一直等下去(放入阻塞队列),直到信号量大于等于1时。
简单的流程示意图

三、功能函数

    1.Creating semaphores
函数 int semget (key_t key,int nsem,int flag)
头文件 #include
功能 使用此函数可以创建或者获得一个信号量集ID,如果想使用信号量(semaphore),必须使用此函数先获得ID。
输入参数 key:所要创建或者打开的信号量集对应的键值。该键值用来转换成信号量集的ID,
nsem:创建的信号量集中包含的信号量个数,此参数只在创建一个新的信号量集时有效。
flag:表示调用函数的类型,也可以用于设置信号量集的访问权限,两者通过逻辑或表示。
返回参数 successed: ID
failed:        -1
注意点 关于参数flag 的说明(摘录别处,很懒不再翻译)

The value of the flag parameter may be 0 (zero) or different from zero. In the first case it means that we want to obtain the access to a set of semaphores already created. As a consequence the semgetfunction verifies the existence of the set of semaphores indicated by the key and the access right of the process that made the call. If all these conditions are met, the function returns the access descriptor of the set of semaphores, otherwise it returns -1. In case the value of the flag parameter is other than zero, the exact value has to be obtained by a bitwise OR operation made on the following predefined constants:

IPC_CREAT

Indicates that the user wants to create a new set of semaphores. If this option is not used, the semget function will verify the existence of the set of semaphores indicated by the cheie parameter and the permission to that shared resource of the process that made the call. If this option is used, in case the set of semaphores already exists only thing will be verified: whether the process that made the call has access to it. If it has, the function will return the access descriptor to that zone; otherwise the function will end with a failure.

IPC_EXCL

This option is only used in connection with IPC_CREAT and indicates that a new set of semaphores doesn’t have to be created if it already exists, case in which the semget function will fail, and will return -1.



 2.Controlling sets of semaphores

函数 int semctl(int semid,int semnum,int cmd,/*union semun arg*/)
头文件 #include
功能 该函数用来控制信号量集的相关信息,类似于初始化一个信号量。
输入参数 semid    : 该参数是semget的返回值即信号量集ID.
semnum:该参数取值范围在0~nsem-1 之间。用来指定semid 信号集中的某一个信号量,类似于在信号量集资源数组中的下标,用来对指定资源进行操作。信号量集可以设置多个信号量的!!

cmd      :该参数用来定义函数所要进行的操作,是该表后10个命令之一,通常命令为SETVAL和IPC_RMID.其取值及表达的意义与参数arg的设置有关。

arg        : 该参数是可选的,它通常是一个union semum结构。  

返回参数

successed:  return    大于等于0

failed        :  return    -1

注意点

union semun 结构体定义如下:


点击(此处)折叠或打开

  1. union semun
  2. {
  3.     int               val;   /*for SETVAL*/
  4.     struct semid      *buf   /*for IPC_STAT and IPC_SET*/
  5.     unsigned short    *array /*for GETALL and SETALL*/
  6. }

CMD的取值及其含义

cmd 取值
                                                                                     含义
GETALL 获取信号量集semid中信号量的个数,并将该值赋给短整数arg.array
GETVAL 获取信号量集semid中semnum所指定的信号量的值semval
GETNCNT
return the value of semncnt for the member semnum
GETZCNT return the value of semzcnt fot the member sumnum
GETPID return the value of sempid for the member sumnum
SETALL
set all the semaphore values in the sets to the values pointes by the arg.array 
IPC_RMID 删除信号量集,此操作只能拥有超级用户权限的进程或者信号量集拥有者的进程执行,
IPC_SET
按照参数arg.buf指向的结构体中的值来设置此信号量集的sem_perm.uid,sem_perm.gid以及sem_perm.mode的值,此操作只有拥有超级用户权限的进程或者信号量集的拥有者的进程才可以执行。
IPC_STAT
获得该信号量的semid_ds的结构,保存在arg.buf指向的缓冲区。
SETVAL
set the value  of semval for the member semnum,the value specified by arg.val 



3.Operations on semaphores

函数 int semop(int semid,struct sembuf  semoparray[],size_t nops)
头文件 #include
功能 操作信号量,wait和release 操作需要此函数来执行。
输入参数

semid: 信号量集ID

semoparray[]:是一个struct sembuf 结构类型的数组,其中每个元素表示一个操作,次函数是一个原子操作(bitwise),一旦执行就将执行数组中所有的操作。

nops:  指明semoparray[]数组元素的个数。 

输出参数

success  : 返回 0

failure     :-1

注意事项


点击(此处)折叠或打开

  1. struct sembuf
  2. {
  3.     unsigned short sem_buf;          /*member# int set (0 ,1...nsem-1)*/
  4.     short sem_op;                    /*operation(negative,0,positive)*/
  5.     short sem_flag;                  /*IPC_NOWAIT,SEM_UNDO*/
  6. }

四、简单例程
      在学习信号量时,编写的一个简单的小程序(ipc_sem_test.c),来验证信号量的工作机制,该程序目的就是利用信号量确保子进程优先执行printf打印函数。

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <sys/sem.h>
  6. #include <unistd.h>
  7. #include <stdlib.h>

  8. union semun
  9. {
  10.     int val;
  11.     struct sem_ds *buf;
  12.     unsigned short *array;
  13. };
  14. int semheld = 0;
  15. /P operations,semaphore -1/
  16. static int P(int semId.int semNr)
  17. {
  18.     struct sembuf op = {semNr,-1,SEM_UNDO};
  19.     if(semop(semId,&op,1) == -1)
  20.     {
  21.         fprintf(stderr,"P failed\n");
  22.         return 0;
  23.     }
  24.     return 1;
  25. }
  26. /*V operations, semaphore +1*/
  27. static int V(int semId,int semNr)
  28. {
  29.    struct sembuf op = {semNr.1,SEM_UNDO};
  30.    if(semop(semId,&op,1) == -1)
  31.    {
  32.        fprintf(stderr,"V failed\n");
  33.        return 0;
  34.    }

  35. }

  36. void main(int argc,char **argv)
  37. {
  38.     int semId,
  39.     int smeNr;
  40.     union semun sem_un;
  41.     sem_un.val = 0;
  42.     pid_t pid;
  43.     /*create semaphores*/
  44.     semId = semget((key_t)1000,1,IPC_CREAT|0600);
  45.     if(semId<0)
  46.     {
  47.         perror("Eroare create smeaphore failed!\n");
  48.         exit(EXIT_FAILURE);
  49.     }
  50. /*Init semaphore data*/
  51.     if(semctl(semId,0,SETVAL,sem_un) == -1)
  52.     {
  53.         perror(Error init semaphore failed\n);
  54.         exit(EXIT_FAILURE);
  55.     }
  56.     pid = fork();
  57.     if(pid == -1)
  58.     {
  59.         perror("Error create child progress failed\n");
  60.         exit(EXIT_FAILURE);

  61.     }

  62. }


点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <sys/sem.h>
  6. #include <unistd.h>
  7. #include <stdlib.h>

  8. union semun
  9. {
  10.     int val;
  11.     struct sem_ds *buf;
  12.     unsigned short *array;
  13. };
  14. int semheld = 0;
  15. /P operations,semaphore -1/
  16. static int P(int semId.int semNr)
  17. {
  18.     struct sembuf op = {semNr,-1,SEM_UNDO};
  19.     if(semop(semId,&op,1) == -1)
  20.     {
  21.         fprintf(stderr,"P failed\n");
  22.         return 0;
  23.     }
  24.     return 1;
  25. }
  26. /*V operations, semaphore +1*/
  27. static int V(int semId,int semNr)
  28. {
  29.    struct sembuf op = {semNr.1,SEM_UNDO};
  30.    if(semop(semId,&op,1) == -1)
  31.    {
  32.        fprintf(stderr,"V failed\n");
  33.        return 0;
  34.    }

  35. }

  36. void main(int argc,char **argv)
  37. {
  38.     int semId,
  39.     int smeNr;
  40.     union semun sem_un;
  41.     sem_un.val = 0;
  42.     pid_t pid;
  43.     /*create semaphores*/
  44.     semId = semget((key_t)1000,1,IPC_CREAT|0600);
  45.     if(semId<0)
  46.     {
  47.         perror("Eroare create smeaphore failed!\n");
  48.         exit(EXIT_FAILURE);
  49.     }
  50. /*Init semaphore data*/
  51.     if(semctl(semId,0,SETVAL,sem_un) == -1)
  52.     {
  53.         perror(Error init semaphore failed\n);
  54.         exit(EXIT_FAILURE);
  55.     }
  56.     pid = fork();
  57.     if(pid == -1)
  58.     {
  59.         perror("Error create child progress failed\n");
  60.         exit(EXIT_FAILURE);

  61.     }
  62.     esle if(pid == 0)
  63.     {
  64.         if(!V(semId,0))
  65.             {
  66.                 exit(EXIT_FAILURE);
  67.             }
  68.         printf("This is child progress\n");
  69.     }
  70.     if(!P(semId,0))
  71.          exit(EXIT_FAILURE);
  72.     printf("This is father progress\n");
  73.     exit(EXIT_SUCCESS);

  74. }

执行结果

snowstorm@snowstorm:~/Program/sem_test$ ./ipc_sem_test 

This is child progress

This is father progress

This is father progress

snowstorm@snowstorm:~/Program/sem_test$ 


五、总结

        信号量不难理解,期工作原理也很简单,稍微麻烦点的是几个函数各个参数的理解。


六、主要参考资料

        《LINUX C 编程 从初学到精通》,《Advanced Programming in the UNIX Environment》

        还有两篇文章:

        http://blog.csdn.net/ljianhui/article/details/10243617

        





阅读(3767) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~