Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1753405
  • 博文数量: 413
  • 博客积分: 8399
  • 博客等级: 中将
  • 技术积分: 4325
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-09 10:44
文章分类

全部博文(413)

文章存档

2015年(1)

2014年(18)

2013年(39)

2012年(163)

2011年(192)

分类: C/C++

2011-12-07 22:48:16

semaphore --- 信号灯 / 信号量

虽然互斥量和条件变量为线程之间的同步通信提供了一个理想的答案,但是它们不是万能的,不能满足所有的情况。比如在:在信号处理函数中要与等待异步事件的线程之间的通信(来唤醒线程),就不能用互斥量和条件变量来完成(pthread的所有函数都不能在信号处理函数中调用,都不是信号可重入的函数)。当然,我们可以在多线程中利用sigwait或者sigwaitinfo等函数来将线程中的异步时间转化成同步事件来处理。但是,使用异步的信号处理函数已经很普遍,很可能在多线程中会碰到这样的异步编码。

此时,我们可以使用POSIX semaphore,因为sem_post函数是“信号可重入函数”。我们可以在信号处理函数中使用它来唤醒一个在信号灯上等待的线程。

1. POSIX semaphore 与 System  V semaphores
   存在这两种信号灯/信号量的API:
  1. System V semaphores (semget(2), semop(2), etc.) are an older semaphore API. 
  2. POSIX semaphores provide a simpler, and better designed interface than System V semaphores; 
  3. on the other hand POSIX semaphores are less widely available (especially on older systems) 
  4. than System V semaphores. (from man 7 sem_overview)
2. semaphore 是一种通用的同步机制 --- 既可以在进程中使用也可以在线程中使用
  1. POSIX semaphores allow processes and threads to synchronize their actions.
  2. A semaphore is an integer whose value is never allowed to fall below zero. Two operations 
  3. can be performed on semaphores: increment the semaphore value by one (sem_post(3)); and 
  4. decrement the semaphore value by one (sem_wait(3)). If the value of a semaphore is 
  5. currently zero, then a sem_wait(3) operation will block until the value becomes greater
  6. than zero.(from man 7 sem_overview)
3. semaphore分成了“有名信号灯”和“无名信号灯”
这有点类似与“有名管道”和“无名管道”,有名信号灯需要自己知道一个名字(路径),它可以在任何进程之间或者任何进程之间的线程之间通信;但是无名信号灯不需要指定名字(路径),导致它只能在父子进程等有关联的进程之间或者同一个进程中的线程通信。
  1. POSIX semaphores come in two forms: named semaphores and unnamed semaphores.
  2. Named semaphores
  3. A named semaphore is identified by a name of the form /somename; that is, a null-terminated 
  4. string of up to NAME_MAX-4 (i.e., 251) characters consisting of an initial slash, followed
  5. by one or more characters, none of which are slashes. Two processes can operate on the same 
  6. named semaphore by passing the same name to sem_open(3).
  7. The sem_open(3) function creates a new named semaphore or opens an existing named semaphore
  8. After the semaphore has been opened, it can be operated on using sem_post(3) and sem_wait(3).
  9. When a process has finished using the semaphore, it can use sem_close(3) to close the 
  10. semaphore. When all processes have finished using the semaphore, it can be removed from the 
  11. system using sem_unlink(3).
  12. Unnamed semaphores (memory-based semaphores)
  13. An unnamed semaphore does not have a name. Instead the semaphore is placed in a region of 
  14. memory that is shared between multiple threads (a thread-shared semaphore) or processes (a
  15. process-shared semaphore). A thread-shared semaphore is placed in an area of memory shared 
  16. between by the threads of a process, for example, a global variable. A process-shared 
  17. semaphore must be placed in a shared memory region (e.g., a System V shared memory segment 
  18. created using shmget(2), or a POSIX shared memory object built created using shm_open(3)).
  19. Before being used, an unnamed semaphore must be initialized using sem_init(3). It can then 
  20. be operated on using sem_post(3) and sem_wait(3). When the semaphore is no longer required,
  21. and before the memory in which it is located is deallocated, the semaphore should be 
  22. destroyed using sem_destroy(3).(from man 7 sem_overview)
4.Linux对semaphore的支持
  1. Versions
  2. Prior to kernel 2.6, Linux only supported unnamed, thread-shared semaphores. On a system 
  3. with Linux 2.6 and a glibc that provides the NPTL threading implementation, a complete 
  4. implementation of POSIX semaphores is provided.
  5. Persistence
  6. POSIX named semaphores have kernel persistence: if not removed by sem_unlink(3), 
  7. a semaphore will exist until the system is shut down.
  8. Linking
  9. Programs using the POSIX semaphores API must be compiled with cc -lrt to link against the 
  10. real-time library, librt.
  11. Accessing named semaphores via the file system
  12. On Linux, named semaphores are created in a virtual file system, normally mounted under
  13. /dev/shm, with names of the form sem.somename.
  14. (This is the reason that semaphore names are limited to NAME_MAX-4 rather than NAME_MAX 
  15.  characters.)
  16. Since Linux 2.6.19, ACLs can be placed on files under this directory,
  17. to control object permissions on a per-user and per-group basis.(from man 7 sem_overview)
5.多线程中信号灯与互斥量--条件变量的区别
1)信号灯没有“主人”的概念,也就是说:任何线程都可以释放在一个信号灯上阻塞的线程,就好像任何线程都能释放某线程已锁住的互斥量一样。
2)信号灯能独立于任何外部的状态。但是条件变量依赖于一个共享的谓词(即一个flag变量)和一个 
pthread_mutex_t变量。

6.示例代码:
  1. #include <sys/types.h>
  2. #include <unistd.h>
  3. #include <pthread.h>
  4. #include <semaphore.h>
  5. #include <signal.h>
  6. #include <time.h>
  7. #include <errno.h>
  8. #include <stdio.h>

  9. sem_t semaphore;

  10. void signal_catcher(int sig)
  11. {
  12.     sem_post(&semaphore);
  13. }

  14. void *sem_waiter(void *arg)
  15. {
  16.     int number = (int)arg;
  17.     int counter;

  18.     // Each thread waits 5 times.
  19.     for(counter = 1; counter <= 5; counter++){
  20.         while(sem_wait(&semaphore) == -1){
  21.             if(errno != EINTR)
  22.                 perror("Wait on semaphore");
  23.         }
  24.         printf("%d waking (%d)...\n", number, counter);
  25.     }
  26.     return NULL;
  27. }

  28. int main(int argc, char *argv[])
  29. {
  30.     int thread_count;
  31.     struct sigevent   sig_event;
  32.     struct sigaction  sig_action;
  33.     struct itimerspec timer_val;
  34.     sigset_t  sig_mask;
  35.     timer_t   timer_id;
  36.     pthread_t sem_waiters[5];

  37.     sem_init(&semaphore, 0, 0);

  38.     // Create 5 threads to wait on a semaphore.
  39.     for(thread_count = 0; thread_count < 5; thread_count++){
  40.         pthread_create(&sem_waiters[thread_count], NULL, sem_waiter, (void *)thread_count);
  41.     }

  42.     /*
  43.      * Set up a repeating timer using signal number SIGRTMIN,
  44.      * set to occur every 2 seconds.
  45.      */
  46.     sig_event.sigev_value.sival_int = 0;
  47.     sig_event.sigev_signo = SIGRTMIN;
  48.     sig_event.sigev_notify = SIGEV_SIGNAL;
  49.     if(timer_create(CLOCK_REALTIME, &sig_event, &timer_id) == -1)
  50.         perror("Create timer");

  51.     sigemptyset(&sig_mask);
  52.     sigaddset(&sig_mask, SIGRTMIN);
  53.     sig_action.sa_handler = signal_catcher;
  54.     sig_action.sa_mask = sig_mask;
  55.     sig_action.sa_flags = 0;
  56.     if(sigaction(SIGRTMIN, &sig_action, NULL) == -1)
  57.         perror("Set signal action");

  58.     timer_val.it_interval.tv_sec = 2;
  59.     timer_val.it_interval.tv_nsec = 0;
  60.     timer_val.it_value.tv_sec = 2;
  61.     timer_val.it_value.tv_nsec = 0;
  62.     if(timer_settime(timer_id, 0, &timer_val, NULL) == -1)
  63.         perror("Set timer");

  64.     // Wait for all threads to complete.
  65.     for(thread_count = 0; thread_count < 5; thread_count++){
  66.         pthread_join(sem_waiters[thread_count], NULL);
  67.     }

  68.     return 0;
  69. }
编译运行:
  1. digdeep@ubuntu:~/pthread/learnthread$ gcc -Wall -pthread -lrt -o semaphore semaphore_signal.c
  2. digdeep@ubuntu:~/pthread/learnthread$ ./semaphore
  3. 0 waking (1)...
  4. 1 waking (1)...
  5. 2 waking (1)...
  6. 3 waking (1)...
  7. 4 waking (1)...
  8. 0 waking (2)...
  9. 1 waking (2)...
  10. 2 waking (2)...
  11. 3 waking (2)...
  12. 4 waking (2)...
  13. 0 waking (3)...
  14. 1 waking (3)...
  15. 2 waking (3)...
  16. 3 waking (3)...
  17. 4 waking (3)...
  18. 0 waking (4)...
  19. 1 waking (4)...
  20. 2 waking (4)...
  21. 3 waking (4)...
  22. 4 waking (4)...
  23. 0 waking (5)...
  24. 1 waking (5)...
  25. 2 waking (5)...
  26. 3 waking (5)...
  27. 4 waking (5)...
  28. digdeep@ubuntu:~/pthread/learnthread$



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