What Is the Use of File Descriptor 255 in Bash Process

Help with File descriptors in Unix

A good starting point is the article "A small trail through the Linux kernel" from 2001. The mechanisms are still similar, though the implementation has moved on and is best studied in a more recent kernel.

Inside the kernel each open file descriptor corresponds to a struct file, which contains all the information about the open file or device. The file descriptor is really no more than an index into the FDT for the process. In the Linux kernel the struct file is attached to the FDT by the function fd_install(). The struct file can be reassigned to another file descriptor by the dup2 system call.

Processes can share the same FDT if the processes were created by the clone system call with the CLONE_FILES flag, but there is no global FDT. The normal fork operation creates a new FDT which is a copy of the parent FDT. The practical use of this is for each thread of an multi-threaded application to be a cloned process sharing a common FDT, ensuring that all threads can use the same integer file descriptors. If you create a new process using fork/exec, the new process starts with the same file descriptors but can open and close files without affecting the parent.

The FDT entries for stdin, stdout, stderr are inherited from the parent. There is nothing special about their kernel implementation of these three FDT entries; their meaning comes from the conventional use by the C library. The parent process alone decides what they are connected to. They may connect to character devices, or they may have been connected to files or to pipes. For the character device case, the most normal is to be a tty or pty device. The free book Linux Device Drivers has a good overview of these.

How to find next available file descriptor in Bash?

In pure bash, you can use the following method to see if a given file descriptor (3 in this case) is available:

rco="$(true 2>/dev/null >&3; echo $?)"
rci="$(true 2>/dev/null <&3; echo $?)"
if [[ "${rco}${rci}" = "11" ]] ; then
echo "Cannot read or write fd 3, hence okay to use"
fi

This basically works by testing whether you can read or write to the given file handle. Assuming you can do neither, it's probably okay to use.

In terms of finding the first free descriptor, you can use something like:

exec 3>/dev/null    # Testing, comment out to make
exec 4</dev/null # descriptor available.

found=none
for fd in {0..200}; do
rco="$(true 2>/dev/null >&${fd}; echo $?)"
rci="$(true 2>/dev/null <&${fd}; echo $?)"
[[ "${rco}${rci}" = "11" ]] && found=${fd} && break
done
echo "First free is ${found}"

Running that script gives 5 as the first free descriptor but you can play around with the exec lines to see how making an earlier one available will allow the code snippet to find it.


As pointed out in the comments, systems that provide procfs (the /proc file system) have another way in which they can detect free descriptors. The /proc/PID/fd directory will contain an entry for each open file descriptor as follows:

pax> ls -1 /proc/$$/fd
0
1
2
255

So you could use a script similar to the one above to find a free entry in there:

exec 3>/dev/null    # Testing, comment out to make
exec 4</dev/null # descriptor available.

found=none
for fd in {0..200} ; do
[[ ! -e /proc/$$/fd/${fd} ]] && found=${fd} && break
done
echo "First free is ${found}"

Just keep in mind that not all systems providing bash will necessarily have procfs (the BDSs and CygWin being examples). Should be fine for Linux if that's the OS you're targeting.


Of course, you do still have the option of wrapping your entire shell script as something like:

(
# Your current script goes here
)

In that case, the file handles will be preserved outside those parentheses and you can manipulate them within as you see fit.

Closing opened file descriptors in child process

The following idiom is not uncommon (this is taken from the C part of MIMEDefang):

/* Number of file descriptors to close when forking */
#define CLOSEFDS 256
...

static void
closefiles(void)
{
int i;
for (i=0; i<CLOSEFDS; i++) {
(void) close(i);
}
}

(That's from mimedefang-2.78, the implementation has been changed slightly in later releases.)

It is something of a hack (as the MIMEDefang code freely admitted). In many cases it's more useful to start at FD 3 (or STDERR_FILENO+1) instead of 0. close() returns EBADF with an invalid FD, but this doesn't usually present problems (at least not in C, in other languages an exception may be thrown).

Since you can determine the file-descriptor upper limit with getrlimit(RLIMIT_NOFILE,...) which is defined as:

RLIMIT_NOFILE

This is a number one greater than the maximum value that the system may assign to a newly-created descriptor. If this limit is exceeded, functions that allocate a file descriptor shall fail with errno set to [EMFILE]. This limit constrains the number of file descriptors that a process may allocate.

you can use this (subtracting 1) as the upper limit of the loop.
The above and ulimit -n, getconf OPEN_MAX and sysconf(OPEN_MAX) should all agree.

Since open() always assigns the lowest free FD, the maximum number of open files and the highest FD+1 are the same number.

To detemine what fds are open, instead of close() use a no-op lseek(fd, 0, SEEK_CUR) which will return EBADF if the fd is not open (there's no obvious benefit to calling lseek() for a conditional close() though). socat's filan loops over 0 .. FD_SETSIZE calling fstat()/fstat64().

The libslack daemon utility which daemonizes arbitrary processes also uses this brute-force approach (while making sure to keep the first three descriptors open when used under inetd).

In the case where your program can track file handles it is preferable to do so, or use FD_CLOEXEC where available. However, should you wish to code defensively, you might prefer to distrust your parent process, say for an external handler/viewer process started by a browser, e.g. like this long-lived and ancient Mozilla bug on Unix platforms.

For the paranoid (do you want your PDF viewer to inherit every open Firefox FD including your cache, and open TCP connections?):

#!/bin/bash
# you might want to use the value of "ulimit -n" instead of picking 255
for ((fd=3; fd<=255; fd++)); do
exec {fd}<&- # close
done
exec /usr/local/bin/xpdf "$@"

Update after 15 years this issue was resolved in Firefox 58 (2018) when process creation was changed from the Netscape Portable Runtime (NSPR) API to use LaunchApp.

Share a file descriptor between parent and child after fork and exec

File descriptors are always passed between a parent and child process

When you fork a process, the file descriptors that are open in the parent(at the time of fork()) are implicitly passed on to the child. There is no need to send them explicitly.

For example:

The pseudo-code looks as follows:

In process A:

fd = open_socket_or_file;
char str_fd[3];
str_fd[0]=fd;
str_fd[1]=fd;
str_fd[2]=0;
if(fork()==0)
{
execl("./B",str_fd,NULL);
}

In the child process B you can do:

int fd = argv[1][0];
/* now do whatever you want with the fd...*/

EDIT:

In case the processes are different, you need to pass the file descriptor explicitly. This is generally done using UNIX-Domain Sockets(If you are using Linux Flavors). For code related to this, you can see this answer

How to sed output of a command and not supress output

With Linux, bash and tee:

word123=$( ./test.sh | tee >&255 >(sed -nr 's/Hello world (.*)/\1/p') )

File descriptor 255 is a non-redirected copy of stdout.

See: What is the use of file descriptor 255 in bash process

Why standard inputs are not mixed in linux?

Each process has its own set of file descriptors. File descriptor 0 of one process doesn't necessarily reference the same file handle as file descriptor 0 of another process.

Same goes for memory addresses. Because of memory virtualization, each process has its own address space. Different process can use the same address for different purposes.

pass on file descriptors to child process with node.js

Replace stdio: 'inherit' with stdio: [0, 1, 2, 3]. 'inherit' is shorthand for [0, 1, 2], per the documentation, so if you want FD 3 to be passed too, you need to add it manually. I don't see a way to tell it to inherit every FD other than manually creating an array with all of their numbers.



Related Topics



Leave a reply



Submit