1. 文件描述符在內核中數據結構
一個進程在此存在期間,會有一些文件被打開,從而會返回一些文件描述符,從shell
中運行一個進程,默認會有3個文件描述符存在(0、1、2),
0與進程的標準輸入相關聯,
1與進程的標準輸出相關聯,
2與進程的標準錯誤輸出相關聯,
一個進程當前有哪些打開的文件描述符可以通過/proc/進程ID/fd目錄查看。
下圖可以清楚的說明問題:
進程表項
————————————————
fd標誌 文件指針
_____________________
fd 0:|________|____________|————> 文件表
fd 1:|________|____________|
fd 2:|________|____________|
fd 3:|________|____________|
| ……. |
|_____________________|
圖1
文件表中包含:文件狀態標誌、當前文件偏移量、v節點指針,這些不是本文討論的
重點,我們只需要知道每個打開的文件描述符(fd標誌)在進程表中都有自己的文件表
項,由文件指針指向。
2. dup/dup2函數
APUE和man文檔都用一句話簡明的說出了這兩個函數的作用:複製一個現存的文件描述符。
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
從圖1來分析這個過程,當調用dup函數時,內核在進程中創建一個新的文件描述符,此
描述符是當前可用文件描述符的最小數值,這個文件描述符指向oldfd所擁有的文件表項。
進程表項
————————————————
fd標誌 文件指針
_____________________
fd 0:|________|____________| ______
fd 1:|________|____________|—————-> | |
fd 2:|________|____________| | 文件表 |
fd 3:|________|____________|—————-> | _____ |
| ……. |
|_____________________|
圖2:調用dup後的示意圖
如圖2 所示,假如oldfd的值爲1, 當前文件描述符的最小值爲3, 那麼新描述符3指向
描述符1所擁有的文件表項。
dup2和dup的區別就是可以用newfd參數指定新描述符的數值,如果newfd已經打開,則
先將其關閉。如果newfd等於oldfd,則dup2返回newfd, 而不關閉它。dup2函數返回的新
文件描述符同樣與參數oldfd共享同一文件表項。
APUE用另外一個種方法說明了這個問題:
實際上,調用dup(oldfd);
等效與
fcntl(oldfd, F_DUPFD, 0)
而調用dup2(oldfd, newfd);
等效與
close(oldfd);
fcntl(oldfd, F_DUPFD, newfd);
- #include <sys/stat.h>
- #include <string.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <unistd.h>
- int main(void)
- {
- #define STDOUT 1 //標準輸出文件描述符 號
- int nul, oldstdout;
- char msg[] = "This is a test";
- //打開一個文件,操作者具有讀寫權限 如果文件不存在就創建
- nul = open("DUMMY.FIL", O_CREAT | O_RDWR, S_IREAD | S_IWRITE);
- /* create a duplicate handle for standard
- output */
- oldstdout = dup(STDOUT);
- /*
- redirect standard output to DUMMY.FIL
- by duplicating the file handle onto the
- file handle for standard output.
- */
- dup2(nul, STDOUT);
- /* close the handle for DUMMY.FIL */
- close(nul);
- /* will be redirected into DUMMY.FIL */
- write(STDOUT, msg, strlen(msg));
- /* restore original standard output
- handle */
- dup2(oldstdout, STDOUT);
- /* close duplicate handle for STDOUT */
- close(oldstdout);
- return 0;
- }