Two File Descriptors to Same File

two file descriptors to same file

It depends on where you got the two file descriptors. If they come from a dup(2) call, then they share file offset and status, so doing a write(2) on one will affect the position on the other. If, on the other hand, they come from two separate open(2) calls, each will have their own file offset and status.

A file descriptor is mostly just a reference to a kernel file structure, and it is that kernel structure that contains most of the state. When you open(2) a file, you get a new kernel file structure and a new file descriptor that refers to it. When you dup(2) a file descriptor (or pass a file descriptor through sendmsg), you get a new reference to the same kernel file struct.

Is creating two FILEs for the same file descriptor well-defined?

POSIX explicitly permits multiple "handles" to be associated simultaneously with the same underlying "open file description", where handles can be either file descriptors or streams. Although it does not specifically address multiple streams opened via fdopen() on the same file descriptor, I see no reason to suppose that these would be subject to more or different requirements than any two streams associated with the same open file description are generally subject to.

POSIX defines constraints on how two handles on the same open file description may be used in order to avoid undefined behavior. It is relevant here that those constraints are few indeed for handles that are file descriptors; almost all of them apply to streams, and they are organized mainly around conditions related to buffering. The exceptions are related to positioning.

If you use your streams in a manner consistent with those constraints -- mostly, but not exclusively, by ensuring that output is not buffered unwritten in one stream when you switch to using the other -- you can expect the stream I/O functions to behave as documented. Otherwise, the behavior is explicitly undefined.

Can I check if two FILE* or file descriptor numbers refer to the same file?

Yes - compare the file device ID and inode. Per the <sys/stat.h> specification:

The st_ino and st_dev fields taken together uniquely identify the file within the system.

Use

int same_file(int fd1, int fd2) {
struct stat stat1, stat2;
if(fstat(fd1, &stat1) < 0) return -1;
if(fstat(fd2, &stat2) < 0) return -1;
return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino);
}

Can pipe be used to connect 2 file descriptors of the same process?

The linked question image says (material added to question while answer being written — leaving it here too because I can leave some [sic] comments):

This project simulates Unix pipe command. …

The parent process takes in two command-line arguments, which are two independent executable programs: p1 and p2. The parent open [sic] a kernel pipe and forks child process. The child inherits the open pipe from parent.

The parent process links its standard output to the input end of the pipe and closes the output end, then it replaces itself with p1. The child process links is [sic] standard input to the output end of the pipe and closes the input end, then the child replaces itself with p2.

… Each pipe is coded as an integer array of two file descriptors (int fd[2]). fd[0] is the input end of the pipe, and fd[1] is the output end of the pipe. …

The terminology 'input end' and 'output end' is defined.

You're left with connecting a read descriptor (fd[0]) to a write channel (FILENO_STDOUT), which "can be done" but is normally meaningless. The directions of the channels are conventional and ensure interworking between programs. But you could subvert the convention for one set of programs — which makes the code useless in other contexts. Unless the progams p1 and p2 are specially created to read from file descriptor 1 (FILENO_STDOUT) and to write to file descriptor 0 (FILENO_STDIN), the information generated by p1 will not be relayed to p2.

  • The exercise has bugs in it.

You can do exactly as told and demonstrate that it does not work. You can create a working solution which does things in the orthodox way and demonstrate that it does work.


I'd also quibble with the terminology 'pipe command'. On macOS, there is an actual command called pipe(8):

NAME

pipe - Postfix delivery to external command

SYNOPSIS

pipe [generic Postfix daemon options] command_attributes...

DESCRIPTION

The pipe(8) daemon processes requests from the Postfix queue manager to deliver messages to external commands. This program
expects to be run from the master(8) process manager.

The | notation is not a command; it is a method of inter-process communication, IPC.

A better description would be:

This project simulates the Unix shell pipe notation p1 | p2.


The question title is 'Can pipe be used to connect 2 file descriptors of the same process?' The question in the title is not actually asked in the body of the question. The answer to the question in the title is "Yes". Indeed, the pipe() system call creates two file descriptors in the same process which are connect such that data written on one of the file descriptors can be read from the other file descriptor. That's the point of the pipe() system call.

If you need to set up the link between a specific pair of file descriptors, you have to call pipe(), then dup2() twice, then close() twice. The pipe() call returns 2 arbitrary file descriptor numbers. The dup2() calls connect the desired numbers to the numbers provided by pipe(); the close() calls disconnect the numbers returned by pipe(). The only gotcha to watch for is if the numbers returned by pipe() collide with the desired numbers. Then you have to be more fiddly — probably two calls to dup() with the descriptors returned by pipe(), two calls to close() on the descriptors returned by pipe(), then two calls to dup2() to map the descriptors from dup() to the desired numbers, and two more calls to close() to close the descriptors from dup(). (Note that dup() chooses the file descriptors for you; dup2() allows you to dictate the file descriptor returned.)

There is still the sick possibility that pipe() returns one of the two descriptors desired; then one of the dup() calls returns the other of the two descriptors. Just add another dup() into the sequence. Don't call close() until you've called dup2().

Two different files but the same descriptor?

The kernel maintains a per-process table of file descriptors.

Otherwise a multi-user multi-process system could not even work, as the STDIN, STDOUT and STDERR file descriptors (0, 1, 2) of all processes would have to point to the same file (terminal), but they are obviously using independent terminals.

On the other hand, when you fork, the child process inherits all open file descriptors from the parent and they point to the same physical file. But files opened after the fork are independent.

open() what happens if I open twice the same file?

In this case, since you're opening both files as read-only, you will get two different file descriptors that refer to the same file. See the man page for open for more details.



Related Topics



Leave a reply



Submit