File descriptors
A file descriptor is a handle in a program that allows data to be read and written. It is assigned a number starting at 0 and going to the file descriptor limit. A descriptor of -1 indicates an error. File descriptors are limited to files but there is other sorts of descriptors (like sockets) that behave similarly so we bunch them together here.
Descriptors
When a new process is created (by means of the fork(2) system call) it inherits all open file descriptors from the parent process. New file descriptors are made by the following system calls open(2), pipe(2), socket(2), accept(2) and socketpair(2). A descriptor can be duplicated thus creating a new descriptor and number with the dup(2) system call. A descriptor can be destroyed by means of the close(2) system call. All programs executed from a shell have the following 3 descriptors open bound to the terminal: Stdin, Stdout and Stderr, representing descriptor numbers 0, 1 and 2 respectively. More about descriptors can be found in the fd manual page on the OpenBSD system; "man 4 fd" to see this.
Careful plumbing done by a program (such as a user shell) can use the pipe(2), fork(2), exec(2) and dup2(2) syscalls to connect the stdout of one program with stdin of another program this is called piping (see pipe). Long pipe chains (proper: pipelines) can be created this way.
When a program becomes a daemon stdin, stdout and stderr are usually closed and attached to the null device since they don't have a controlling terminal anymore. All writes to these descriptors will be discarded.
With descriptors control messaging can be used to talk to the kernel directly without having to write to it. Interfaces for this include fcntl(2), ioctl(2) and tcsetattr(2). With fcntl one can manipulate the close-on-exec flag which is useful with setuid programs.
Descriptors can be polled without having to use them in order to find out that one is ready for i/o. There are two methods to do this: poll(2) and select(2).
Descriptors can be set non-blocking meaning that a read will not block until data arrives when there is no data available. Instead if a block condition exists the syscall returns immediately and sets the errno to EWOULDBLOCK.
Instead of being inherited by from a parent by means of the fork(2) syscall a descriptor can be passed from one process to another. The means to do this is through a UNIX domain socket. One process that uses this technique is sshd.
There are a number of ways to see the open file descriptors of another program. In BSD the fstat(1) command lists all running programs (processes) of the system and their open descriptors furthermore it lists what type of descriptor it is (file, socket, pipe, etc) and tries to give a hint of what the descriptor is reading or writing on such as what filesystem and what inode number on that file system. In OpenBSD network sockets display what port number they have open which is useful for finding the program of that port. See ports:
In Linux you'd use lsof or dig through the /proc filesystem to see open descriptors / ports.