1. Data Structure of file descriptors in the kernel
Before specifying DUP/dup2, I think it is necessary to first understand the form of file descriptors in the kernel.
When a process exists, some files are opened, and some file descriptors are returned.
By default, three file descriptors exist (0, 1, 2). 0 is associated with the standard input of the process,
1. Associated with standard output of a process; 2. Associated with standard error output of a process.
You can view the file descriptor in the/proc/process ID/FD directory. You can clearly explain the problem:
Progress table item
----------------
FD mark file pointer
_____________________
FD 0: | ________ | ____________ | ----> file table
FD 1: | ________ | ____________ |
FD 2: | ________ | ____________ |
FD 3: | ________ | ____________ |
| ....... |
| _____________________ |
Figure 1
The file table contains: File status mark, current file offset, and V node pointer, which are not discussed in this article.
The key point is that we only need to know that each open file descriptor (FD mark) has its own file table in the progress table.
Object Pointer.
2. DUP/dup2 Functions
The apue and man documents use a simple sentence to express the functions of these two functions: copying an existing file descriptor.
# Include <unistd. h>
Int DUP (INT oldfd );
Int dup2 (INT oldfd, int newfd );
This process is analyzed from figure 1. When the DUP function is called, the kernel creates a new file descriptor in the process.
The descriptor is the minimum value of the currently available file descriptor. This file descriptor points to the file table items owned by oldfd.
Progress table item
----------------
FD mark file pointer
_____________________
FD 0: | ________ | ____________ | ______
FD 1: | ________ | ____________ | ------> |
FD 2: | ________ | ____________ | file table |
FD 3: | ________ | ____________ | ------ >|______ |
| ....... |
| _____________________ |
Figure 2:
2. If the value of oldfd is 1 and the minimum value of the current file descriptor is 3, the new descriptor 3 points
File Table items owned by descriptor 1.
The difference between dup2 and DUP is that you can use the newfd parameter to specify the value of the new descriptor. If newfd is enabled
Disable it first. If newfd is equal to oldfd, dup2 returns newfd without disabling it. The new value returned by the dup2 Function
The file descriptor shares the same file table item with the oldfd parameter.
Apue illustrates this problem using another method:
In fact, DUP (oldfd) is called );
Equivalent
Fcntl (oldfd, f_dupfd, 0)
Call dup2 (oldfd, newfd );
Equivalent
Close (oldfd );
Fcntl (oldfd, f_dupfd, newfd );
3. dup2 in CGI
CGI writtenProgramWhen the browser uses the POST method to submit form data, CGI reads data from the standard
Input stdin, write data to stdout (C LanguageUse the printf function ). According to our normal principle
Solution: printf output should be displayed on the terminal. The original CGI program uses the dup2 function to convert stdout_finleno (this
Macro defined in unitstd. H, is 1) This file descriptor is redirected to the connection socket.
Dup2 (connfd, stdout_fileno);/* the actual situation also involves pipelines, not the focus of this article */
As stated in section 1, the default file descriptor 1 (stdout_fileno) of a process is consistent with the standard output stdout.
Associated. For the kernel, all open files are referenced by file descriptors, and the kernel does not know the stream
Exist (such as stdin, stdout), so the data output by the printf function to stdout is finally written to the file description
Character 1. File descriptors 0, 1, and 2 are associated with standard input, standard output, and standard error output.
It's just shell and many applications, but it's not related to the kernel.
The following flow chart can be used to illustrate the problem: (PS: although it is not a flow chart relationship, it is helpful to understand)
Printf-> stdout-> stdout_fileno (1)-> terminal (TTY)
The final output of printf is to the terminal device. The file descriptor 1 points to the current terminal, which can be understood as follows:
Stdout_fileno = open ("/dev/tty", o_rdwr );
After dup2 is used, stdout_fileno no longer points to the terminal device, but to connfd.
The output is written to connfd. Is it beautiful? :)
4. How to restore stdout_fileno In the Fork sub-process of the CGI program
If you can see this, thank you for your patience. I know that many people may feel a little complicated.
A complex problem is a collection of small problems. So it's okay to figure out every small problem. Section 3
Stdout_fileno is redirected to the connfd socket. Sometimes we may want
And some input and output are inevitable in these scripts. After fork is known,
The child process inherits all the file descriptors of the parent process, so the input and output of these scripts are not as expected.
Output to the terminal device, but associated with connfd, this will obviously disrupt the output of the webpage. So how?
Restore stdout_fileno and terminal Association?
Method 1: Save the original file descriptor before dup2 and restore it.
CodeThe implementation is as follows:
Savefd = DUP (stdout_fileno);/* savefd points to the terminal */
Dup2 (connfd, stdout_fileno);/* stdout_fileno (1) is redirected to connfd */
..... /* Handle some things */
Dup2 (savefd, stdout_fileno);/* stdout_fileno (1) restore to savefd */
Unfortunately, the CGI program cannot use this method, because dup2 is not completed in the CGI program, but in
It is not a good idea to modify the web server.
Method 2: trace the source and open the current terminal to restore stdout_fileno.
How is stdout_fileno associated with the terminal when analyzing the flow chart in Section 3? Let's just try again.
The code is implemented as follows:
Ttyfd = open ("/dev/tty", o_rdwr );
Dup2 (ttyfd, stdout_fileno );
Close (ttyfd );
/Dev/tty is the terminal where the program runs, which should be obtained in one way. Practice has proved this method
It is feasible, but I always feel a bit inappropriate. I don't know why, maybe some potential problems haven't appeared yet.