標籤:
轉自:http://eriol.iteye.com/blog/1180624
轉自:http://www.cnblogs.com/jht/archive/2006/04/04/366086.html
dup和dup2也是兩個非常有用的調用,它們的作用都是用來複製一個檔案的描述符。它們經常用來重新導向進程的stdin、stdout和stderr。這兩個函數的原形如下:
C代碼
- #include <unistd.h>
- int dup( int oldfd );
- int dup2( int oldfd, int targetfd );
dup()函數
利用函數dup,我們可以複製一個描述符。傳給該函數一個既有的描述符,它就會返回一個新的描述符,這個新的描述符是傳給它的描述符的拷貝。這意味著,這兩個描述符共用同一個資料結構。例如,如果我們對一個檔案描述符執行lseek操作,得到的第一個檔案的位置和第二個是一樣的。下面是用來說明dup函數使用方法的程式碼片段:
C代碼
- int fd1, fd2;
- ...
- fd2 = dup( fd1 );
需要注意的是,我們可以在調用fork之前建立一個描述符,這與調用dup建立描述符的效果是一樣的,子進程也同樣會收到一個複製出來的描述符。
dup2()函數
dup2函數跟dup函數相似,但dup2函數允許調用者規定一個有效描述符和目標描述符的id。dup2函數成功返回時,目標描述符(dup2函數的第二個參數)將變成源描述符(dup2函數的第一個參數)的複製品,換句話說,兩個檔案描述符現在都指向同一個檔案,並且是函數第一個參數指向的檔案。下面我們用一段代碼加以說明:
C代碼
- int oldfd;
- oldfd = open("app_log", (O_RDWR | O_CREATE), 0644 );
- dup2( oldfd, 1 );
- close( oldfd );
在本例中,我們開啟了一個新檔案,稱為“app_log”,並收到一個檔案描述符,該描述符叫做fd1。我們調用dup2函數,參數為oldfd和1,這會導致用我們新開啟的檔案描述符替換掉由1代表的檔案描述符(即stdout,因為標準輸出檔案的id為1)。任何寫到stdout的東西,現在都將改為寫入名為“app_log”的檔案中。需要注意的是,dup2函數在複製了oldfd之後,會立即將其關閉,但不會關掉新近開啟的檔案描述符,因為檔案描述符1現在也指向它。
例子
下面我們介紹一個更加深入的範例程式碼。回憶一下命令列管道,我們可以將ls –l命令的標準輸出作為標準輸入串連到wc –l命令。接下來,我們就用一個C程式來加以說明這個過程的實現。代碼如下所示。
C代碼
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
-
- int main()
- {
- int pfds[2];
-
- if ( pipe(pfds) == 0 ) {
-
- if ( fork() == 0 ) {
-
- close(1);
- dup2( pfds[1], 1 );
- close( pfds[0] );
- execlp( "ls", "ls", "-l", NULL );
-
- } else {
-
- close(0);
- dup2( pfds[0], 0 );
- close( pfds[1] );
- execlp( "wc", "wc", "-l", NULL );
-
- }
-
- return 0;
- }
在範例程式碼中,首先在第9行代碼中建立一個管道,然後將應用程式分成兩個進程:一個子進程(第13–16行)和一個父進程(第20–23行)。接下來,在子進程中首先關閉stdout描述符(第13行),然後提供了ls –l命令功能,不過它不是寫到stdout(第13行),而是寫到我們建立的管道的輸出端,這是通過dup2函數來完成重新導向的。在第14行,使用dup2 函數把stdout重新導向到管道(pfds[1])。之後,馬上關掉管道的輸入端。然後,使用execlp函數把子進程的映像替換為命令ls –l的進程映像,一旦該命令執行,它的任何輸出都將發給管道的輸入端。
現在來研究一下管道的接收端。從代碼中可以看出,管道的接收端是由父進程來擔當的。首先關閉stdin描述符(第20行),因為我們不會從機器的鍵盤等標準裝置檔案來接收資料的輸入,而是從其它程式的輸出中接收資料。然後,再一次用到dup2函數(第21行),讓管道的輸入端作為輸入,這是通過讓檔案描述符0(即常規的stdin)重新導向到pfds[0]實現的。關閉管道的stdout端(pfds[1]),因為在這裡用不到它。最後,使用 execlp函數把父進程的映像替換為命令wc -l的進程映像,命令wc -l把管道的內容作為它的輸入(第23行)。
這兩個函數的功能是輸出的重新導向
定義這兩個函數的標頭檔是unistd.h,有興趣的可以自己看看這個標頭檔包含的內容
要提的是這個標頭檔同時定義了下面三個常量
STDERR_FILENO
= 2 標準錯誤輸出
STDIN_FILENO
= 0 標準輸入
STDOUT_FILENO
= 1 標準輸出
兄弟們學習網路編程用0,1,2這些參數的時候也得知道代表的意思
要說這兩個函數的意思,還是看一段具體的代碼
int fd, fd2;
mode_t fd_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
void redir_stdout(const char *filename)
{
fd2=dup(STDOUT_FILENO);
fd = open(filename, O_WRONLY|O_CREAT, fd_mode); //開啟檔案操作
dup2(fd, STDOUT_FILENO); //把輸出重新導向到fd標識的檔案
close(fd);
}
fd2=dup(STDOUT_FILENO);說明fd2表示了標準輸出
如果我們想把剛剛定向到fd的輸出,再定向回標準輸出,我們可以用下面的代碼實現:
void resume_stdout() //恢複輸出,把標準輸出定向到fd2,fd2代表的是標準輸出
{
dup2(fd2, STDOUT_FILENO);
close(fd2);
}
dup,dup2函數【轉】