Chinaunix首页 | 论坛 | 博客
  • 博客访问: 374042
  • 博文数量: 48
  • 博客积分: 1032
  • 博客等级: 上士
  • 技术积分: 1256
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-19 13:24
文章分类

全部博文(48)

文章存档

2014年(3)

2013年(23)

2012年(22)

分类: LINUX

2012-10-19 13:57:35

   使用ipcs -s查看当前系统的信号量。
    信号量(又称之为信号灯)与(管道、FIFO和消息队列)不同,它是一个计数器,用于多进程对共享数据对象的访问,保证关键代码段不被并发调用。
    为了获得共享资源,进程需要进行如下操作:
    (1)测试控制该资源的信号量。
    (2)若此信号量的值为正,则进程可以使用该资源。进程将信号量值减一,表示它使用了一个资源单位。
    (3)若此信号量的值为0,则进程进入休眠状态,直至信号量值大于0.进程被唤醒后,回到第一步开始。
    信号量值的测试及减一操作应当是原子操作。信号量通常是在内核中实现的。信号量的值表示由多少个共享资源单位可以供享用。


相应的头文件:#include
              #include
              #include

1、创建信号量:#include

    int semget(key_t key, int nsems, int flag);
成功:返回信号量集的IPC标识符。 失败:返回-1;

参数说明:
第一个参数:在本地系统中表示要创建或访问信号量的ID值,IPC_PRIVATE表示创建一个新的信号量;
第二个参数 nsems:表示该集合中的信号量。大于等于0;表示由可用资源数。
如果是创建新的信号量(一般是在服务器中进行),则必须指定nsems。如果引用一个现存的集合(一个客户进程),则将nsems指定为0;
第三个参数flag:指定选项和权限位的标志。IPC_CREAT和IPC_EXEL;

2、信号量的相关操作:
两个函数semop()和semctl():函数semop()用来操作一个信号量集,通过修改sem_ip指定对资源进行操作,而semctl()函数对信号量本身的值进行操作,可以修改信号量的值或者删除一个信号量。

  1. int semop(int semid, struct sembuf semoparray[], size_t nops);
成功返回0,失败返回-1;

参数说明:
     semid:通过semget()函数返回的一个信号量集标识符ID;
     nops:标明参数semoparray所指向数组中的元素个数。
     semoparray是一个结构数组指针。结构体struct sembuf用来说明要执行的操作。
     struct sembuf{
     unsigned short sem_num;           //对应信号量集中的某个资源
     short sem_op;                     //指明所要执行的操作
     short sem_flg;                    //函数semop的行为
     }
sem_num:其实相对应的信号量集合中的某个资源,它的值是从0到相对应信号量集的资源总数(ipc_perm.sem_nsems)之间的整数。
sem_op:指明想要进行的操作。
sem_flag:说明函数semop的行为。

sem_op的值是一个整数:
    (1)sem_op > 0 :释放相应的资源数,如果有两个信号量,释放信号量1,则其semval+1,对信号量这个无名结构体的操作,通过semctl函数来实现。
    (2)sem_op == 0:进程阻塞直到信号量的相应值为0,档信号量已经为0,函数立即返回。
    (3)sem_op < 0:请求sem_op的绝对值的资源数。
sem_flag:
该参数可以设置成为IPC_NOWAIT和SEM_UNDO两种状态。
    IPC_NOWAIT:对信号的操作不能满足时,semop()不会阻塞,并立即返回,同事设定错误信息。
    IPC_UNDO:程序结束时释放信号量,这样做可以避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。 

  int semctl(int sem_id, int semnum, int cmd[, union semun arg]);
 
参数说明:
    sem_id:信号量集标识符;
    semnum:指定信号量集中某一个信号灯成员,其值在(0~nsems-1);
    cmd:定义了函数要进行的操作;
cmd的常用值:
IPC_STAT:对此集合区semid_ds结构,并不存放在arg.buf之中。
GETVAL:返回结构体数组中以semnum为下标的元素的成员semval值。
SETVAL:使用arg.val对该信号量的semnum.sempid赋值

第四个参数是可选的,但是其很重要不能忽略,它的类型是semun,它是多个特定命令的联合(union):
union semun {
    int               val;    //val的值为cmd命令的SETVAL所用;
    struct semid_ds   *duf;   
    unsigned short    *array;
}

信号量在使用之前必须进行初始化,初始化的方式为:

点击(此处)折叠或打开

  1. union semum sem_arg; //定义联合变量;
  2. sem_arg.val = 1;
  3. int rec;
  4. rec = semctl(semid,0,SETVAL,sem_arg);
  5. if(rec == -1)
  6. {
  7.  perror("init\n");
  8. }
在使用信号量的时候这个联合应先定义在代码的开头。

参考程序:由于将各个部分功能分开成了子函数,有的没有用到,但是不影响功能,程序写的有点长;首先运行./server & (后台运行);接着运行./client ChinaUnix  (需要给定一个参数给client程序);server程序获取client程序传递的参数;

server程序:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <errno.h>
  5. #include <sys/types.h>
  6. #include <sys/ipc.h>
  7. #include <sys/sem.h>
  8. #include <sys/shm.h>
  9. #include <unistd.h>

  10. #define path_shm "/opt"
  11. #define PERM_shm 0666 | IPC_CREAT

  12. #define path_sem "/tmp"
  13. #define PERM_sem 0666 | IPC_CREAT
  14. //semctl()第四个参数
  15. union semun{
  16.     int val;
  17.     struct semid_ds *buf;
  18.     unsigned short *array;
  19.     };    
  20. //创建信号量
  21. int creat_sem(void)
  22. {
  23. int semid = 0;
  24. key_t sem_key;
  25.     
  26. sem_key = ftok(path_sem,10);
  27. if(sem_key == -1)
  28. {
  29. perror("key error");
  30. return -1;
  31. }
  32.             
  33. semid = semget(sem_key,1,PERM_sem);
  34. if(semid == -1)
  35. {
  36.  perror("semid");
  37.  return -1;
  38.   }
  39.   return semid;
  40. }
  41. //初始化信号量,在使用之前必须先初始化
  42. int init_sem(int semid)
  43. {
  44. union semun sem_arg;
  45. sem_arg.val = 1;

  46. int rc;
  47. rc = semctl(semid, 0, SETVAL, sem_arg);
  48. if(rc == -1)
  49. {
  50. perror("sem init");
  51. return -1;
  52. }
  53. return 0;
  54. }
  55. //占用资源
  56. int sem_p(int semid)
  57. {
  58. struct sembuf sem_arg;
  59. sem_arg.sem_num = 0;
  60. sem_arg.sem_op = -1;
  61. sem_arg.sem_flg = SEM_UNDO;

  62. int rc;
  63. rc = semop(semid, &sem_arg, 1);
  64. if( rc == -1 )
  65.   {
  66.   perror("sem_p");
  67.   return -1;
  68.   }
  69.   return 0;
  70. }
  71. //阻塞到信号灯为0 阻塞:block;
  72. int sem_b(int semid)
  73. {
  74. struct sembuf sem_arg;
  75. sem_arg.sem_num = 0;
  76. sem_arg.sem_op = 0;
  77. sem_arg.sem_flg = SEM_UNDO;

  78. int rc;
  79. rc = semop(semid,&sem_arg,1);
  80. if(rc == -1)
  81. {
  82. perror("sem_b");
  83. return -1;
  84. }
  85. return 0;
  86. }
  87. //释放资源
  88. int sem_v(int semid)
  89. {
  90. struct sembuf sem_arg;
  91. sem_arg.sem_num = 0;
  92. sem_arg.sem_op = 1;
  93. sem_arg.sem_flg = SEM_UNDO;

  94. int rc;
  95. rc = semop(semid,&sem_arg,1);
  96. if(rc == -1)
  97. {
  98.             perror("sem_v");
  99. return -1;
  100. }
  101. return 0;
  102. }
  103. //删除信号量
  104. int del_sem(int semid)
  105. {
  106. int rc;
  107. rc = semctl(semid,0,IPC_RMID);
  108. if(rc == -1)
  109. {
  110.             perror("del_sem");
  111. return -1;
  112. }
  113. return 0;    
  114. }
  115.     
  116. //创建共享内存
  117. int creat_shm(void)
  118. {
  119. int shmid = 0;
  120. key_t shm_key;
  121. int rcs = 0;
  122. shm_key = ftok(path_shm,11);
  123. if(shm_key == -1)
  124. {
  125.             perror("shm_key error");
  126. return -1;
  127. }
  128.             
  129. shmid = shmget(shm_key,1024,PERM_shm);
  130.  if(shmid == -1)
  131.   {
  132.   perror("shmid");
  133.   return -1;
  134.    }
  135.    return shmid;
  136. }    
  137. int main(void)
  138. {
  139.   int semid,shmid;
  140.   char *s_addr;
  141.   int rcs = 0;    
  142.   int liv_init = 0;
  143.   //获取信号量标识符
  144.   semid = creat_sem();
  145.   if(semid == -1)
  146.   {
  147.   perror("semid error");
  148.   exit(1);
  149.   }
  150. //初始化信号亮
  151.   liv_init = init_sem(semid);
  152.   if(liv_init == -1)
  153.   {
  154.   perror("liv_init");
  155.   exit(1);
  156.   }
  157.  //获取共享内存标识符
  158.   shmid = creat_shm();
  159.   if(shmid == -1)
  160.   {
  161.   perror("shmid error");
  162.   exit(1);
  163.   }
  164.   //映射内存到当前进程
  165.   s_addr = shmat(shmid, 0, 0);
  166.   
  167.   //printf("server semid=%d,shmid=%d\n",semid,shmid);
  168.    
  169.   while(1)
  170.   {
  171.    rcs = sem_b(semid);
  172.    if(rcs == -1)
  173.    {
  174.    perror("sem_b");
  175.    exit(1);
  176.    }
  177.    else
  178.    {
  179.     rcs = sem_p(semid);
  180.     if(rcs == -1)
  181.     {
  182.      perror("sem_p");
  183.      exit(1);
  184.      }
  185.      printf("server get %s\n",s_addr);     
  186.      sem_v(semid);
  187.      sleep(1);
  188.    }    
  189.   }
  190.   return 0;
  191.     }
client程序:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <errno.h>
  5. #include <sys/types.h>
  6. #include <sys/ipc.h>
  7. #include <sys/sem.h>
  8. #include <sys/shm.h>
  9. #include <unistd.h>

  10. #define path_shm "/opt"
  11. #define PERM_shm 0666 | IPC_CREAT

  12. #define path_sem "/tmp"
  13. #define PERM_sem 0666 | IPC_CREAT
  14. //semctl()第四个参数
  15. union semun{
  16.     int val;
  17.     struct semid_ds *buf;
  18.     unsigned short *array;
  19.     };    
  20. //创建信号量
  21. int creat_sem(void)
  22. {
  23.   int semid = 0;
  24.   key_t sem_key;
  25.     
  26.   sem_key = ftok(path_sem,10);
  27.   if(sem_key == -1)
  28.   {
  29.   perror("key error");
  30.   return -1;
  31.   }
  32.               
  33.   semid = semget(sem_key,1,PERM_sem);
  34.   if(semid == -1)
  35.   {
  36.   perror("semid");
  37.   return -1;
  38.   }
  39.   return semid;
  40.   }
  41. //初始化信号量,在使用之前必须先初始化
  42. int init_sem(int semid)
  43. {
  44.   union semun sem_arg;
  45.   sem_arg.val = 1;
  46.     
  47.   int rc;
  48.   rc = semctl(semid, 0, SETVAL, sem_arg);
  49.   if(rc == -1)
  50.   {
  51.   perror("sem init");
  52.   return -1;
  53.   }
  54.   return 0;
  55.   }
  56. //占用资源
  57. int sem_p(int semid)
  58. {
  59.   struct sembuf sem_arg;
  60.   sem_arg.sem_num = 0;
  61.   sem_arg.sem_op = -1;
  62.   sem_arg.sem_flg = SEM_UNDO;
  63.     
  64.   int rc;
  65.   printf("hello\n");
  66.   rc = semop(semid, &sem_arg, 1);
  67.   printf("world\n");
  68.   if( rc == -1 )
  69.   {
  70.   perror("sem_p");
  71.   return -1;
  72.   }
  73.   return 0;
  74. }
  75. //阻塞到信号灯为0 阻塞:block;
  76. int sem_b(int semid)
  77. {
  78.   struct sembuf sem_arg;
  79.   sem_arg.sem_num = 0;
  80.   sem_arg.sem_op = 0;
  81.   sem_arg.sem_flg = SEM_UNDO;
  82.     
  83.   int rc;
  84.   rc = semop(semid,&sem_arg,1);
  85.   if(rc == -1)
  86.   {
  87.               perror("sem_b");
  88.   return -1;
  89.   }
  90.   return 0;
  91. }
  92. //释放资源
  93. int sem_v(int semid)
  94. {
  95.   struct sembuf sem_arg;
  96.   sem_arg.sem_num = 0;
  97.   sem_arg.sem_op = 1;
  98.   sem_arg.sem_flg = SEM_UNDO;
  99.     
  100.   int rc;
  101.   rc = semop(semid,&sem_arg,1);
  102.   if(rc == -1)
  103.   {
  104.               perror("sem_v");
  105.   return -1;
  106.   }
  107.   return 0;
  108. }
  109. //删除信号量
  110. int del_sem(int semid)
  111. {
  112.   int rc;
  113.   rc = semctl(semid,0,IPC_RMID);
  114.   if(rc == -1)
  115.   {              perror("del_sem");
  116.   return -1;
  117.   }
  118.   return 0;    
  119. }
  120.     
  121. //创建共享内存
  122. int creat_shm(void)
  123. {
  124.   int shmid = 0;
  125.   key_t shm_key;
  126.   int rcs = 0;
  127.   shm_key = ftok(path_shm,11);
  128.   if(shm_key == -1)
  129.   {
  130.               perror("shm_key error");
  131.   return -1;
  132.   }
  133.             
  134.    shmid = shmget(shm_key,1024,PERM_shm);
  135.   if(shmid == -1)
  136.   {
  137.   perror("shmid");
  138.   return -1;
  139.   }
  140.   return shmid;
  141. }    
  142. int main(int argc,char **argv)
  143. {
  144.   int semid,shmid;
  145.   char *c_addr;
  146.   int liv_init = 0;
  147.   int rcs = 0;
  148.   if(argc!=2)
  149.   {
  150.     fprintf(stderr,"Usage:%s\n\a",argv[0]);
  151.   exit(1);
  152.   }
  153. //获取信号量标识符
  154.   semid = creat_sem();
  155.   if(semid == -1)
  156.   {
  157.   perror("semid error");
  158.   exit(1);
  159.   }
  160. //信号量初始化
  161.   liv_init = init_sem(semid);
  162.   if(liv_init == -1)
  163.   {
  164.   perror("init_sem error");
  165.   exit(1);
  166.   }
  167.  //获取共享内存标识符
  168.   shmid = creat_shm();
  169.   if(shmid == -1)
  170.   {
  171.   perror("shmid error");
  172.   exit(1);
  173.   }
  174. //映射内存到当前进程
  175.   c_addr = shmat(shmid, 0, 0);
  176.   memset(c_addr,'\0',1024);
  177.   //printf("server semid=%d,shmid=%d\n",semid,shmid);
  178.   
  179.    struct sembuf sem_arg[1] = {{0,-1,SEM_UNDO}}; //申请占用资源
  180.    rcs = semop(semid,sem_arg,1); //一直处于阻塞状态?
  181.   // rcs = sem_p(semid);
  182.   if(rcs == -1)
  183.   {
  184.   perror("sem_p");
  185.   exit(1);
  186.   }
  187.   else
  188.   {
  189.   strncpy(c_addr,argv[1],1024);
  190.   sleep(1);
  191.   sem_v(semid);
  192.   }
  193.   if(shmdt(c_addr) == -1)
  194.   {
  195.   perror("shmdt");
  196.   exit(1);
  197.   }
  198.   return 0;
  199. }
这两个程序在RHE5.5上调试通过;
调试过程中遇到的主要问题就是,一开始没有初始化信号量,导致了后面的semop()函数一直处于阻塞状态。所用在应用信号量的时候应该要先初始化。保持良好的编程习惯!

 参考:http://blog.chinaunix.net/uid-23193900-id-3194924.html
   
           














      









阅读(1199) | 评论(0) | 转发(0) |
0

上一篇:IPC---共享内存

下一篇:IPC---管道

给主人留下些什么吧!~~