Unix-Linux

Read-Write Lock

実験 共有メモリ上でのpthread_rwlock

shared設定するとプロセス間でmutexを利用することができる。Read-Writeロックについて、Linuxではwriteロック待ちのスレッドがいても、readロックが取得できてしまう?現象があるようだ。これについては、非標準のPTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NPを指定すると回避できる(実際に利用する場合には、各プラットフォームで要検証)

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <fcntl.h>
  4. #include <errno.h>
  5. #include <stdlib.h>
  6. #include <signal.h>
  7. #include <pthread.h>
  8. #include <sys/shm.h>
  9. #include <string.h>
  10.  
  11. #define CHILD_NUM 4
  12. #define IPCProtection (0600)
  13. #define MAX_SHMEM 3
  14. #define BUFSIZE 128
  15.  
  16. /*
  17.  * Shared memory
  18.  */
  19. typedef struct Shm
  20. {
  21. int shmid;
  22. void *memaddr;
  23. } Shm;
  24.  
  25. static Shm shmem[MAX_SHMEM];
  26. static int shmem_idx = 0;
  27.  
  28. /* pointer on shared memory */
  29. static pthread_rwlock_t *rwlock;
  30. static char *shared_data;
  31. volatile int* shutdown_flag;
  32.  
  33. /* child processes */
  34. static pid_t child_procs[CHILD_NUM];
  35.  
  36. /* my process id */
  37. static int myid;
  38.  
  39. static pid_t spawn(void);
  40. static void do_it(void);
  41. static void wait_children(void);
  42. static void kill_all_children(int sig);
  43. static void* shmem_create(int idx, size_t size);
  44. static void shmem_detach(void);
  45. static void shmem_destroy(void);
  46.  
  47. static pid_t spawn(void)
  48. {
  49. pid_t pid;
  50.  
  51. pid = fork();
  52.  
  53. if (pid < 0) {
  54. perror("failed to fork");
  55. exit(1);
  56. } else if (pid == 0) {
  57. myid = getpid();
  58. do_it();
  59. }
  60.  
  61. return pid;
  62. }
  63.  
  64. static void do_it(void)
  65. {
  66. char buf[BUFSIZE];
  67. for (;;)
  68. {
  69. pthread_rwlock_rdlock(rwlock);
  70. {
  71. //pthread_rwlock_rdlock(rwlock); /* DeadLock!! */
  72. memcpy(buf, shared_data, BUFSIZE);
  73. printf("child[%d]: %ld: %s\n", myid, time(NULL), buf);
  74. fflush(stdout);
  75. memset(buf, 0, sizeof(buf));
  76. }
  77. pthread_rwlock_unlock(rwlock);
  78.  
  79. if (*shutdown_flag == 1)
  80. break;
  81.  
  82. sleep(1);
  83. }
  84.  
  85. shmem_detach();
  86. exit(0);
  87. }
  88.  
  89. static void kill_all_children(int sig)
  90. {
  91. int i;
  92. for (i = 0; i < CHILD_NUM; ++i)
  93. {
  94. if (child_procs[i] != 0)
  95. kill(child_procs[i], sig);
  96. }
  97. }
  98.  
  99. static void wait_children(void)
  100. {
  101. pid_t pid;
  102. int status;
  103.  
  104. do {
  105. pid = waitpid(-1, &status, 0);
  106. } while (pid > 0 || errno == EINTR);
  107.  
  108. }
  109.  
  110. static void* shmem_create(int idx, size_t size)
  111. {
  112. if (MAX_SHMEM <= idx)
  113. return NULL;
  114.  
  115. shmem[idx].shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | IPC_EXCL | IPCProtection);
  116. if (shmem[idx].shmid < 0) {
  117. perror("main : shmget ");
  118. return NULL;
  119. }
  120.  
  121. shmem[idx].memaddr = shmat(shmem[idx].shmid, NULL, 0);
  122. if (shmem[idx].memaddr == (void *) -1) {
  123. perror("main : shmat ");
  124. shmctl(shmem[idx].shmid, IPC_RMID, NULL);
  125. return NULL;
  126. }
  127.  
  128. return shmem[idx].memaddr;
  129. }
  130.  
  131. static void shmem_detach(void)
  132. {
  133. int i;
  134. for (i = 0; i < MAX_SHMEM; ++i)
  135. {
  136. if (shmdt((void *) shmem[i].memaddr) < 0)
  137. {
  138. perror("main : shmdt ");
  139. }
  140. }
  141. }
  142.  
  143. static void shmem_destroy(void)
  144. {
  145. int i;
  146. for (i = 0; i < shmem_idx; ++i)
  147. {
  148. if (shmdt((void *) shmem[i].memaddr) < 0)
  149. {
  150. perror("main : shmdt ");
  151. }
  152.  
  153. if (shmctl(shmem[i].shmid, IPC_RMID, NULL) < 0)
  154. {
  155. perror("main : shmctl ");
  156. }
  157. }
  158. }
  159.  
  160. int main(int argc, char *argv[])
  161. {
  162. int i;
  163.  
  164. pthread_rwlockattr_t attrrwlock;
  165. pthread_rwlockattr_init(&attrrwlock);
  166. /* This setting is needed on Linux. */
  167. #ifdef __USE_UNIX98
  168. pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
  169. #endif
  170. pthread_rwlockattr_setpshared(&attrrwlock, PTHREAD_PROCESS_SHARED);
  171.  
  172. rwlock = shmem_create(shmem_idx++, sizeof(pthread_rwlock_t));
  173. if (rwlock == NULL) {
  174. perror("main : shmat ");
  175. exit(EXIT_FAILURE);
  176. }
  177.  
  178. if (pthread_rwlock_init(rwlock, &attrrwlock))
  179. {
  180. perror("main: pthread_rwlock_init ");
  181. exit(EXIT_FAILURE);
  182. }
  183.  
  184. pthread_rwlockattr_destroy(&attrrwlock);
  185.  
  186. shared_data = shmem_create(shmem_idx++, BUFSIZE);
  187. if (shared_data == NULL) {
  188. perror("main: shmat ");
  189. exit(EXIT_FAILURE);
  190. }
  191.  
  192. shutdown_flag = shmem_create(shmem_idx++, sizeof(int));
  193. if (shutdown_flag == NULL) {
  194. perror("main: shmat ");
  195. exit(EXIT_FAILURE);
  196. }
  197. *shutdown_flag = 0;
  198.  
  199. for (i = 0; i < CHILD_NUM; ++i)
  200. child_procs[i] = spawn();
  201.  
  202. int retries = 5;
  203. char* messages[] = {
  204. "hello", "world", "foo", "bar", "oops!"
  205. };
  206.  
  207. setbuf(stdout, NULL);
  208.  
  209. while (retries-- > 0)
  210. {
  211. char *m = messages[retries % 5];
  212. pthread_rwlock_wrlock(rwlock);
  213. {
  214. strncpy(shared_data, m, strlen(m));
  215. sleep(2);
  216. }
  217. pthread_rwlock_unlock(rwlock);
  218.  
  219. sleep(1);
  220. }
  221.  
  222. *shutdown_flag = 1;
  223.  
  224. kill_all_children(SIGTERM);
  225. wait_children();
  226.  
  227. shmem_destroy();
  228.  
  229. puts("done");
  230. exit(0);
  231. }

参考

シグナル

スレッドごとにシグナルマスクを持つ。シグナルはプロセスに送られ、複数のスレッドがいる場合は、実行中の任意のスレッドに送られる。どのスレッドが捕捉するかはシグナルマスクによる。特定のスレッドにシグナルを処理して欲しい場合は、他のスレッドでシグナルをブロックすればよい。

マルチスレッドプログラムでシグナルをマスクする場合、pthread_sigmask 関数を使用する。マルチスレッドプログラムで、sigprocmask関数を使用した場合の動作は未定義となっている。

参考リンク


トップ   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
目次
ダブルクリックで閉じるTOP | 閉じる
GO TO TOP