Multiplexing i/o A look at some alternatives under Linux for dealing concurrently with multiple sources of device-input What’s the problem? • Some applications need to process input from more than one peripheral device • Input is normally obtained using a ‘read()’ operation on an open file-descriptor • But ‘read()’ may cause a process to sleep if no data is ready from a particular device • While asleep the process is unable to read new data that is ready on any other device Alternative approaches • There are at least five possible ways for overcoming this problem of ‘deadlock’ • All of these approaches have been used, but some have worked better than others • Four of these alternatives are supported by the standard features of UNIX systems • (The fifth approach, while very interesting, seems to be unique to the Mach system) One immediate application • We proposed the idea of a Linux program that would utilize our recent knowledge of how to program RealTek’s 8139 network controllers in our classroom’s workstations • We want any pair of users who are sitting at different computers to be able to ‘chat’ by typing messages in a window that will be visible remotely in a different window • This requires multiplexed input (Project 2) Method 1: timesharing • Any UNIX application can fork a number of child processes, each dedicated to reading data from just one of the open device-files • The operating system’s scheduler will then give each process its opportunity to read and process any new data that becomes available from its particular device-file • But scheduling consumes CPU time, and any task-coordination can add complexity Method 2: non-blocking i/o • UNIX applications can open device-files in a ‘non-blocking’ mode (if the driver allows) • Instead of sleeping when no data is ready, a process can proceed immediately to try reading from one of the other device-files • But this produces a ‘CPU-bound’ program that can ‘starve’ other system applications (by constantly calling ‘read()’ for no data) Method 3: using signals • UNIX programs can install signal-handlers • Then the application can ‘sleep’ until it is woken up by a signal that indicates new data can be read from a specific device • Indeed this approach is widely used -- in cases where the underlying device-driver implements the required support-routines for so-called ‘asynchronous’ input-output Method 4: using ‘select()’ • UNIX programs can invoke a convenient library-function, called ‘select()’, which lets the process ‘sleep’ until at least one of the device-files of interest is ready for reading new data (or for writing new data), or until a predetermined ‘timeout’ has expired • This approach is favored because it enjoys even greater efficiency and flexibility, not to mention simplicity, than using ‘signals’ Method 5: multiplexed-read • This method is the most efficient of all, as it needs only one system-call, in ‘blocking mode’, to be sure of obtaining new data from at least one device in a specified set • Its programming syntax is not (yet) part of the POSIX (Portable Operating Systems) standard; it’s not implemented in Linux Using Method 4 • To support use of the ‘select()’ function, a character-mode device-driver needs only to implement a ‘poll()’ driver-method • It is very straightforward to do this: review Chapter 6 of “Linux Device Drivers (3Ed)” • We do it for our RealTek driver ‘withpoll.c’ • Our ‘trychat.cpp’ demo-program uses the services of this ‘withpoll.c’ device-driver The ‘fd_set’ object Header-file: Declare: Initialize: Setup for use: #include <sys/select.h> fd_set readset; FD_ZERO( &readset ); FD_SET( nic, &readset ); FD_SET( kb, &readset ); Use: int m = 1 + ( ( kb < nic ) ? nic : kb; if ( select( m, &readset, 0, 0, 0 ) < 0 ) break; if ( FD_ISSET( kb, &readset ) read_kb(); if ( FD_ISSET( nic, &readset ) read_nic(); In-class exercise • Try your hand at implementing Method 1 (the timeshare method) for a version of ‘nicchat.cpp’ that does not require ‘poll()’ • You can use our earlier ‘rxplustx.c’ driver • You can replace the ‘select()’ function-call with a ‘fork()’ function-call, letting the childprocess read from standard-input while the parent-process reads from ‘/dev/nic’.