Saturday, November 22, 2014

Linux epoll

Both POSIX poll and select accepts and walks the list of fd passed over and if the list is long, this affect efficiency and performance. Linex epoll() is created to address this by separate the registration from the monitoring using different calls.

epoll works by firstly creating a polling context using epoll_create1() call.    The call returns a handle which must be freed via close() call.

fd to be monitored are then add to the context using epoll_ctl().  The events to be monitored are also specified.

Program then goes into wait for the events via the epoll_wait() call.

Scatter/Gather IO

One single system call to read or write data into a string of non-contiguous buffers.  Its advantage is performance due to reduction of overhead comparing to a serial of individual read or write system calls.  In addition, the call is atomic and it will not interleave with calls from other threads.

readv() and writev() accept 3 parameters - a fd, an array of iovec structure and a count of the number of buffers.

struct iovec {
    void *iov_base  /* ptr to the start of buffer */
    size_t iov_len
}

The buffers are processed sequentially.

Linux Process Priority

Each process has a static priority in the task_struct which is its nice value (between -20 to 19 which lower number signifies higher priority).  It is static because it does not change throughout the lifetime of the process.  Schedule pick the next task to run based on the dynamic priority.

The dynamic priority is a function of the static priority and also the interactivity of processes.  Dynamic priority uses effective_prio() which add a bonus or penalty value (in range of -5 to +5) based on the interactivity of the task.  This bonus value is added to the static priority to form the dynamic priority.

Kernel tracks how active (interactive) a process via the sleep_avg field in the task_struct.  When a task is created, it receives a high sleep_avg value.  The time a task spent sleeping will be add to sleep_avg (up to MAX_SLEEP_AVG = 10 msec).  Each time a task runs, the corresponding time will be subtract from sleep_avg.  A task with high sleep_avg is I/O bound and a zero sleep_avg value means the task is processor-bound.

The size of timeslice is proportional to the task's priority.  Higher priority task receive longer timeslice.  When a task spawn a child, the child share the timeslice with the parent.  This is to prevent task keep spawning child to get unlimited supply of processor time.

Double Buffering in Standard IO

Standard IO improved performance by maintaining buffers in user space thus avoid making successive system calls.  On the other hand, the down side is that data is required to transfer between the standard IO buffer to the user supplied buffer.

Call like getc triggers a read() call which causes data to be read from the disk to the kernel buffer, copy to the standard IO buffer and finally copy to the buffer passed into the standard IO call.

putc copies the data from the supplied buffer to the standard IO buffer.  The data will then be copied out to kernel buffer via write().

Standard IO File Locking

Standard IO is thread-safe.  When there is concurrent calls made to a stream, the call will be serially applied to the stream.  If an application needs to ensure several calls are performed together without interleaved with calls from other program, it need to lock the file using flockfile().  After the series of calls are issue, unlock the file using funlockfile().

Controlling Buffering in Standard IO

There are 3 types of buffering

Unbuffer - no user buffering is performed.  Data is written to kernel.  This option is rarely used except for stderr

Line-buffered - the buffer is submit to kernel upon every newline character in the stream.  This is default for stdout (screen).

Block-buffered (Full buffering) - data is buffered in form of block.  This is the default for file.

The type of buffering is specified via the setvbuf() call.  The 3 buffering types are specified as _IONBF, _IOLBF and _IOFBF respectively.  The call must be issued after opening a stream and before performing the first IO.  The caller must also supply the buffer to be managed by Standard IO

Typically application seldom need to manipulate the buffering mode or default buffer size.

C Standard IO Library

stdio provides a platform independent user buffering solution.  Files are referred by file pointer instead of the system level fd.  The file point type FILE is in cap because stdio as originally written as MACRO and thus follow the convention.

  • fopen - open a file and return a pointer to FILE.  The file opened is called a stream.
  • fdopen - open a file using fd
  • fclose - close file
  • fcloseall - close all streams
  • fgetc - read a char from a stream
  • fgets - reading multiple characters until a NEWLINE char or EOF is reached.  The \n will be stored as part of line read.  A NULL char will be added to the end of the string
  • fungetc - put a character (casted as unsigned int) into the stream.  You can issue more than one calls and the char are pushed back like a stack - so the next fread will return the last pushed char.  The standard defines only one push back is allowed.  Linux allows multiple push back as long as memory is available.  If fungetc is followed by a seek, the pushed back char will be lost because the buffer will be reused by a new block.
  • fread - reading binary data (in form of a structure, record) from the stream.  Caller pass the size of the structure and the number of structure to be read.  The function returns the number of structure read.  If the number is less than specified, it could be due to EOF reached or error is encountered during read.  Use ferror() or feof() to identify the condition.  Note that the program needs to assume the file is created (could be by another problem in another system) with same variable sizes, alignment, padding abd byte order.
  • fputc - write a byte
  • fputs - write a string
  • fwrite - writ binary data
  • fseek - advance the position in a stream.  Uses whence parameter to indicate if the offset provided represents the absolute position, relative to the current file position or to the end of file
  • fsetpos - like fseek with whence = SEEK_SET (absolute position).  This API is provided for non-Linux system that uses complex type to store file position.
  • rewind - set the position to the start of the stream
  • fgetpos - fseek does not return the current position like lseek.  This API acheive the requirement.
  • fflush - write out standard io buffer to the kernel buffer.
  • fileno - return the file descriptor of a stream.   Caution - do not intermix standard io with system IO call