I believe that most programmers programming in Unix/Linux have the "apue in UNIX environment" Super classic masterpiece. In this book, the author explains DUP/dup2 previously mentioned "file sharing", which is helpful for understanding DUP/dup2. Here is a simple excerpt for later analysis:
Steven s said:
(1) Each process has a record item in the Process Table. Each record item contains an open file descriptor table, which can be considered as a vector, and each descriptor occupies one item. Associated with each file descriptor is:
(A) file descriptor flag.
(B) pointer to a file table item.
(2) The kernel maintains a file table for all open files. Each file table item includes:
(A) File status signs (read, write, add write, synchronization, non-blocking, etc ).
(B) Current file displacement.
(C) pointer to the table entry of the file v node.
Figure:
File descriptor table
------------
Fd0 0 | P0 -------------> File Table 0 ---------> vnode0
------------
Fd1 1 | P1 -------------> File Table 1 ---------> vnode1
------------
Fd2 2 2 | p2
------------
FD3 3 3 | p3
------------
......
......
------------
1. DUP and dup2 in a single process
Assume that process a has an opened file descriptor FD3, and its status is as follows:
Process A's file descriptor table (before dup2)
------------
Fd0 0 | P0
------------
Fd1 1 | P1 -------------> File Table 1 ---------> vnode1
------------
Fd2 2 2 | p2
------------
FD3 3 3 | P3 -------------> File Table 2 ---------> vnode2
------------
......
......
------------
It is called as follows:
N_fd = dup2 (FD3, stdout_fileno); The process status is as follows:
Process A's file descriptor table (after dup2)
------------
Fd0 0 | P0
------------
N_fd 1 | P1 ------------
------------/
Fd2 2 2 | P2/
------------ _/|
FD3 3 3 | P3 -------------> File Table 2 ---------> vnode2
------------
......
......
------------
Explanation:
N_fd = dup2 (FD3, stdout_fileno) indicates that n_fd and FD3 share a file table item (their file table Pointer Points to the same file table item ), the n_fd position in the file descriptor table is stdout_fileno, and the file table item pointed to by the original stdout_fileno is disabled. I think this should be clearly reflected. According to the above explanation, we can explain some of the problems raised in Cu:
(1) "Must the first parameter of dup2 be a valid enabled filedes? "-- Answer: required.
(2) "Can the second parameter of dup2 be a filedes value in any valid range? "-- Answer: Yes. In UNIX, the value range is [0,255].
In addition, I feel that a good way to understand dup2 is to regard FD as a struct type, as shown in the figure above. We may wish to define it:
Struct fd_t {
Int index;
Filelistitem * PTR;
};
Then, dup2 matches the index and modifies the PTR to complete the dup2 operation.
When studying dup2, you always encounter the word "redirection", which completes a "redirection from standard output to file ", after dup2, any I/O operations such as printf with the target stdout_fileno of process a will flow into the file corresponding to fd3. The following is an example program:
# Define teststr "Hello dup2/N"
Int main (){
Int FD3;
FD3 = open ("testdup2.dat", 0666 );
If (FD <0 ){
Printf ("Open error/N ");
Exit (-1 );
}
If (dup2 (FD3, stdout_fileno) <0 ){
Printf ("err in dup2/N ");
}
Printf (teststr );
Return 0;
}
The result is that you can see "Hello dup2" in testdup2.dat ".
2. Restore after redirection
There is such a post on Cu, that is, how to restore the original state after redirection? First, you can think of saving the file descriptor before redirection. So how to save it, like below?
Int s_fd = stdout_fileno;
Int n_fd = dup2 (FD3, stdout_fileno );
Or can this happen?
Int s_fd = DUP (stdout_fileno );
Int n_fd = dup2 (FD3, stdout_fileno );
What is the difference between the two methods? The answer is that the second solution is correct. The analysis is as follows: according to the first method, we only saved the index equivalent to fd_t (according to the method I mentioned earlier) on the surface, after calling dup2, the file table items pointed to by PTR are closed because the Count value is zero. If we call dup2 (s_fd, FD3) Again) an error occurs (the cause of the error is described above ). In the second method, we first perform a copy. The copied status is shown in:
Process A's file descriptor table (after DUP)
------------
Fd0 0 | P0
------------
Fd1 1 | P1 -------------> File Table 1 ---------> vnode1
------------/|
Fd2 2 2 | P2/
------------/
FD3 3 3 | P3 -------------> File Table 2 ---------> vnode2
------------/
S_fd 4 | P4 ------/
------------
......
......
------------
After calling dup2, the status is:
Process A's file descriptor table (after dup2)
------------
Fd0 0 | P0
------------
N_fd 1 | P1 ------------
------------/
Fd2 2 2 | P2/
------------ _/|
FD3 3 3 | P3 -------------> File Table 2 ---------> vnode2
------------
S_fd 4 | P4 -------------> File Table 1 ---------> vnode1
------------
......
......
------------
The meaning of DUP (FD) is that the new file descriptor returned shares a file table item with FD. Just like s_fd and fd1 shared file table 1 in the after DUP diagram.
After determining the second scheme, it is easy to restore the redirection. You only need to call dup2 (s_fd, n_fd. The following is a complete example program:
# Define teststr "Hello dup2/N"
# Define sizeofteststr 11
Int main (){
Int FD3;
Int s_fd;
Int n_fd;
FD3 = open ("testdup2.dat", 0666 );
If (FD3 <0 ){
Printf ("Open error/N ");
Exit (-1 );
}
/* Copy the standard output descriptor */
S_fd = DUP (stdout_fileno );
If (s_fd <0 ){
Printf ("err in DUP/N ");
}
/* Redirect standard output to file */
N_fd = dup2 (FD3, stdout_fileno );
If (n_fd <0 ){
Printf ("err in dup2/N ");
}
Write (stdout_fileno, teststr, sizeofteststr);/* write to testdup2.dat */
/* Redirect recovery standard output */
If (dup2 (s_fd, n_fd) <0 ){
Printf ("err in dup2/N ");
}
Write (stdout_fileno, teststr, sizeofteststr);/* output to the screen */
Return 0;
}
Note that when I output data, I use a write library function without buffering. If I use a printf with a buffer, the final result is two rows of "Hello dup2" output on the screen, and the file testdup2.dat is empty because the buffer is faulty because the final target is the screen, so the program finally outputs the buffer content to the screen.
3. DUP/dup2 between parent and child Processes
The same file descriptor of the child process and the parent process called by fork shares the same file table item, as shown in:
File descriptor table of parent process
------------
Fd0 0 | P0
------------
Fd1 1 | P1 -------------> File Table 1 ---------> vnode1
------------/|/
Fd2 2 2 | P2 |
------------ |
|
File descriptor table of sub-process B |
------------ |
Fd0 0 | P0 |
------------ |
Fd1 1 | P1 ------------------- |
------------
Fd2 2 2 | p2
------------
Therefore, the proper use of dup2 and DUP can establish a "communication bridge" between the parent and child processes ". This is not detailed here.
Iv. Summary
The flexible use of DUP/dup2 can bring you a lot of powerful functions, it took some time to summarize the above so much, do not know whether you understand thoroughly, it can only be explored in future practices.
References:
1. Advanced Programming in UNIX environment