I have a minimal complete example where the Linux API behavior seems to be a bit ambiguous. The stdout is closed, reopened and used as a regular file's descriptor:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
system("echo > testfile");
fclose(stdout);
int fd = open("testfile", O_WRONLY);
if (fd < 0) {
perror("open():");
return -1;
}
dprintf(fd, "Test\n");
system("echo Test2");
system("echo TestFFFFFFFFFFFF >> /proc/$$/fd/1"); // problem line
sleep(5);
dprintf(fd, "Test4\n");
system("echo Test5");
return 0;
}
Before sleep in testfile
I see
Test
Test2
TestFFFFFFFFFFFF
But after sleep this line is overwritten:
Test
Test2
Test4
Test5
FFFF
It seems that for regular files this behavior is strange: if we write something to file - we change its position an appropriate way (to the end).
But for stdout
it looks quite reasonable: if we write to stdout
- we rewind its position back after finish writing (to the start).
Is this a bug or the right way?
P.S. Besides the bash
I also tried dash
and ksh
- the same thing.
CodePudding user response:
The question is:
Is this a bug or the right way?
It is the right way.
/proc/$$/fd/1
is a symlink to testfile
, so >> /proc/$$/fd/1
is the same as >> testfile
. It is a separate child process that writes to testfile
. When this child process terminates, you see the stuff it has written in the file. It has no effect on your parent process.
STDOUT_FILENO
is not special, it's just like any other file descriptor.
Why child processes affect your parent process file descriptor position? Because dup
licated file descriptors refer to the same open file description. Read it twice - "file descriptor" != "file description". It is like a double-mapping. We say, file descriptors are not copied, they are duplicated, they share the file offset and file status flags, because they share the file description. See man 2 dup
and man 2 open
.
When a separate process opens the same file, it has a separate file description, so it does not affect your process file description.
(It is so badly named. I mentally call "file descriptor" like "system handle" (because it can refer to anything kernel wants to) and call "file description" like "file data" (if it's a file, it can be anything kernel wants to). It's then clear - this "system handle" refers to this "file data" and "system handle" is duplicated between processes that means that two "system handles" refer to the same open "file data". And "file data" stores file position and file locks and such. Processes have "handles", not "data". "File data" are removed automatically, when the last "system handle" is getting removed (the confusing man 2 unlink
).)