Semaphores and Mutexes
So let us look at how we can add locking to scull. Our goal is to make our operations on the scull data structure atomic, meaning that the entire operation happens at once as far as other threads of execution are concerned. For our memory leak example, we need to ensure that if one thread finds that a particular chunk of memory must be allocated, it has the opportunity to perform that allocation before any other thread can make that test. To this end, we must set up critical sections: code that can be executed by only one thread at any given time. 因此,让我们看看如何为scull添加锁。我们的目标是使我们对scull数据结构的操作成为原子性的,也就是说,就其他执行线程而言,整个操作是一次性发生的。对于我们的内存泄漏的例子,我们需要确保如果一个线程发现必须分配某块内存,它就有机会在其他线程进行这一测试之前执行这一分配。为此,我们必须设置关键部分:在任何时候只能由一个线程执行的代码。
Not all critical sections are the same, so the kernel provides different primitives for different needs. In this case, every access to the scull data structure happens in process context as a result of a direct user request; no accesses will be made from interrupt handlers or other asynchronous contexts. There are no particular latency (response time) requirements; application programmers understand that I/O requests are not usually satisfied immediately. Furthermore, the scull is not holding any other critical system resource while it is accessing its own data structures. What all this means is that if the scull driver goes to sleep while waiting for its turn to access the data structure, nobody is going to mind. 不是所有的关键部分都是一样的,所以内核为不同的需求提供了不同的基元。在这种情况下,对scull数据结构的每一次访问都发生在进程上下文中,是用户直接请求的结果;不会从中断处理程序或其他异步上下文中进行访问。没有特别的延迟(响应时间)要求;应用程序的编程人员明白,I/O请求通常不会立即满足。此外,Scull在访问自己的数据结构时,并不持有任何其他关键的系统资源。这一切意味着,如果Scull驱动在等待轮到它访问数据结构时睡觉,没有人会介意。
"Go to sleep" is a well-defined term in this context. When a Linux process reaches a point where it cannot make any further processes, it goes to sleep (or "blocks"), yielding the processor to somebody else until some future time when it can get work done again. Processes often sleep when waiting for I/O to complete. As we get deeper into the kernel, we will encounter a number of situations where we cannot sleep. The write method in scull is not one of those situations, however. So we can use a locking mechanism that might cause the process to sleep while waiting for access to the critical section. 在这种情况下,"进入睡眠 "是一个定义明确的术语。当一个Linux进程达到不能再进行任何进程的时候,它就会进入睡眠状态(或称 "阻塞"),将处理器让给其他人,直到未来某个时间它可以再次完成工作。进程在等待I/O完成时经常休眠。随着我们对内核的深入了解,我们会遇到一些不能睡觉的情况。然而,scull中的写法并不是这些情况之一。所以我们可以使用一个锁定机制,在等待访问关键部分时可能会导致进程睡眠。
Just as importantly, we will be performing an operation (memory allocation with kmalloc) that could sleep—so sleeps are a possibility in any case. If our critical sections are to work properly, we must use a locking primitive that works when a thread that owns the lock sleeps. Not all locking mechanisms can be used where sleeping is a possibility (we'll see some that don't later in this chapter). For our present needs, however, the mechanism that fits best is a semaphore. 同样重要的是,我们将执行一个可能会休眠的操作(用kmalloc进行内存分配),所以在任何情况下都有可能出现休眠。如果我们的关键部分要正常工作,我们必须使用一个锁的基元,当拥有锁的线程睡觉时,它也能工作。并非所有的锁机制都可以在可能出现睡眠的情况下使用(我们将在本章后面看到一些不能使用的锁)。然而,对于我们目前的需要来说,最适合的机制是semaphore。
Semaphores are a well-understood concept in computer science. At its core, a semaphore is a single integer value combined with a pair of functions that are typically called P and V. A process wishing to enter a critical section will call P on the relevant semaphore; if the semaphore's value is greater than zero, that value is decremented by one and the process continues. If, instead, the semaphore's value is 0 (or less), the process must wait until somebody else releases the semaphore. Unlocking a semaphore is accomplished by calling V; this function increments the value of the semaphore and, if necessary, wakes up processes that are waiting. 在计算机科学中,semaphores是一个广为人知的概念。在其核心部分,信号是一个单一的整数值,与一对通常被称为P和V的函数结合在一起。一个希望进入关键部分的进程将在相关的信号上调用P;如果信号的值大于0,则该值被减去1,进程继续。相反,如果semaphore的值为0(或更小),那么进程必须等待,直到其他人释放这个semaphore。解除信号灯是通过调用V来完成的;这个函数会增加信号灯的值,如果有必要,会唤醒正在等待的进程。
When semaphores are used for mutual exclusion—keeping multiple processes from running within a critical section simultaneously—their value will be initially set to 1. Such a semaphore can be held only by a single process or thread at any given time. A semaphore used in this mode is sometimes called a mutex, which is, of course, an abbreviation for "mutual exclusion." Almost all semaphores found in the Linux kernel are used for mutual exclusion. 当信号被用于相互排斥时--防止多个进程同时在一个关键部分内运行--它们的值最初将被设置为1。这样的信号在任何时候都只能由单个进程或线程持有。在这种模式下使用的信号灯有时被称为mutex,当然,它是 "相互排斥 "的缩写。在Linux内核中发现的几乎所有信号都是用于互斥的。