user-0 提问: 关于C语言函数可重入的问题

回复:
user: user-0
content: 谢谢大大的解答，看来还是学的太浅了。

user: user-1
content: 你的信号处理函数中有调用 localtime_r 的地方吗？ 有的话会锁死就很自然了

user: user-2
content: 引用 7 楼 pang68599 的回复:@zhao4zhong1 And @mymtom   两位大大，我想的是为什么像我那样写会产生死锁，这个死锁是如何产生的？  自己写个程序模拟就可以啦！  *-  * Copyright (C), 2014, mymtom  *  * vi:set ts=4 sw=4:  */ #ifndef lint static const char rcsid[] = "$Id$"; #endif /* not lint */  /**  * @file    foo.c  * @brief     */  #include <stdio.h> #include <signal.h>  #include <pthread.h> #include <sched.h>  struct tm * my_localtime(const time_t *timep) {     struct tm *res;      sigset_t set, oset;      sigemptyset(&set);     sigaddset(&set, SIGINT);      sigprocmask(SIG_SETMASK, &set, &oset);     res = localtime(timep);     sigprocmask(SIG_SETMASK, &oset, NULL);      return res; }  #if defined(_REENTRANT_LOCALTIME) #define localtime   my_localtime #endif  static void sig_int(int sig) {     time_t tks;     struct tm tms;      printf("sig=%d\n", sig);     tks = time(NULL);     tms = *localtime(&tks); }  void *worker(void *arg) {     int i;     time_t tks;     struct tm tms;      for (i = 0; i < 10000; ++i) {         printf("i=%d\n", i);         tks = time(NULL);         tms = *localtime(&tks);         sched_yield();     }      return NULL; }  int main(int argc, char *argv[]) {     int i;     pthread_t tid;      sigset(SIGINT, &sig_int);      pthread_create(&tid, NULL, &worker, NULL);      sched_yield();     for (i = 0; i < 10000; ++i) {         pthread_kill(tid, SIGINT);         sched_yield();     }     pthread_join(tid, NULL);      return 0; }  因为localtime和会调用一个处理时区的函数__tz_convert，这个函数会使用锁l进行同步 如果加锁后解锁前收到信号，执行信号处理函数的时候再次加锁，就会死锁，这个程序我编译后，运行不到5次就出现死锁了。 下面是用调试器看到的信息，注意标红的部分，死锁出现在信号处理函数的地方！ $ ./foo sig=2 i=0 sig=2 i=1 sig=2 i=2 sig=2 i=3 sig=2 i=4 sig=2 i=5 sig=2 i=6 sig=2 i=7 sig=2 i=8 sig=2 i=9 sig=2 i=10 sig=2 i=11 sig=2 i=12 sig=2 i=13 sig=2 i=14 sig=2 i=15 sig=2 i=16 sig=2 i=17 sig=2 i=18 sig=2 i=19 sig=2 i=20 sig=2 i=21 sig=2 i=22 i=23 i=24 i=25 i=26 i=27 i=28 i=29 i=30 i=31 i=32 i=33 i=34 i=35 i=36 i=37 i=38 i=39 i=40 i=41 i=42 i=43 i=44 i=45 i=46 i=47 i=48 i=49 i=50 i=51 i=52 i=53 i=54 i=55 i=56 i=57 i=58 i=59 i=60 i=61 i=62 i=63 i=64 i=65 i=66 i=67 i=68 i=69 i=70 i=71 i=72 i=73 i=74 i=75 i=76 i=77 i=78 i=79 i=80 i=81 i=82 i=83 i=84 i=85 i=86 i=87 i=88 i=89 i=90 i=91 i=92 i=93 i=94 i=95 i=96 i=97 i=98 i=99 i=100 i=101 i=102 i=103 i=104 i=105 i=106 i=107 i=108 i=109 i=110 i=111 i=112 i=113 i=114 i=115 i=116 i=117 i=118 i=119 i=120 i=121 i=122 i=123 i=124 i=125 i=126 i=127 i=128 i=129 i=130 i=131 i=132 i=133 i=134 i=135 i=136 i=137 i=138 i=139 i=140 i=141 i=142 i=143 i=144 i=145 i=146 i=147 i=148 i=149 i=150 i=151 i=152 i=153 i=154 i=155 i=156 i=157 i=158 i=159 i=160 i=161 i=162 i=163 i=164 i=165 i=166 i=167 i=168 i=169 i=170 i=171 i=172 i=173 i=174 i=175 i=176 i=177 i=178 i=179 i=180 i=181 i=182 i=183 i=184 i=185 i=186 i=187 i=188 i=189 i=190 i=191 i=192 i=193 i=194 i=195 i=196 i=197 i=198 i=199 i=200 i=201 i=202 i=203 i=204 i=205 i=206 i=207 i=208 i=209 i=210 i=211 i=212 i=213 i=214 i=215 i=216 i=217 i=218 i=219 i=220 i=221 i=222 i=223 i=224 i=225 i=226 i=227 i=228 i=229 i=230 sig=2 ^Z [1]+  Stopped                 ./foo $ bg [1]+ ./foo & $ ps   PID TTY          TIME CMD 26461 pts/6    00:00:00 bash 26603 pts/6    00:00:00 foo 26612 pts/6    00:00:00 ps $ gdb GNU gdb (GDB) Red Hat Enterprise Linux (7.2-48.el6) Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.  Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-redhat-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. (gdb) attach 26603 Attaching to process 26603 Reading symbols from /home/feel/devel/mymtom/src/foo/foo...done. Reading symbols from /lib64/libpthread.so.0...(no debugging symbols found)...done. [Thread debugging using libthread_db enabled] [New Thread 0x7f32205a9700 (LWP 26604)] Loaded symbols for /lib64/libpthread.so.0 Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done. Loaded symbols for /lib64/libc.so.6 Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done. Loaded symbols for /lib64/ld-linux-x86-64.so.2 0x000000308040803d in pthread_join () from /lib64/libpthread.so.0 Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.25.el6.x86_64 (gdb) bt #0  0x000000308040803d in pthread_join () from /lib64/libpthread.so.0 #1  0x0000000000400a31 in main (argc=1, argv=0x7fffafe93fc8) at foo.c:83 (gdb) info thread   2 Thread 0x7f32205a9700 (LWP 26604)  0x000000307f8f619e in __lll_lock_wait_private () from /lib64/libc.so.6 * 1 Thread 0x7f32205ab700 (LWP 26603)  0x000000308040803d in pthread_join () from /lib64/libpthread.so.0 (gdb) thread 2 [Switching to thread 2 (Thread 0x7f32205a9700 (LWP 26604))]#0  0x000000307f8f619e in __lll_lock_wait_private ()    from /lib64/libc.so.6 (gdb) bt #0  0x000000307f8f619e in __lll_lock_wait_private () from /lib64/libc.so.6 #1  0x000000307f89cc9d in _L_lock_2163 () from /lib64/libc.so.6 #2  0x000000307f89ca57 in __tz_convert () from /lib64/libc.so.6 #3  0x00000000004008e2 in sig_int (sig=2) at foo.c:49 #4  <signal handler called> #5  0x000000307f89a7be in __offtime () from /lib64/libc.so.6 #6  0x000000307f89d1bf in __tzfile_compute () from /lib64/libc.so.6 #7  0x000000307f89cb17 in __tz_convert () from /lib64/libc.so.6 #8  0x0000000000400961 in worker (arg=0x0) at foo.c:61 #9  0x00000030804077e1 in start_thread () from /lib64/libpthread.so.0 #10 0x000000307f8e68ed in clone () from /lib64/libc.so.6 (gdb)

user: user-3
content: 引用 1 楼 pang68599 的回复:如何实现才能让  static somelock;  int localtime_r(...)  {      lock(somelock);           ...do something...       unlock(somelock);  }   是可重入的 试试看，不保证行： #define MAXRL 100 static somelock[MAXRL]; int localtime_r(...) {     static int L=0;     assert(L<MAXRL);     lock(somelock[L++]);       ...do something...     unlock(somelock[--L]); }

user: user-4
content: 你的信号处理函数中有调用 localtime_r 的地方吗？ 有的话会锁死就很自然了

user: user-0
content: 如何实现才能让  static somelock;  int localtime_r(...)  {      lock(somelock);           ...do something...       unlock(somelock);  }   是可重入的

user: user-3
content: 《Windows核心编程》 Synchronization Functions The following functions are used in synchronization.    CancelWaitableTimer   CreateEvent   CreateMutex   CreateSemaphore   CreateWaitableTimer   DeleteCriticalSection   EnterCriticalSection   GetOverlappedResult   InitializeCriticalSection   InitializeCriticalSectionAndSpinCount   InterlockedCompareExchange   InterlockedDecrement   InterlockedExchange   InterlockedExchangeAdd   InterlockedIncrement   LeaveCriticalSection   MsgWaitForMultipleObjects   MsgWaitForMultipleObjectsEx   OpenEvent   OpenMutex   OpenSemaphore   OpenWaitableTimer   PulseEvent   QueueUserAPC   ReleaseMutex   ReleaseSemaphore   ResetEvent   SetCriticalSectionSpinCount   SetEvent   SetWaitableTimer   SignalObjectAndWait   TimerAPCProc   TryEnterCriticalSection   WaitForMultipleObjects   WaitForMultipleObjectsEx   WaitForSingleObject   WaitForSingleObjectEx

user: user-0
content: @zhao4zhong1 And @mymtom   两位大大，我想的是为什么像我那样写会产生死锁，这个死锁是如何产生的？

user: user-0
content: 引用 4 楼 mymtom 的回复:这个函数式不可重入的，在信号处理函数里不能使用这个函数的。 如果一定要使用，可以考虑在函数中屏蔽信号，这个方法是否周全，还请大家多多指教！  #include <stdio.h> #include <stdlib.h>  #include <unistd.h>  #include <pthread.h> #include <sched.h>  int increment_counter(void) {     static int counter = 0;     static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;      int result;      /* 设置信号掩码 */     sigset_t set, oset;     sigemptyset(&set);     sigaddset(&set, SIGINT);     pthread_sigmask(SIG_SETMASK, &set, &oset);      pthread_mutex_lock(&mutex);     result = ++counter;     sleep(1);     pthread_mutex_unlock(&mutex);      /* 恢复信号掩码 */     pthread_sigmask(SIG_SETMASK, &oset, NULL);      return result; }  void sig_int(int sig) {     printf("%d:sig_intr\n", increment_counter()); }  int main(int argc, char *argv[]) {     int i, n;     pthread_t tid[10];      sigset(SIGINT, &sig_int);      printf("%d\n", increment_counter());     printf("%d\n", increment_counter());     printf("%d\n", increment_counter());     printf("%d\n", increment_counter());     printf("%d\n", increment_counter());      return 0; }   版主的意思是在我处理localtime_r的把此进程的信号给（会触发localtime_r）屏蔽掉么？

user: user-0
content: 引用 3 楼 zhao4zhong1 的回复:[quote=引用 1 楼 pang68599 的回复:] 如何实现才能让  static somelock;  int localtime_r(...)  {      lock(somelock);           ...do something...       unlock(somelock);  }   是可重入的 试试看，不保证行： #define MAXRL 100 static somelock[MAXRL]; int localtime_r(...) {     static int L=0;     assert(L<MAXRL);     lock(somelock[L++]);       ...do something...     unlock(somelock[--L]); } [/quote]  如果这么使用 ...do something...就不是互斥的了， ...do something...是不是会被调用多次。

user: user-2
content: 这个函数式不可重入的，在信号处理函数里不能使用这个函数的。 如果一定要使用，可以考虑在函数中屏蔽信号，这个方法是否周全，还请大家多多指教！  #include <stdio.h> #include <stdlib.h>  #include <unistd.h>  #include <pthread.h> #include <sched.h>  int increment_counter(void) {     static int counter = 0;     static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;      int result;      /* 设置信号掩码 */     sigset_t set, oset;     sigemptyset(&set);     sigaddset(&set, SIGINT);     pthread_sigmask(SIG_SETMASK, &set, &oset);      pthread_mutex_lock(&mutex);     result = ++counter;     sleep(1);     pthread_mutex_unlock(&mutex);      /* 恢复信号掩码 */     pthread_sigmask(SIG_SETMASK, &oset, NULL);      return result; }  void sig_int(int sig) {     printf("%d:sig_intr\n", increment_counter()); }  int main(int argc, char *argv[]) {     int i, n;     pthread_t tid[10];      sigset(SIGINT, &sig_int);      printf("%d\n", increment_counter());     printf("%d\n", increment_counter());     printf("%d\n", increment_counter());     printf("%d\n", increment_counter());     printf("%d\n", increment_counter());      return 0; }