Boris Dorestand <
bdore...@example.com> writes:
[...]
> So let me see if I myself understand your definition of blocking. You
> say that ``with portability in mind, [...] `blocking' means `waiting an
> indeterminate time for an event which may never happen'''. Operations
> on regular files are exceptions, despite processes having to wait for
> disk operations to complete.
>
> So, for example, if I am a process, I might call read() and read() might
> take a certain short time interval to return. In a certain sense, this
> short time interval is blocking me, but assuming the system guarantees
> the time interval is short relative to my perspective as a process, it
> does not qualify as blocking me.
>
> It is obvious that, no matter how short a time interval is, it is always
> a time I have to wait in the list of sequential operations which I must
> compute. (My use of ``sequential operations'' implies that I must wait
> for each operation to finish before I can start the next.) So, for
> example, when I request to read a value from main memory, it does take
> some time for the data to be fetched from main memory and be placed
> somewhere in the CPU where I can access and compute with. But we are
> not going to say this short time interval is blocking me.
It's not "my definition" but more or less paraphrased from APUE. It's
also not a question of "having to wait for some time" but about a
certain mechanism for waiting: A process calls into the kernel in order
to perform "some I/O operation". It is determined that the operation
cannot complete immediately, hence, the process state is changed from
"runnable" to "waiting for some event" and the scheduler selects another
process to run. If "some event" actually occurs, the process will become
runnable again ("be woken up"), will eventually be scheduled to run and
pick up where it stopped.
That's the basic I/O wait mechanism and it's used for all I/O waits (and
for other waits, eg, waiting for a mutex/ semaphore to become
available).
NB: The following uses "input" instead of "I/O" for simplification.
There are certain kinds of "input sources" in a system where the kernel
cannot predict when (if ever) input will actually become
available. Originally (before networking was added to the system), these
were terminals and pipes. Later on, sockets where added to this set as
well. As a process which sleeps in the kernel until input from such a
source becomes available might never be woken up, this utilizes a
so-called "interruptible sleep": Should a signal become pending for such
a process, it's going to be woken up in order to handle the signal and
the original I/O call will return with an EINTR error.
It's possible to configure a file descriptor referring to such an input
source for "non-blocking mode" by setting the O_NONBLOCK flag. If this
is done, a read will immediately fail with an EAGAIN (EWOULDBLOCK on
early BSD and BSD-derived systems) error instead of blocking the
process. Further, a single process can handle input on a number of such
file descriptors by using one of the synchronous I/O multiplexing calls,
eg, select or poll. It will then be woken up if input becomes available
on any of the descriptors in the set (or will receive an EINTR error if
a signal which had to be handled occurred first).
====================
BIG LINE IN THE SAND
====================
x x x x x x x x x x -
\
x x x x x x x x x /- barbed wire
(machine guns are not pictured)
On the opposite side of this hopefully invincible fortification, there's disk
I/O. In certain circumstances, a process will need to wait for data from
the disk as well. As that's a local device, the kernel knows that this
wait will only take a fairly short, finite amount of time. Hence, the
kernel just lies to the process: It causes it to go asleep in the
abovementioned way. There's no way the process can avoid this (with the
synchronous I/O calls) and also, signals which become pending during the
sleep will remain pending. The kernel basically pretends that the
process hasn't really been descheduled.
Hence, while this technically uses means similar to the "blocking I/O"
described above, that's an implementation detail which is hidden from
applications.