File Descriptors Opened by Crystal at Initialization - crystal-lang/crystal GitHub Wiki

You may be concerned by the file descriptors your Crystal program has open. Your concern may be security, or you are checking for file descriptor leaks.

On Unix-like systems, at initialization, Crystal opens:

  • A copy of each of stdin, stdout, and stderr if these are terminal devices. These would appear as file descriptors 3, 4, and 5 if all of stdin, stdout, and stderr are connected to a terminal. These are separate opens. Crystal internally performs non-blocking I/O so that it can start a thread while another thread is waiting for file I/O. Due to the semantics of Unix file descriptors, we can't call O_NONBLOCK on a terminal device file descriptor that is shared with our parent and sibling processes. It breaks them. So we figure out what the terminal device is, and open it again.
  • Both ends of two pipes, for a total of 4 file descriptors. These are a kludge for signal handling. Since few libc functions are safe to call in a signal handler, Crystal catches signals, and writes to a pipe an indication that the signal was caught. Crystal's main select() or poll() loop will wake up on the pipe and receive that notification, and activate the user's signal handler.
  • One file descriptor used by eventpoll.

Most Unix-like systems have file descriptors 0, 1, and 2 open for stdin, stdout, and stderr when they execute any program. So your Crystal program will have those file descriptors.

This makes a total of 11 file descriptors you should expect to see when a minimal Crystal program starts. Depending on your app, you can also expect to see one or more sockets open for your database connection, a socket for accepting web connections, and additional sockets for the existing connections.