linux重定向標準輸入後,再重新打開標準輸入爲什麼會失效?

首先來看一段代碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    int fd1,fd2;
    
    fd2 = open("p1.py",O_RDONLY,0);
    dup2(fd2,0);
    char c;
    while( read(0,&c,1) > 0)
        printf("%c",c);
    close(fd2);

    fd1 = open("/dev/stdin",O_RDONLY);

    printf("%d\n",fd1);
    while( read(fd1, &c, 1) > 0)
        printf("%c",c);

    return 0;
}

先講一下文件描述符是什麼。
linux進程每打開一個文件都會返回一個文件描述符(整數)。
這個描述符實際是打開的文件在該進程的描述符表上的偏移值。
比如說p是描述符表,1是描述符,那麼p[1]就能夠索引到1描述符對應的打開文件。
有了這個偏移值(文件描述符)就能夠快速的找到並操作文件。

(當然實際的情況是這個文件描述符能夠索引到打開文件表表項,然後再通過打開文件表表項索引到對應的V-node節點表表項,而這個v-node節點表表項才代表真正的文件。不過只從邏輯上來看不需要理解這個括號裏的說明。)

解釋一下這段程序:
1、首先打開了一個叫做p1.py的文件。
2、然後用dup2這個函數使得文件描述符0這個位置的指針指向文件描述符fd2這個位置的指針指向的文件。
也就是說本來是這樣:p[0] = &fiel1,p[fd2] = &file2,現在p[0] = p[fd2] = &file2。
而我們都知道文件描述符0這個位置對應的文件file1是標準輸入文件/dev/stdin。
那麼這個函數的意義就是把標準輸入重定向到了p1.py這個文件裏了。

之後再用標準輸入比如說scanf(),來讀,那麼都是從p1.py這個文件裏讀了。
3、然後把這個文件裏的東西讀出來並輸出到屏幕上。
4、打開/dev/stdin這個文件,這個文件是標準輸入。
5、把這個文件裏的東西讀出來並打印到屏幕上。

預期結果是什麼?
執行到第5步應該停下來,等待鍵盤輸入。
然後把輸入的東西打印到屏幕。

實際結果?
沒有等待鍵盤輸入,它直接把p1.py的文件內容輸出到屏幕上,也就是說和上面的輸出是一樣的!!!

why???how???
我打開了一個文件,應該讀取文件裏的內容。而這個文件是標準輸入,那麼既然是標準輸入(從鍵盤輸入),我還沒輸入呢,怎麼就輸出結果了呢???而且結果還很奇特。。。

這是因爲,標準輸入這個文件/dev/stdin是個鏈接文件!!!
存放的是別的文件的地址!!!
如果文件描述符指向的文件是個普通文件,那麼把這個文件描述符指向別的文件,就是真的指向了別的文件。
而這裏的文件描述符指向的是個鏈接文件,那麼把這個文件描述符指向別的文件,意味着什麼???意味着它
把這個鏈接文件裏的內容(地址)更改了,而它仍然指向這個文件。只不過它知道這是個鏈接文件,因此它會訪問的是這個鏈接文件中的地址對應的文件。

理解了上面的操作就可以說得通了,dup2對於鏈接文件只是修改了文件中的地址,它並沒有真正指向別的文件,這也導致了一個問題,那就是它把這個鏈接文件給修改了,如果進程再次打開這個鏈接文件,那麼鏈接文件之前存的地址就沒有了,因此執行

fd1 = open("/dev/stdin",O_RDONLY);

這個的時候,實際上是又一次打開了p1.py這個文件!!!因爲/dev/stdin這個文件裏存放的地址已經是p1.py這個文件的地址了。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章