Fast and Slow System Calls
- The first week of class, we learned about kernel-resident functions called system calls.
- Fast system calls are those that return immediately, where "immediately" means they just need the processor and other local resources to get their work done.
- By my own definitely, there's no hard limit on the time they're allowed to take.
- Even if a system call were to take 60 seconds to complete, it'd be considered fast if all 60 seconds were spent executing code (i.e. no idle time blocking on external resources.)
- Slow system calls are those that wait for an indefinite period of time for something to finish (e.g. waitpid), for something to become available (e.g. read from a client socket that's not seen any data recently), or for some external event (e.g. network connection request from client via accept.)
- Calls to read are considered fast if they're reading from a local file, because there aren't really any external resources to prevent read from doing it's work. It's true that some hardware needs to be accessed, but because that hardware is grafted into the machine, we can say with some confidence that the data being read from the local file will be available within a certain amount of time.
- Calls to write are considered slow if data if being published to a socket and previously published data has congested internal buffers and not been pushed off the machine yet.
- Slow system calls are the ones capable of blocking a thread of execution indefinitely, rendering that thread inert until the system call is able to return.
- We've relied on signals and signal handlers to take calls to waitpid off the normal flow of execution, and we've also relied on the WNOHANG flag to ensure that waitpid never actually blocks.
- That's an example of nonblocking.
- We just didn't call it that back then.
- We've relied on multithreading to get calls to read and write off the main thread. (Note: remember that the iosocsktream class implementation layers over calls to read and write.)
- Threading doesn't make the calls to read and write any faster, but it does parallelize the stall times, and it also frees up the main thread so that it can, if it chooses, focus on other computations.
- You should be intimiately familiar with these ideas based on your work with news-aggregator and proxy.
- accept and read are the system calls that everyone always identifies as slow.
Making Slow System Calls Fast
- Configuring a descriptor as nonblocking.
- It's possible to configure a descriptor to be what's called nonblocking. When nonblocking descriptors are passed to accept, read, or write, the function will always return as quickly as possible without waiting for anything external.
- accept
- If a client connection is available when accept is called, it'll return immediately with a socket connection to that client.
- Provided the server socket is configured to be nonblocking, accept will return -1 instead of blocking if there are no pending connection requests. The -1 normally denotes that some error occurred, but if the errno global is set to EWOULDBLOCK, the -1 isn't really identifying an error, but instead saying that accept would have blocked had the server socket passed to it been a traditional (i.e. blocking) socket descriptor.
- read
- If one or more bytes of data are immediately available when read is called, then those bytes (or at least some of them) are written into the supplied character buffer, and the number of bytes placed is returned.
- If no data is available (and the descriptor is still open and the other end of the descriptor hasn't been shut down,) then read will return -1, provided the descriptor has been configured to be nonblocking. Again, this -1 normally denotes that some error occurred, but in this case, the errno global is set to EWOULDBLOCK. That's our clue that read didn't really fail. The -1/EWOULDBLOCK combination is just saying that the call to read would have blocked had the descriptor been a traditional (i.e. blocking) one.