Calvin's Tech Space

          成于堅忍,毀于浮躁

             :: 首頁 :: 聯系 :: 聚合  :: 管理
          ?? 在linux紛繁復雜的內核代碼中,sys_dup()的代碼也許稱得上是最簡單的之一了,但是就是這么一個簡單的系統調用,卻成就了unix/linux系統最著名的一個特性:輸入/輸出重定向。
          ??? sys_dup()的主要工作就是用來“復制”一個打開的文件號,使兩個文件號都指向同一個文件。既然說簡單,我們就首先來看一下它的代碼(定義在fs/fcntl.c中):
          ?1?asmlinkage?long?sys_dup(unsigned?int?fildes)
          ?2?{
          ?3?????int?ret?=?-EBADF;
          ?4?????struct?file?*?file?=?fget(fildes);
          ?5?
          ?6?????if?(file)
          ?7?????????ret?=?dupfd(file,?0);
          ?8?????return?ret;
          ?9?}
          10?

          而sys_dup()的主體是dupfd()(定義在同一個文件中):

          ?1?static?int?dupfd(struct?file?*file,?int?start)
          ?2?{
          ?3?????struct?files_struct?*?files?=?current->files;
          ?4?????int?ret;
          ?5?
          ?6?????ret?=?locate_fd(files,?file,?start);
          ?7?????if?(ret?<?0)
          ?8?????????goto?out_putf;
          ?9?????allocate_fd(files,?file,?ret);
          10?????return?ret;
          11?
          12?out_putf:
          13?????write_unlock(&files->file_lock);
          14?????fput(file);
          15?????return?ret;
          16?}
          17?

          注:dup和dup2的原型如下:
          #include <unistd.h>

          int dup(int file_descriptor);
          int dup2(int file_descriptor1, int file_descriptor2)
          dup返回的文件描述符總是取最小的可用值
          dup2返回的文件描述符或者與file_descriptor2相同,或者是第一個大于該參數的可用值。


          ??? 而這么一個簡單的系統調用是如何完成重定向這個艱巨的任務的呢?我們不妨先看個例子。
          ??? 當我們在shell下輸入如下命令:“echo hello!”,這條命令要求shell進程執行一個可執行文件echo,參數為“hello!”。當shell接收到命令之后,先找到 bin/echo,然后fork()出一個子進程讓他執行bin/echo,并將參數傳遞給它,而這個進程從shell繼承了三個標準文件,即標準輸入 (stdin),標準輸出(stdout)和標準出錯信息(stderr),他們三個的文件號分別為0、1、2。而至于echo進程的工作很簡單,就是將參數“hello!”寫到標準輸出文件中去,通常都是我們的顯示器上。但是如果我們將命令改成“echo hello! > foo”,則在執行時輸出將會被重定向到磁盤文件foo中(注:重定向于文件描述符有關)。我們假定在此之前該shell進程只有三個標準文件打開,文件號分別為0、1、2,以上命令行將按如下序列執行:
          ??? (1) 打開或創建磁盤文件foo,如果foo中原來有內容,則清除原來內容,其文件號為3。
          ??? (2) 通過dup()復制文件stdout,即將文件號1出的file結構指針復制到文件號4處,目的是將stdout的file指針暫時保存一下
          ??? (3) 關閉stdout,即1號文件,但是由于4號文件對stdout也同時有個引用,所以stdout文件并未真正關閉,只是騰出1號文件號位置。
          ??? (4) 通過dup(),復制3號文件(即磁盤文件foo),由于1號文件關閉,其位置空缺,故3號文件被復制到1號,即進程中原來指向stdout的指針指向了foo。
          ??? (5) 通過系統調用fork()和exec()創建子進程并執行echo,子進程在執行echo前夕關閉3號和4號文件,只留下0、1、2三個文件,請注意,這 時的1號文件已經不是stdout而是磁盤文件foo了。當echo想向stdout文件寫入“hello!”時自然就寫入到了foo中。
          ??? (6) 回到shell后,關閉指向foo的1號與3號文件文件,再用dup()和close()將2號恢復至stdout,這樣shell就恢復了0、1、2三個標準輸入/輸出文件。

          ??? 由此可見,當echo程序(或其他)在運行的時候并不知道stdout(對于stdin和stderr同樣)指向什么,進程與實際輸出文件或設備的結合是在運行時由其父進程“包辦”的。這樣就簡化了子進程的程序設計,因為在設計時只要跟三個邏輯上存在的文件打交道就可以了。可能有人會覺得這很像面向對象中 的多態和重載,沒有什么新奇之處,但是如果你活在30甚至40年前,可能你會改變你的看法。?
          posted on 2011-12-13 14:20 calvin 閱讀(2369) 評論(0)  編輯  收藏 所屬分類: LinuxC
          主站蜘蛛池模板: 贵港市| 临洮县| 措美县| 郸城县| 元江| 安新县| 平遥县| 江口县| 红安县| 二连浩特市| 福安市| 敦化市| 诸暨市| 库车县| 遵义市| 依兰县| 万宁市| 乡宁县| 泌阳县| 渭南市| 康保县| 宜黄县| 四会市| 平谷区| 深州市| 汝州市| 靖西县| 遂宁市| 商洛市| 威远县| 台东市| 宜兰县| 湘西| 岐山县| 武威市| 西充县| 鲁山县| 灵丘县| 北碚区| 仪征市| 石嘴山市|