📄 faq.html
字号:
<CODE>read()</CODE> returns -1 and looks up errno. Since<CODE>_REENTRANT</CODE> is not defined, the reference to errnoaccesses the global errno variable, which is most likely 0. Hence thecode concludes that it cannot handle the error and stops.<P><H4><A NAME="H.4">H.4: With LinuxThreads, I can no longer use the signals<code>SIGUSR1</code> and <code>SIGUSR2</code> in my programs! Why? </A></H4>LinuxThreads needs two signals for its internal operation.One is used to suspend and restart threads blocked on mutex, conditionor semaphore operations. The other is used for thread cancellation.Since the only two signals not reserved for the Linux kernel are<code>SIGUSR1</code> and <code>SIGUSR2</code>, LinuxThreads has noother choice than using them. I know this is unfortunate, and hopethis problem will be addressed in future Linux kernels, either byfreeing some of the regular signals (unlikely), or by providing morethan 32 signals (as per the POSIX 1003.1b realtime extensions).<P>In the meantime, you can try to use kernel-reserved signals either inyour program or in LinuxThreads. For instance,<code>SIGSTKFLT</code> and <code>SIGUNUSED</code> appear to beunused in the current Linux kernels for the Intel x86 architecture.To use these in LinuxThreads, the only file you need to changeis <code>internals.h</code>, more specifically the two lines:<PRE> #define PTHREAD_SIG_RESTART SIGUSR1 #define PTHREAD_SIG_CANCEL SIGUSR2</PRE>Replace them by e.g.<PRE> #define PTHREAD_SIG_RESTART SIGSTKFLT #define PTHREAD_SIG_CANCEL SIGUNUSED</PRE>Warning: you're doing this at your own risks.<P><H4><A NAME="H.5">H.5: Is the stack of one thread visible from theother threads? Can I pass a pointer into my stack to other threads?</A></H4>Yes, you can -- if you're very careful. The stacks are indeed visiblefrom all threads in the system. Some non-POSIX thread libraries seemto map the stacks for all threads at the same virtual addresses andchange the memory mapping when they switch from one thread toanother. But this is not the case for LinuxThreads, as it would makecontext switching between threads more expensive, and at any ratemight not conform to the POSIX standard.<P>So, you can take the address of an "auto" variable and pass it toother threads via shared data structures. However, you need to makeabsolutely sure that the function doing this will not return as longas other threads need to access this address. It's the usual mistakeof returning the address of an "auto" variable, only made much worsebecause of concurrency. It's much, much safer to systematicallyheap-allocate all shared data structures. <P><HR><P><H2><A NAME="I">I. X-Windows and other libraries</A></H2><H4><A NAME="I.1">I.1: My program uses both Xlib and LinuxThreads.It stops very early with an "Xlib: unknown 0 error" message. Whatdoes this mean? </A></H4>That's a prime example of the errno problem described in question <AHREF="#H.2">H.2</A>. The binaries for Xlib you're using have not beencompiled with <CODE>-D_REENTRANT</CODE>. It happens Xlib contains apiece of code very much like the one in question <AHREF="#H.2">H.2</A>. So, your Xlib fetches the error code from thewrong errno location and concludes that an error it cannot handleoccurred.<P><H4><A NAME="I.2">I.2: So, what can I do to build a multithreaded XWindows client? </A></H4>The best solution is to recompile the X libraries with multithreadingoptions set. They contain optional support for multithreading; it'sjust that all binary distributions for Linux were built without thissupport. See the file <code>README.Xfree3.3</code> in the LinuxThreadsdistribution for patches and info on how to compile thread-safe Xlibraries from the Xfree3.3 distribution. The Xfree3.3 sources arereadily available in most Linux distributions, e.g. as a source RPMfor RedHat. Be warned, however, that X Windows is a huge system, andrecompiling even just the libraries takes a lot of time and diskspace.<P>Another, less involving solution is to call X functions only from themain thread of your program. Even if all threads have their own errnolocation, the main thread uses the global errno variable for its errnolocation. Thus, code not compiled with <code>-D_REENTRANT</code>still "sees" the right error values if it executes in the main threadonly. <P><H4><A NAME="I.2">This is a lot of work. Don't you have precompiledthread-safe X libraries that you could distribute?</A></H4>No, I don't. Sorry. But you could approach the maintainers ofyour Linux distribution to see if they would be willing to providethread-safe X libraries.<P><H4><A NAME="I.3">I.3: Can I use library FOO in a multithreadedprogram?</A></H4>Most libraries cannot be used "as is" in a multithreaded program.For one thing, they are not necessarily thread-safe: callingsimultaneously two functions of the library from two threads might notwork, due to internal use of global variables and the like. Second,the libraries must have been compiled with <CODE>-D_REENTRANT</CODE> to avoidthe errno problems explained in question <A HREF="#H.2">H.2</A>.<P><H4><A NAME="I.4">I.4: What if I make sure that only one thread callsfunctions in these libraries?</A></H4>This avoids problems with the library not being thread-safe. Butyou're still vulnerable to errno problems. At the very least, arecompile of the library with <CODE>-D_REENTRANT</CODE> is needed.<P><H4><A NAME="I.5">I.5: What if I make sure that only the main threadcalls functions in these libraries?</A></H4>That might actually work. As explained in question <A HREF="#I.1">I.1</A>,the main thread uses the global errno variable, and can thereforeexecute code not compiled with <CODE>-D_REENTRANT</CODE>.<P><H4><A NAME="I.6">I.6: SVGAlib doesn't work with LinuxThreads. Why?</A></H4>Because both LinuxThreads and SVGAlib use the signals<code>SIGUSR1</code> and <code>SIGUSR2</code>. One of the two shouldbe recompiled to use different signals. See question <AHREF="#H.4">H.4</A>.<P><HR><P><H2><A NAME="J">J. Signals and threads</A></H2><H4><A NAME="J.1">J.1: When it comes to signals, what is sharedbetween threads and what isn't?</A></H4>Signal handlers are shared between all threads: when a thread calls<CODE>sigaction()</CODE>, it sets how the signal is handled not onlyfor itself, but for all other threads in the program as well.<P>On the other hand, signal masks are per-thread: each thread chooseswhich signals it blocks independently of others. At thread creationtime, the newly created thread inherits the signal mask of the threadcalling <CODE>pthread_create()</CODE>. But afterwards, the new threadcan modify its signal mask independently of its creator thread.<P><H4><A NAME="J.2">J.2: When I send a <CODE>SIGKILL</CODE> to aparticular thread using <CODE>pthread_kill</CODE>, all my threads arekilled!</A></H4>That's how it should be. The POSIX standard mandates that all threadsshould terminate when the process (i.e. the collection of all threadsrunning the program) receives a signal whose effect is toterminate the process (such as <CODE>SIGKILL</CODE> or <CODE>SIGINT</CODE>when no handler is installed on that signal). This behavior makes alot of sense: when you type "ctrl-C" at the keyboard, or when a threadcrashes on a division by zero or a segmentation fault, you really wantall threads to stop immediately, not just the one that caused thesegmentation violation or that got the <CODE>SIGINT</CODE> signal.(This assumes default behavior for those signals; see question<A HREF="#J.3">J.3</A> if you install handlers for those signals.)<P>If you're trying to terminate a thread without bringing the wholeprocess down, use <code>pthread_cancel()</code>.<P><H4><A NAME="J.3">J.3: I've installed a handler on a signal. Whichthread executes the handler when the signal is received?</A></H4>If the signal is generated by a thread during its execution (e.g. athread executes a division by zero and thus generates a<CODE>SIGFPE</CODE> signal), then the handler is executed by thatthread. This also applies to signals generated by<CODE>raise()</CODE>.<P>If the signal is sent to a particular thread using<CODE>pthread_kill()</CODE>, then that thread executes the handler.<P>If the signal is sent via <CODE>kill()</CODE> or the tty interface(e.g. by pressing ctrl-C), then the POSIX specs say that the handleris executed by any thread in the process that does not currently blockthe signal. In other terms, POSIX considers that the signal is sentto the process (the collection of all threads) as a whole, and anythread that is not blocking this signal can then handle it.<P>The latter case is where LinuxThreads departs from the POSIX specs.In LinuxThreads, there is no real notion of ``the process as a whole'':in the kernel, each thread is really a distinct process with adistinct PID, and signals sent to the PID of a thread can only behandled by that thread. As long as no thread is blocking the signal,the behavior conforms to the standard: one (unspecified) thread of theprogram handles the signal. But if the thread to which PID the signalis sent blocks the signal, and some other thread does not block thesignal, then LinuxThreads will simply queue in that thread and execute the handler only when that thread unblocksthe signal, instead of executing the handler immediately in the otherthread that does not block the signal.<P>This is to be viewed as a LinuxThreads bug, but I currently don't seeany way to implement the POSIX behavior without kernel support.<P><H4><A NAME="J.3">J.3: How shall I go about mixing signals and threadsin my program? </A></H4>The less you mix them, the better. Notice that all<CODE>pthread_*</CODE> functions are not async-signal safe, meaningthat you should not call them from signal handlers. Thisrecommendation is not to be taken lightly: your program can deadlockif you call a <CODE>pthread_*</CODE> function from a signal handler!<P>The only sensible things you can do from a signal handler is set aglobal flag, or call <CODE>sem_post</CODE> on a semaphore, to recordthe delivery of the signal. The remainder of the program can theneither poll the global flag, or use <CODE>sem_wait()</CODE> and<CODE>sem_trywait()</CODE> on the semaphore.<P>Another option is to do nothing in the signal handler, and dedicateone thread (preferably the initial thread) to wait synchronously forsignals, using <CODE>sigwait()</CODE>, and send messages to the otherthreads accordingly.<H4><A NAME="J.4">J.4: When one thread is blocked in<CODE>sigwait()</CODE>, other threads no longer receive the signals<CODE>sigwait()</CODE> is waiting for! What happens? </A></H4>It's an unfortunate consequence of how LinuxThreads implements<CODE>sigwait()</CODE>. Basically, it installs signal handlers on allsignals waited for, in order to record which signal was received.Since signal handlers are shared with the other threads, thistemporarily deactivates any signal handlers you might have previouslyinstalled on these signals.<P>Though surprising, this behavior actually seems to conform to thePOSIX standard. According to POSIX, <CODE>sigwait()</CODE> isguaranteed to work as expected only if all other threads in theprogram block the signals waited for (otherwise, the signals could bedelivered to other threads than the one doing <CODE>sigwait()</CODE>,which would make <CODE>sigwait()</CODE> useless). In this particularcase, the problem described in this question does not appear.<P>One day, <CODE>sigwait()</CODE> will be implemented in the kernel,along with others POSIX 1003.1b extensions, and <CODE>sigwait()</CODE>will have a more natural behavior (as well as better performances).<P><HR><P><H2><A NAME="K">K. Internals of LinuxThreads</A></H2><H4><A NAME="K.1">K.1: What is the implementation model forLinuxThreads?</A></H4>LinuxThreads follows the so-called "one-to-one" model: each thread isactually a separate process in the kernel. The kernel scheduler takescare of scheduling the threads, just like it schedules regularprocesses. The threads are created with the Linux<code>clone()</code> system call, which is a generalization of<code>fork()</code> allowing the new process to share the memoryspace, file descriptors, and signal handlers of the parent.<P>Advantages of the "one-to-one" model include:<UL><LI> minimal overhead on CPU-intensive multiprocessing (withabout one thread per processor);<LI> minimal overhead on I/O operations;<LI> a simple and robust implementation (the kernel scheduler doesmost of the hard work for us).</UL>The main disadvantage is more expensive context switches on mutex andcondition operations, which must go through the kernel. This ismitigated by the fact that context switches in the Linux kernel arepretty efficient.<P><H4><A NAME="K.2">K.2: Have you considered other implementationmodels?</A></H4>There are basically two other models. The "many-to-one" modelrelies on a user-level scheduler that context-switches between thethreads entirely in user code; viewed from the kernel, there is onlyone process running. This model is completely out of the question forme, since it does not take advantage of multiprocessors, and requireunholy magic to handle blocking I/O operations properly. There areseveral user-level thread libraries available for Linux, but I foundall of them deficient in functionality, performance, and/or robustness.<P>The "many-to-many" model combines both kernel-level and user-levelscheduling: several kernel-level threads run concurrently, eachexecuting a user-level scheduler that selects between user threads.Most commercial Unix systems (Solaris, Digital Unix, IRIX) implementPOSIX threads this way. This model combines the advantages of boththe "many-to-one" and the "one-to-one" model, and is attractivebecause it avoids the worst-case behaviors of both models --especially on kernels where context switches are expensive, such asDigital Unix. Unfortunately, it is pretty complex to implement, andrequires kernel support which Linux does not provide. Linus Torvaldsand other Linux kernel developers have always been pushing the"one-to-one" model in the name of overall simplicity, and are doing apretty good job of making kernel-level context switches betweenthreads efficient. LinuxThreads is just following the generaldirection they set.<P><H4><A NAME="K.3">K.3: I looked at the LinuxThreads sources, and I sawquite a lot of spinlocks and busy-waiting loops to acquire thesespinlocks. Isn't this a big waste of CPU time?</A></H4>Look more carefully. Spinlocks are used internally to protectLinuxThreads's data structures, but these locks are held for veryshort periods of time: 10 instructions or so. The probability that athread has to loop busy-waiting on a taken spinlock for more than,say, 100 cycles is very, very low. When a thread needs to wait on amutex, condition, or semaphore, it actually puts itself on a waitingqueue, then suspends on a signal, consuming no CPU time at all. Thethread will later be restarted by sending it a signal when the stateof the mutex, condition, or semaphore changes.<P><HR><ADDRESS>Xavier.Leroy@inria.fr</ADDRESS></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -