I've recently become interested in the design and history of the Linux kernel, so I decided to start at the beginning. That's right, 0.01. :-)
Of course, I realize such old code may be very buggy or incomplete, but that's part of what makes it interesting. Anyway, I noticed a peculiarity in the scheduler function sleep_on_interruptible(). Before kernel version 1.0, both forms of sleep_on() just waited on a task_struct pointer variable, setting that variable to point to the last caller of sleep_on(). wake_up() would thus only directly awaken the last caller of sleep_on(); that caller was responsible for waking up the previous caller (a pointer to which it had saved), and so on down the chain. There was no explicit wait queue, only what you might call an "implicit wait stack". For uninterruptible waits, this will do, but if the wait is interruptible, there seems to be a problem. Suppose one of the tasks waiting on the pointer variable gets a signal. It then is awakened by the signal-handling code in the scheduler. This tasks will then awaken *all* the tasks waiting on the pointer variable, just like wake_up() would. (If the task is at the top of the "wait stack", the behavior is just like a call to wake_up(). If the task is somewhere else in the wait stack, it wakes up the task at the top, puts itself back to sleep, and waits to be awakened. Then things proceed as with wake_up().) Of course, you have to do things this way with such an implementation, since there's no practical way of unlinking a task from the middle of such a wait stack. But the behavior still seems odd. Maybe I'm mistaken about the intended behavior of sleep_on_interruptible(), but I thought a signal was only supposed to wake the receiving task, not all the tasks. Am I wrong? It certainly seems to work this way in versions 1.0 and later, which used a conventional wait queue approach. Mind you, the old kernel code still works, because tasks which are awakened always seem to recheck the condition they are waiting for before proceeding; if it hasn't occurred, they go back to sleep again. But it seems inefficient to wake all the tasks simply because one got a signal, when they will all just call sleep_on_interruptible() again. Comments? Is this a valid criticism and a reason the kernel was changed, or am I just confused about sleep_on()? And, assuming the criticism is valid, why did they wait until version 1.0 to make the change? P.S. Is there any archive or record of early discussions about the kernel design? The oldest thing I can find is an archive of the linux-kernel mailing list which only dates back to the summer of 1995, four years after the project started.
|