亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 7147 | 回復(fù): 8
打印 上一主題 下一主題

文件描述符在內(nèi)核態(tài)下的一些小把戲 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2010-06-29 19:09 |只看該作者 |倒序瀏覽
本帖最后由 zyr-linux 于 2010-06-29 19:18 編輯

前面的話:
linux環(huán)境:虛擬機VMware Server上安裝的ubuntu10.4,通過putty登錄shell。

抄書:
文件描述符(file descriptor:fd)是個簡單的整數(shù),用以標明每一個被進程所打開的文件。
可以通過查看/proc/pid/fd/目錄查看該進程的fd。

先從用戶態(tài)開始:
    編寫一個helloworld,運行后通過proc可以看到進程helloworld有三個fd(0,1,2),指向3個設(shè)備文件,均為/dev/pts/0。
    然后在helloworld中打開一個文件,查看會發(fā)現(xiàn)0、1、2沒有變化,另多了一個fd(3)指向打開的文件。

繼續(xù)抄書,這次是Linux Programmer's Manual:
DESCRIPTION
       Under normal circumstances every Unix program has three streams opened for it when it starts up, one for input, one for output, and one for print‐\r
       ing diagnostic or error messages.  These are typically attached to the user's terminal (see tty(4) but might  instead  refer  to  files  or  other
       devices, depending on what the parent process chose to set up.  (See also the "Redirection" section of sh(1).)

       The input stream is referred to as "standard input"; the output stream is referred to as "standard output"; and the error stream is referred to as
       "standard error".  These terms are abbreviated to form the symbols used to refer to these files, namely stdin, stdout, and stderr.

       Each of these symbols is a stdio(3) macro of type pointer to FILE, and can be used with functions like fprintf(3) or fread(3).

       Since FILEs are a buffering wrapper around Unix file descriptors, the same underlying files may also be accessed using the raw  Unix  file  inter‐\r
       face, that is, the functions like read(2) and lseek(2).

       On  program  startup,  the integer file descriptors associated with the streams stdin, stdout, and stderr are 0, 1, and 2, respectively.  The pre‐\r
       processor symbols STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO are defined with these values in <unistd.h>.   (Applying  freopen(3)  to  one  of
       these streams can change the file descriptor number associated with the stream.)

       Note that mixing use of FILEs and raw file descriptors can produce unexpected results and should generally be avoided.  (For the masochistic among
       you: POSIX.1, section 8.2.3, describes in detail how this interaction is supposed to work.)  A general rule is that file descriptors  are  handled
       in  the  kernel,  while stdio is just a library.  This means for example, that after an exec(3), the child inherits all open file descriptors, but
       all old streams have become inaccessible.

       Since the symbols stdin, stdout, and stderr are specified to be macros, assigning to them is non-portable.  The standard streams can  be  made  to
       refer  to  different  files  with help of the library function freopen(3), specially introduced to make it possible to reassign stdin, stdout, and
       stderr.  The standard streams are closed by a call to exit(3) and by normal program termination.


    fd(0,1,2)就是常說的stdin、stdout、stderr;用戶態(tài)程序運行時默認建立,/dev/pts/0則是運行程序時的終端。
    (純粹的內(nèi)核進程則不同,后面會提到)
    fd在用戶態(tài)下可以通過函數(shù)dup2()進行重定向,而內(nèi)核態(tài)下也有系統(tǒng)調(diào)用sys_dup2(),有興趣的可以試試。

評分

參與人數(shù) 1可用積分 +30 收起 理由
Godbach + 30 多謝LZ分享

查看全部評分

論壇徽章:
0
2 [報告]
發(fā)表于 2010-06-29 19:21 |只看該作者
本帖最后由 zyr-linux 于 2010-06-29 19:47 編輯

以上算是背景介紹,這里是內(nèi)核版,老是在用戶態(tài)繞未免有點不成體統(tǒng)^0^

進入內(nèi)核態(tài),如何獲得文件描述符相關(guān)信息?
1、內(nèi)核函數(shù)fget(),根據(jù)fd號獲得指向文件的struct file;
2、內(nèi)核函數(shù)d_path(),根據(jù)struct file獲取文件名及路徑;
3、默認打開文件最大值(fd最大值):NR_OPEN_DEFAULT。

PS:如果要深究,這些信息存放在struct task_struct中:

struct task_struct
{
......
/* filesystem information */
        struct fs_struct *fs;
/* open file information */
        struct files_struct *files;
......
}


把以上東東寫成一個函數(shù):
  1. #include <linux/fs.h>           /*struct file*/
  2. #include <linux/file.h>         /*fget() fput()*/
  3. #include <linux/fdtable.h>      /*NR_OPEN_DEFAULT*/
  4. #include <linux/dcache.h>       /*d_path()*/

  5. void KernelFd_ShowFd(void)
  6. {
  7.         int i_Loop = 0;
  8.         char ac_Buf[64];
  9.         char * pc_FdName = NULL;
  10.         struct file * pst_File = NULL;

  11.         printk("\nshow Fd:\n");

  12.         //遍歷FD       
  13.         for (i_Loop = 0; i_Loop < NR_OPEN_DEFAULT; i_Loop++)
  14.         {
  15.                 //取出FD對應(yīng)struct file并檢驗
  16.                 pst_File = fget(i_Loop);

  17.                 if (NULL != pst_File)
  18.                 {
  19.                         //取出FD對應(yīng)文件路徑及文件名并檢驗
  20.                         pc_FdName = d_path(&(pst_File->f_path), ac_Buf, sizeof(ac_Buf));

  21.                         if (NULL != pc_FdName)
  22.                         {
  23.                                 printk("\tfd %02d is %s, addr is 0x%p\n", i_Loop, pc_FdName, pst_File);
  24.                         }
  25.                         else
  26.                         {
  27.                                 printk("\tfd %02d name is NULL, addr is 0x%p\n", i_Loop, pst_File);
  28.                         }
  29.                        
  30.                         //fget(),fput()成對
  31.                         fput(pst_File);

  32.                 }

  33.         }

  34.         printk("\n");

  35. }
復(fù)制代碼
進行驗證,編寫內(nèi)核模塊,在init函數(shù)中加入延時,insmod后通過proc查看fd,和KernelFd_ShowFd()結(jié)論一致。

    看看不同情況下fd指向誰:
      1、通過putty登錄,加載模塊,fd(0、1、2)均指向/dev/pts/0;
      2、再開啟一個putty,加載模塊,fd(0、1、2)均指向/dev/pts/1;
      3、在ubuntu桌面進入shell,加載模塊,fd(0、1、2)均指向/dev/tty1;
      4、使用kthread_create建立內(nèi)核態(tài)thread,通過proc查看,fd0指向./,fd1指向../。

    insmod加載模塊時,實際是先啟動了進程:insmod  XXX.ko;結(jié)果和helloworld一致。
    此時fd(0、1、2)為stdin、stdout、stderr,而stdin、stdout、stderr都指向當前終端,可見通過不同的終端加載模塊,fd指向的設(shè)備文件不同。
    而純粹的kernel thread,其fd既沒有三個,也沒有指向任何設(shè)備文件,因為對它而言,沒有stdin、stdout、stderr。

論壇徽章:
0
3 [報告]
發(fā)表于 2010-06-29 19:23 |只看該作者
小把戲之一:

既然fd(0、1、2)均指向當前終端,那么,操作一下?
有了fd,如何操作,很自然的就想到了sys_read(),sys_write();但是很不幸,unbutu10.4中sys_XXX沒有導(dǎo)出。
那么研究一下sys_write():

SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
                size_t, count)
{
        struct file *file;
        ssize_t ret = -EBADF;
        int fput_needed;

        file = fget_light(fd, &fput_needed);
        if (file) {
                loff_t pos = file_pos_read(file);
                ret = vfs_write(file, buf, count, &pos);
                file_pos_write(file, pos);
                fput_light(file, fput_needed);
        }

        return ret;
}


先抱怨一下,系統(tǒng)調(diào)用都用SYSCALL_DEFINE封裝了,查找起來很麻煩。
file_pos_read()和file_pos_write()內(nèi)容很簡單;
fget_light()和fput_light()比較麻煩,不過好在有兩個已經(jīng)導(dǎo)出的內(nèi)核函數(shù)fget()、fput()可以代替。

重寫的sys_write()如下:
  1. //功能實現(xiàn)所需頭文件
  2. #include <linux/fs.h>           /*struct file
  3.                                                                     vfs_write()*/
  4.                                                                     
  5. #include <linux/file.h>         /*fget()
  6.                                                                     fput()*/

  7. #include <linux/uaccess.h>    /*get_fs()
  8.                                                                KERNEL_DS
  9.                                                                set_fs();*/
  10.                                                                

  11. long Kprintf_SysWrite(unsigned int Vui_Fd, char * Vstr_buf, unsigned int Vui_BufLen)
  12. {
  13.         long l_Ret = -EBADF;
  14.         struct file * pst_File = NULL;
  15.         mm_segment_t st_FsStatus;

  16.         //參數(shù)檢查,Vui_Fd在fget()中檢查, Vui_BufLen必為非負
  17.         if (NULL == Vstr_buf)
  18.         {
  19.                 printk(KERN_ALERT "write buffer is empty!\n");
  20.                 return l_Ret;
  21.         }

  22.         //設(shè)置文件系統(tǒng)接受內(nèi)核態(tài)地址
  23.         st_FsStatus = get_fs();
  24.         set_fs(KERNEL_DS);

  25.         pst_File = fget(Vui_Fd);
  26.        
  27.         if (NULL != pst_File)
  28.         {
  29.                 loff_t Tst_Pos = pst_File->f_pos;
  30.                
  31.                 l_Ret = vfs_write(pst_File, Vstr_buf, Vui_BufLen, &Tst_Pos);

  32.                 pst_File->f_pos = Tst_Pos;

  33.                 fput(pst_File);
  34.         }

  35.         //恢復(fù)文件系統(tǒng)原狀態(tài)
  36.         set_fs(st_FsStatus);

  37.         return l_Ret;
  38. }
復(fù)制代碼
這里只實現(xiàn)了sys_write(),其他關(guān)于文件的系統(tǒng)調(diào)用根據(jù)此思路也可以實現(xiàn),有興趣的可以試試。
    調(diào)用它,即可將Vstr_buf中內(nèi)容輸出到終端,如果fd指向的不是終端文件呢?
    比如,一個socket,一個設(shè)備文件?
    或許可以通過在用戶態(tài)打開,在內(nèi)核態(tài)讀、寫、操作,避免用戶態(tài)——內(nèi)核態(tài)切換對性能造成的影響。
    當然,真要去實現(xiàn)還有很多后續(xù)工作。

論壇徽章:
0
4 [報告]
發(fā)表于 2010-06-29 19:26 |只看該作者
本帖最后由 zyr-linux 于 2010-06-29 19:27 編輯

我更感興趣的是下面的東東。

小把戲之二:

更進一步,參考printk進行封裝Kprintf_SysWrite():
  1. //kprintf一次最多打印1024個字符,1024參考printk()中設(shè)定
  2. #define KPRINTF_MAX 1024

  3. char Gac_KprintfBuf[KPRINTF_MAX] = {0, };

  4. int Kprintf(const char * Vstr_Fmt, ...)
  5. {
  6.         int i_Ret;
  7.         va_list st_Args;
  8.        
  9.         //參數(shù)檢查
  10.         if (NULL == Vstr_Fmt)
  11.         {
  12.                 printk(KERN_ALERT "Vstr_Fmt is empty!\n");
  13.                 return -EBADF;
  14.         }

  15.         //清0
  16.         memset(Gac_KprintfBuf, sizeof(Gac_KprintfBuf), 0);

  17.         //組合字符串及其參數(shù)
  18.         va_start(st_Args, Vstr_Fmt);
  19.        
  20.         i_Ret = vsnprintf(Gac_KprintfBuf, KPRINTF_MAX, Vstr_Fmt, st_Args);

  21.         va_end(st_Args);

  22.         //檢查組合后字符串長度
  23.         if (0 < i_Ret)
  24.         {
  25.                 //為正數(shù)才寫入fd0
  26.                 i_Ret = (int)Kprintf_SysWrite(0, Gac_KprintfBuf, (unsigned int)i_Ret);
  27.         }
  28.         else
  29.         {
  30.                 printk("something is wrong with Vstr_Fmt or snprintf\n");
  31.         }
  32.        
  33.         return i_Ret;
  34. }
復(fù)制代碼
這樣,和printf的使用完全一樣,內(nèi)核態(tài)程序在可以在shell上顯示信息,
再進一步,我們可以實現(xiàn)一條非常符合使用習(xí)慣的,內(nèi)核與shell直接交互的通道。

論壇徽章:
0
5 [報告]
發(fā)表于 2010-06-29 19:30 |只看該作者
其他:


將printk中原始代碼加入Kprintf()中,Kprintf就可以帶有printk功能:
  1. int Kprintf_K(const char * Vstr_Fmt, ...)
  2. {
  3.         int i_Ret;
  4.         va_list st_Args;
  5.         va_list st_PrintkArgs;
  6.        
  7.         //參數(shù)檢查
  8.         if (NULL == Vstr_Fmt)
  9.         {
  10.                 printk(KERN_ALERT "Vstr_Fmt is empty!\n");
  11.                 return -EBADF;
  12.         }

  13.         //清0
  14.         memset(Gac_KprintfBuf_K, sizeof(Gac_KprintfBuf_K), 0);

  15.         //組合字符串及其參數(shù)
  16.         va_start(st_Args, Vstr_Fmt);
  17.        
  18.         i_Ret = vsnprintf(Gac_KprintfBuf_K, KPRINTF_MAX, Vstr_Fmt, st_Args);

  19.         va_end(st_Args);

  20.         //檢查組合后字符串長度
  21.         if (0 < i_Ret)
  22.         {
  23.                 //為正數(shù)才寫入fd0
  24.                 i_Ret = (int)Kprintf_SysWrite(0, Gac_KprintfBuf_K, (unsigned int)i_Ret);
  25.         }
  26.         else
  27.         {
  28.                 printk("something is wrong with Vstr_Fmt or snprintf\n");
  29.         }

  30. [color=Red]        //原printk打印
  31.         va_start(st_PrintkArgs, Vstr_Fmt);
  32.        
  33.         vprintk(Vstr_Fmt, st_PrintkArgs);

  34.         va_end(st_PrintkArgs);[/color]

  35.         return i_Ret;
  36. }
復(fù)制代碼
內(nèi)核版曾有帖子提到內(nèi)核態(tài)下如何操作文件,走的是file->f_op->write,按該帖思路實現(xiàn)對各個fd操作代碼如下:
(僅驗證用,沒有寫成與Kprintf_SysWrite一致格式)
  1. void KernelFd_WriteFd(void)
  2. {
  3.         int i_Loop = 0;
  4.         long l_Ret;
  5.         char * str_WriteString0 = "Test write fd in kernel module\n";
  6.         //char * str_WriteString1 = "Test write fd in kernel module";
  7.         char ac_Buf[64];
  8.         char * pc_FdName = NULL;
  9.         struct file * pst_File = NULL;

  10.         printk("\nfile->f_op->write:\n");

  11.         //遍歷FD       
  12.         for (i_Loop = 0; i_Loop < NR_OPEN_DEFAULT; i_Loop++)
  13.         {
  14.                 //取出FD對應(yīng)struct file并檢驗
  15.                 pst_File = fget(i_Loop);               

  16.                 if (NULL != pst_File)
  17.                 {
  18.                         //取出FD對應(yīng)文件路徑及文件名并檢驗
  19.                         pc_FdName = d_path(&(pst_File->f_path), ac_Buf, sizeof(ac_Buf));

  20.                         if (NULL != pc_FdName)
  21.                         {
  22.                                 printk("\tfd %2d is %s, addr is 0x%p\n", i_Loop, pc_FdName, pst_File);
  23.                         }
  24.                         else
  25.                         {
  26.                                 printk("\tfd %2d name is NULL, addr is 0x%p\n", i_Loop, pst_File);
  27.                         }

  28.                         //調(diào)用file->f_op->write進行操作
  29.                         printk("\t\twrite '%s' to %s\n", str_WriteString0, pc_FdName);

  30.                         if ((NULL != pst_File->f_op) && (NULL != pst_File->f_op->write))
  31.                         {
  32.                                 mm_segment_t Tst_FsStatus;
  33.                                 Tst_FsStatus = get_fs();
  34.                                 set_fs(KERNEL_DS);
  35.                                 l_Ret = pst_File->f_op->write(pst_File, str_WriteString0, strlen(str_WriteString0), &(pst_File->f_pos));
  36.                                 set_fs(Tst_FsStatus);
  37.                                 printk(KERN_ALERT "\t\twrite fd %2d return %ld!\n", i_Loop, l_Ret);
  38.                         }


  39.                         //fget(),fput()成對
  40.                         fput(pst_File);

  41.                         printk("\n");
  42.                 }

  43.         }

  44.         printk("\n");

  45. }
復(fù)制代碼

論壇徽章:
0
6 [報告]
發(fā)表于 2010-06-29 19:37 |只看該作者
本帖最后由 zyr-linux 于 2010-06-30 09:49 編輯

最后附上調(diào)用kprintk和kprintf_k的圖:
  1. static __init int Kprintf_init(void)
  2. {
  3.     printk(KERN_ALERT "Hello world!\n");

  4.         Kprintf("Hello everybody, I am kprintf!\n");

  5.         Kprintf("Test: %d, %ld, 0x%x, %c, %s!\n", 1024, 1024UL, 1024, 'c', "string");


  6.         Kprintf_K("Hello everybody, I am Kprintf_K!\n");

  7.         Kprintf_K("Kprintf_K Test: %d, %ld, 0x%x, %c, %s!\n", 1024, 1024UL, 1024, 'c', "string");

  8.     return 0;
  9. }
復(fù)制代碼

論壇徽章:
0
7 [報告]
發(fā)表于 2010-06-29 22:26 |只看該作者
有意思
不錯的思路,收藏了!

論壇徽章:
1
2015年辭舊歲徽章
日期:2015-03-03 16:54:15
8 [報告]
發(fā)表于 2010-06-30 08:57 |只看該作者
好東西,收藏!

論壇徽章:
36
IT運維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
9 [報告]
發(fā)表于 2010-06-30 11:36 |只看該作者
多謝LZ分享。有時間仔細拜讀一下。
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP