📄 pth.3
字号:
.IP "\fBStandard \s-1POSIX\s0 Replacement \s-1API\s0\fR" 4.IX Item "Standard POSIX Replacement API"pth_nanosleep,pth_usleep,pth_sleep,pth_waitpid,pth_system,pth_sigmask,pth_sigwait,pth_accept,pth_connect,pth_select,pth_pselect,pth_poll,pth_read,pth_readv,pth_write,pth_writev,pth_pread,pth_pwrite,pth_recv,pth_recvfrom,pth_send,pth_sendto..SH "DESCRIPTION".IX Header "DESCRIPTION".Vb 5\& ____ _ _\& | _ \e| |_| |__\& | |_) | __| '_ \e ``Only those who attempt\& | __/| |_| | | | the absurd can achieve\& |_| \e__|_| |_| the impossible.''.Ve.PP\&\fBPth\fR is a very portable \s-1POSIX/ANSI\-C\s0 based library for Unix platforms whichprovides non-preemptive priority-based scheduling for multiple threads ofexecution (aka `multithreading') inside event-driven applications. All threadsrun in the same address space of the application process, but each thread hasits own individual program counter, run-time stack, signal mask and \f(CW\*(C`errno\*(C'\fRvariable..PPThe thread scheduling itself is done in a cooperative way, i.e., the threadsare managed and dispatched by a priority\- and event-driven non-preemptivescheduler. The intention is that this way both better portability and run-timeperformance is achieved than with preemptive scheduling. The event facilityallows threads to wait until various types of internal and external eventsoccur, including pending I/O on file descriptors, asynchronous signals,elapsed timers, pending I/O on message ports, thread and process termination,and even results of customized callback functions..PP\&\fBPth\fR also provides an optional emulation \s-1API\s0 for \s-1POSIX\s0.1c threads(`Pthreads') which can be used for backward compatibility to existingmultithreaded applications. See \fBPth\fR's \fIpthread\fR\|(3) manual page fordetails..Sh "Threading Background".IX Subsection "Threading Background"When programming event-driven applications, usually servers, lots ofregular jobs and one-shot requests have to be processed in parallel.To efficiently simulate this parallel processing on uniprocessormachines, we use `multitasking' \*(-- that is, we have the applicationask the operating system to spawn multiple instances of itself. OnUnix, typically the kernel implements multitasking in a preemptive andpriority-based way through heavy-weight processes spawned with \fIfork\fR\|(2).These processes usually do \fInot\fR share a common address space. Insteadthey are clearly separated from each other, and are created by directcloning a process address space (although modern kernels use memorysegment mapping and copy-on-write semantics to avoid unnecessary copyingof physical memory)..PPThe drawbacks are obvious: Sharing data between the processes iscomplicated, and can usually only be done efficiently through sharedmemory (but which itself is not very portable). Synchronization iscomplicated because of the preemptive nature of the Unix scheduler(one has to use \fIatomic\fR locks, etc). The machine's resources can beexhausted very quickly when the server application has to serve too manylong-running requests (heavy\-weight processes cost memory). And wheneach request spawns a sub-process to handle it, the server performanceand responsiveness is horrible (heavy\-weight processes cost time tospawn). Finally, the server application doesn't scale very well with theload because of these resource problems. In practice, lots of tricksare usually used to overcome these problems \- ranging from pre-forkedsub-process pools to semi-serialized processing, etc..PPOne of the most elegant ways to solve these resource\- and data-sharingproblems is to have multiple \fIlight-weight\fR threads of executioninside a single (heavy\-weight) process, i.e., to use \fImultithreading\fR.Those \fIthreads\fR usually improve responsiveness and performance of theapplication, often improve and simplify the internal program structure,and most important, require less system resources than heavy-weightprocesses. Threads are neither the optimal run-time facility for alltypes of applications, nor can all applications benefit from them. Butat least event-driven server applications usually benefit greatly fromusing threads..Sh "The World of Threading".IX Subsection "The World of Threading"Even though lots of documents exists which describe and define the worldof threading, to understand \fBPth\fR, you need only basic knowledge aboutthreading. The following definitions of thread-related terms should atleast help you understand thread programming enough to allow you to use\&\fBPth\fR..IP "\fBo\fR \fBprocess\fR vs. \fBthread\fR" 2.IX Item "o process vs. thread"A process on Unix systems consists of at least the following fundamentalingredients: \fIvirtual memory table\fR, \fIprogram code\fR, \fIprogramcounter\fR, \fIheap memory\fR, \fIstack memory\fR, \fIstack pointer\fR, \fIfiledescriptor set\fR, \fIsignal table\fR. On every process switch, the kernelsaves and restores these ingredients for the individual processes. Onthe other hand, a thread consists of only a private program counter,stack memory, stack pointer and signal table. All other ingredients, inparticular the virtual memory, it shares with the other threads of thesame process..IP "\fBo\fR \fBkernel-space\fR vs. \fBuser-space\fR threading" 2.IX Item "o kernel-space vs. user-space threading"Threads on a Unix platform traditionally can be implemented eitherinside kernel-space or user\-space. When threads are implemented by thekernel, the thread context switches are performed by the kernel withoutthe application's knowledge. Similarly, when threads are implemented inuser\-space, the thread context switches are performed by an applicationlibrary, without the kernel's knowledge. There also are hybrid threadingapproaches where, typically, a user-space library binds one or moreuser-space threads to one or more kernel-space threads (there usuallycalled light-weight processes \- or in short LWPs)..SpUser-space threads are usually more portable and can perform fasterand cheaper context switches (for instance via \fIswapcontext\fR\|(2) or\&\fIsetjmp\fR\|(3)/\fIlongjmp\fR\|(3)) than kernel based threads. On the other hand,kernel-space threads can take advantage of multiprocessor machines anddon't have any inherent I/O blocking problems. Kernel-space threads areusually scheduled in preemptive way side-by-side with the underlyingprocesses. User-space threads on the other hand use either preemptive ornon-preemptive scheduling..IP "\fBo\fR \fBpreemptive\fR vs. \fBnon-preemptive\fR thread scheduling" 2.IX Item "o preemptive vs. non-preemptive thread scheduling"In preemptive scheduling, the scheduler lets a thread execute until ablocking situation occurs (usually a function call which would block)or the assigned timeslice elapses. Then it detracts control from thethread without a chance for the thread to object. This is usuallyrealized by interrupting the thread through a hardware interruptsignal (for kernel-space threads) or a software interrupt signal (foruser-space threads), like \f(CW\*(C`SIGALRM\*(C'\fR or \f(CW\*(C`SIGVTALRM\*(C'\fR. In non-preemptivescheduling, once a thread received control from the scheduler it keepsit until either a blocking situation occurs (again a function call whichwould block and instead switches back to the scheduler) or the threadexplicitly yields control back to the scheduler in a cooperative way..IP "\fBo\fR \fBconcurrency\fR vs. \fBparallelism\fR" 2.IX Item "o concurrency vs. parallelism"Concurrency exists when at least two threads are \fIin progress\fR at thesame time. Parallelism arises when at least two threads are \fIexecuting\fRsimultaneously. Real parallelism can be only achieved on multiprocessormachines, of course. But one also usually speaks of parallelism or\&\fIhigh concurrency\fR in the context of preemptive thread schedulingand of \fIlow concurrency\fR in the context of non-preemptive threadscheduling..IP "\fBo\fR \fBresponsiveness\fR" 2.IX Item "o responsiveness"The responsiveness of a system can be described by the user visibledelay until the system responses to an external request. When this delayis small enough and the user doesn't recognize a noticeable delay,the responsiveness of the system is considered good. When the userrecognizes or is even annoyed by the delay, the responsiveness of thesystem is considered bad..IP "\fBo\fR \fBreentrant\fR, \fBthread-safe\fR and \fBasynchronous-safe\fR functions" 2.IX Item "o reentrant, thread-safe and asynchronous-safe functions"A reentrant function is one that behaves correctly if it is calledsimultaneously by several threads and then also executes simultaneously.Functions that access global state, such as memory or files, of course,need to be carefully designed in order to be reentrant. Two traditionalapproaches to solve these problems are caller-supplied states andthread-specific data..SpThread-safety is the avoidance of \fIdata races\fR, i.e., situationsin which data is set to either correct or incorrect value dependingupon the (unpredictable) order in which multiple threads access andmodify the data. So a function is thread-safe when it still behavessemantically correct when called simultaneously by several threads (itis not required that the functions also execute simultaneously). Thetraditional approach to achieve thread-safety is to wrap a function bodywith an internal mutual exclusion lock (aka `mutex'). As you shouldrecognize, reentrant is a stronger attribute than thread\-safe, becauseit is harder to achieve and results especially in no run-time contentionbetween threads. So, a reentrant function is always thread\-safe, but notvice versa..SpAdditionally there is a related attribute for functions namedasynchronous\-safe, which comes into play in conjunction with signalhandlers. This is very related to the problem of reentrant functions. Anasynchronous-safe function is one that can be called safe and withoutside-effects from within a signal handler context. Usually very fewfunctions are of this type, because an application is very restricted inwhat it can perform from within a signal handler (especially what systemfunctions it is allowed to call). The reason mainly is, because only afew system functions are officially declared by \s-1POSIX\s0 as guaranteed tobe asynchronous\-safe. Asynchronous-safe functions usually have to bealready reentrant..Sh "User-Space Threads".IX Subsection "User-Space Threads"User-space threads can be implemented in various way. The twotraditional approaches are:.IP "\fB1.\fR" 3.IX Item "1."\&\fBMatrix-based explicit dispatching between small units of execution:\fR.SpHere the global procedures of the application are split into smallexecution units (each is required to not run for more than a fewmilliseconds) and those units are implemented by separate functions.Then a global matrix is defined which describes the execution (andperhaps even dependency) order of these functions. The main serverprocedure then just dispatches between these units by calling onefunction after each other controlled by this matrix. The threads arecreated by more than one jump-trail through this matrix and by switchingbetween these jump-trails controlled by corresponding occurred events..SpThis approach gives the best possible performance, because one canfine-tune the threads of execution by adjusting the matrix, and thescheduling is done explicitly by the application itself. It is also veryportable, because the matrix is just an ordinary data structure, andfunctions are a standard feature of \s-1ANSI\s0 C..SpThe disadvantage of this approach is that it is complicated to writelarge applications with this approach, because in those applicationsone quickly gets hundreds(!) of execution units and the control flowinside such an application is very hard to understand (because it isinterrupted by function borders and one always has to remember theglobal dispatching matrix to follow it). Additionally, all threadsoperate on the same execution stack. Although this saves memory, it isoften nasty, because one cannot switch between threads in the middle ofa function. Thus the scheduling borders are the function borders..IP "\fB2.\fR" 3.IX Item "2."\&\fBContext-based implicit scheduling between threads of execution:\fR.SpHere the idea is that one programs the application as with forkedprocesses, i.e., one spawns a thread of execution and this runs from thebegin to the end without an interrupted control flow. But the controlflow can be still interrupted \- even in the middle of a function.Actually in a preemptive way, similar to what the kernel does for theheavy-weight processes, i.e., every few milliseconds the user-spacescheduler switches between the threads of execution. But the threaditself doesn't recognize this and usually (except for synchronizationissues) doesn't have to care about this..SpThe advantage of this approach is that it's very easy to program,because the control flow and context of a thread directly followsa procedure without forced interrupts through function borders.Additionally, the programming is very similar to a traditional and wellunderstood \fIfork\fR\|(2) based approach.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -