timer_settime | ||
fcntl | setpgid | times |
fdatasync | setsid | umask |
fork | setuid | uname |
fstat | sigaction | unlink |
fsync | sigaddset | utime |
getegid | sigdelset | wait |
geteuid | sigemptyset | waitpid |
getgid | sigfillset | write |
POSIX.1b provides counting semaphores, and most systems that support Pthreads also support POSIX.1b semaphores. You may notice that the sem_post function, which wakes threads waiting on a semaphore, appears in the list of async-signal safe functions. If your system supports POSIX semaphores (<unistd.h> defines the _POSIX_SEMAPHORES option), then Pthreads adds the ability to use semaphores between threads within a process. That means you can
A semaphore is a different kind of synchronization object — it is a little like a mutex, a little like a condition variable. The differences can make semaphores a little harder to use for many common tasks, but they make semaphores substantially easier to use for certain specialized purposes. In particular, semaphores can be
Semaphores are a general synchronization mechanism. We just have no reason to use them that way.
I am emphasizing the use of semaphores to pass information from a signal-catching function, rather than for general use, for a couple of reasons. One reason is that semaphores are part of a different standard. As I said, most systems that support Pthreads will also support POSIX. lb, but there is no such requirement anywhere in the standard. So you may well find yourself without access to semaphores, and you shouldn't feel dependent on them. {Of course, you may also find yourself
Another reason for keeping semaphores here with signals is that, although semaphores are a completely general synchronization mechanism, it can be more difficult to solve many problems using semaphores—mutexes and condition variables are simpler. If you've got Pthreads, you only
POSIX semaphores contain a count, but no 'owner,' so although they can be used essentially as a lock, they can also be used to wait for events. The terminology used in the POSIX semaphore operations stresses the 'wait' behavior rather than the 'lock' behavior. Don't be confused by the names, though; there's no difference between 'waiting' on a semaphore and 'locking' the semaphore.
A thread waits on a semaphore (to lock a resource, or wait for an event) by calling sem_wait. If the semaphore counter is greater than zero, sem_wait decrements the counter and returns immediately. Otherwise, the thread blocks. A thread can post a semaphore (to unlock a resource, or awaken a waiter) by calling sem_post. If one or more threads are waiting on the semaphore, sem_post will wake one waiter (the highest priority, or earliest, waiter). If no threads are waiting, the semaphore counter is incremented.
The initial value of the semaphore counter is the distinction between a 'lock' semaphore and a 'wait' semaphore. By creating a semaphore with an initial count of 1, you allow one thread to complete a sem_wait operation without blocking— this 'locks' the semaphore. By creating a semaphore with an initial count of 0, you force all threads that call sem_wait to block until some thread calls sem_post.
The differences in how semaphores work give the semaphore two important advantages over mutexes and condition variables that may be of use in threaded programs:
1. Unlike mutexes, semaphores have no concept of an 'owner.' This means that any thread may release threads blocked on a semaphore, much as if any thread could unlock a mutex that some thread had locked. (Although this is usually not a good programming model, there are times when it is handy.)
2. Unlike condition variables, semaphores can be independent of any external state. Condition variables depend on a shared predicate and a mutex for waiting—semaphores do not.
A semaphore is represented in your program by a variable of type sem_t. You should never make a copy of a sem_t variable — the result of using a copy of a sem_t variable in the sem_wait, sem_trywait, sem_post, and sem_destroy functions is undefined. For our purposes, a sem_t variable is initialized by calling the sem_init function. POSIX. lb provides other ways to create a 'named' semaphore that can be shared between processes without sharing memory, but there is no need for this capability when using a semaphore within a single process.
Unlike Pthreads functions, the POSIX semaphore functions use errno to report errors. That is, success is designated by returning the value 0, and errors are designated by returning the value -1 and setting the variable errno to an error code.
If you have a section of code in which you want up to two threads to execute simultaneously while others wait, you can use a semaphore without any additional state. Initialize the semaphore to the value 2; then put a sem_wait at the beginning of the code and a sem_post at the end. Two threads can then