Another experiment would involve launching multiple threads and using one thread to call a kernel that is known to be a blocking primitive. If the effect is to block all threads, the thread package is a user-level thread package. If other threads continue running, there must be some kind of cooperation between the thread package and the kernel, and it is likely that the kernel is directly implementing the threads.
Part b: If the user-to-user load balance is good, there is little reason to share CPUs between users. In this case, centralized CPU clusters make sense if the communication demand between user processes and the file system is higher than the communication demand between user processes and the display or display server, and centralized CPU clusters make sense if inter-user communication has a higher bandwidth than the communication between user processes and the display or display server.
The use of a display server at the desktop, as opposed to a long video cable, is justified if the video display is relatively static. If the display contains large amounts of real-time animation, the bandwidth needed to update the display server's data structures would actually exceed the bandwidth of a standard video signal, so it would be less expensive to centralize the display servers and only use long wires for the video signals themselves.
If the user-to-user load balance is poor, there is reason to share CPUs between users. In this case, if the processes of a single user need higher communication bandwidth than is required for communication between user processes and displays or display servers, then such balancing will be most economical within a computer. If this is not the case, it may be reasonable to use local clusters of CPU's at each user's desk.
Thus, a supply-driven load-balancing system places the responsibility for initiating load balancing with the computers that have a surplus of CPU power or memory. Such machines would seek processes that could consume these resources.
A demand-driven load-balancing scheme, in contrast, puts the responsibilty for initiating load blanancing on the kernels of the machines that are attempting to serve an excessive load. In this case, these kernels, acting as agents for processes that are creating this excessive demand, seek places for those processes to run.
To make reads from a file non-blocking, the O_NONBLOCK flag can be set, either as part of the flags argument to open() or as part of the flags to fcntl(d,F_SETFL,f).
When a call to read() would otherwise block, if O_NONBLOCK has been set and
characters are available, read() gets these characters and the return
value is positive. If no characters are available, read() returns -1 and
indicates that it would have blocked using the error codes EWOULDBLOCK
or EAGAIN.
Part b:
Nonblocking read is valuable to user-level thread packages so that other
threads can be scheduled during the execution of a thread-package read.
So, for example, the top-level outline of a thread-read operation might
be:
thread_read(...)
force file into nonblocking mode
loop
return = read( ... )
if read completed normally, exit loop
relinquish
end loop
restore file mode