Sometimes programs need to interact with the system or external environments in ways other than file reading and writing. In UNIX systems, many of these interactions are made to resemble file access. This allows developers to use familiar functions like read
, write
, and even standard I/O library functions such as fprintf
and fgets
.
A common example of this is interacting with the terminal, where you can print to the terminal using printf
, which outputs to stdout
. Similarly, you can read user input from the terminal using stdin
. These streams (stdin
, stdout
, and stderr
) are automatically opened when your program starts, with file descriptors 0, 1, and 2 respectively. Note that these streams should never be closed by your code because they are managed by the C library.
Another example is data transfer over the network. The program first creates a socket (a file descriptor representing a network connection) using the socket
system call. After setting up the socket, data can be sent and received using write
and read
, or higher-level functions like fprintf
and fgets
through fdopen
.
A pipe is a one-way communication channel between two processes. One process writes to one end of the pipe, while another reads from the other end. This is conceptually similar to reading and writing files, where processes read and write file descriptors. Shell commands like cmd1 | cmd2
use this type of communication.
UNIX systems offer access to various hardware devices through "device special files" typically located in /dev
. These files don’t represent traditional files but instead trigger specific OS functionality. For example, /dev/random
allows you to read secure pseudo-random numbers generated by the kernel.
Some interactions don’t follow the "everything is a file" model and involve other types of system calls. For example:
gettimeofday
system call retrieves the current time.fork
system call creates a new process, while execve
replaces the current process with another program.Signals allow the OS to notify programs asynchronously. Signals like SIGSEGV
(segmentation fault) are sent when the program encounters fatal errors. Programs can also customize signal handling, except for certain signals like SIGKILL
, which cannot be overridden.
stdin
, stdout
, and stderr
are integral to terminal interactions and should not be manually closed.