kernel module編程(八):讀取proc檔案之seq_file

來源:互聯網
上載者:User

  本文也即《Linux Device Drivers》,LDD3的第四章Debuging Techniques的讀書筆記之三,但我們不限於此內容。

  在上次我們使用了read_proc的方式通過/proc檔案讀取kernel module的資訊。作者給的例子他自己說是ugly。而我們在讀取大量資料時發現,受到使用者buffer大小的限制(page的大小),可能需要讀取多次,不僅需要記錄上次讀取的位置,而且由於每次讀取我們申請了訊號量,讀取完釋放,那麼如果多次讀取的間隔中,如果訊號量被寫所擷取就好出現混亂。linux kernel提供seq_file更好的方式來解決這個問題,除非我們確定讀取的資訊量非常少,能夠在page中返回,我們應使用seq_file的方式而不是read_proc 。

  LDD3中介紹的方式,我覺得是典型的西方人和中國人思維方式的不同。在seq_file的介紹中,LDD3先從每個操作具體將其,然後到如何和proc檔案聯絡,最後到如何建立proc檔案,我喜歡反過來的方式,先建立proc,在一步步細化。老外是日月年,我們是年月日,嘿嘿。seq_file的處理方式開看有點發展,步驟有些多,但是安全,是規範的處理方式。

步驟一:建立proc檔案。

  通過一個struct proc_dir_entry的元素,在/proc中建立檔案,如下:

struct proc_dir_entry * entry = create_proc_entry(“scullseq”,0,NULL)。參數的內容和read_proc,第一個參數表示檔案名稱,第二個參數表示檔案屬性,對於唯讀方式為0,第三個參數表示檔案路徑,NULL表示預設路徑,即/proc。

步驟二:關聯proc的操作。

   需要對檔案進行操作,見過檔案和struct file_operations相關聯,我們注意到這個資料結構也用於模組操作關聯中。具體操作如下:

#ifdef SCULL_SEQ_FILE

/* 步驟二:2、定義proc檔案所關聯的檔案操作資料 */

static struct file_operations

scull_proc_ops

= { 

        .owner  = THIS_MODULE,  

        .open    = scull_proc_open,  //open通常這是我們唯一需要重新定義的函數,需要和特定的seq_file關聯起來。

        .read     = seq_read,               //採用系統的處理方式

        .llseek   = seq_lseek,              //採用系統的處理方式

        .release = seq_release,         //採用系統的處理方式

};

/* 步驟二:4、在前面的步驟二1~3中我們建立了proc檔案,關聯了proc檔案和file_operations,並進一步關聯了seq_file,這裡我們具體定義被關聯的seq_file */

static struct seq_operations

scull_seq_ops = {

        .start          = scull_seq_start,

        .next          = scull_seq_next,

        .stop          = scull_seq_stop,

        .show         = scull_seq_show,

};

static struct proc_dir_entry * entry;
#endif

… …

static int __init scull_init(void)

{

… …

#ifdef SCULL_SEQ_FILE

        /* 步驟一:建立proc檔案*/

        entry = create_proc_entry

("scullseq", 0 ,NULL);

        /* 步驟二:1、將proc檔案和對應的檔案操作關聯起來*/

        if(entry)

                entry -> proc_fops = & scull_proc_ops

;

#endif

}

... ... 

static void __exit scull_exit(void)

{

#ifdef SCULL_SEQ_FILE


/* 我們在模組中建立的proc檔案,都應該模組cleanup模組的時候刪除,以防影響系統,另外,我們應該在刪除模組函數的開始執行這個操作,防止相關聯的資料已經刪除或者登出後再來處理,避免異常出現。

*/

        remove_proc_entry("scullseq",NULL

);

#endif

        if(is_get_dev < 0){

                return ;

        }else{

                int i = 0;

                for(i = 0; i < SCULL_DEV_NUM; i ++){

                        scull_trim(&mydev[i]);

                        cdev_del( & mydev[i].cdev );

                }

                unregister_chrdev_region(dev,SCULL_DEV_NUM);

                WDEBUG(WEI_KERN_NOTICE,"Scull module exit/n");

        }

}

/* 步驟二:3、具體實現proc檔案的open操作,目的與seq_file相關聯。*/

int scull_proc_open(struct inode * inode , struct file * file)

{

        return seq_open(file, & scull_seq_ops

);

}

步驟三:處理seq_file操作過程。

  seq_file操作定義了四個操作,格式如下:

void * start(struct seq_file * s, loff_t * v);

void * next (struct seq_file * s, void * v, loff_t * pos);

void   stop (struct seq_file * s, void * v);

int    show (struct seq_file * s, void * v);

  其中loff_t表示位置,這是由我們自己程式控制的,初始為0,在scull中我們依次讀取scull0-3,因此使用該位移量來表示我們所讀取的裝置的序號。

  我們利用void * v來記錄裝置的入口位置。start根據編譯量,即我們的裝置的序號,返回scullx的入口位置,無論下一操作是next,stop,還是show,這個傳回值會作為參數void *v輸入。next表示下一查詢,和start相似,只是多了void * v的輸入,同樣它的傳回值也作為下一操作的參數void *v輸入。show用於通過/proc檔案輸出。stop表示一次讀取的結束。雖然在seq_file中和read_proc不一樣,不需要考慮每次可以輸出的buff的大小,但是實際讀取不會連續一片很大的資料輸出,在例子後面,我們將討論這些操作的執行。

  輸出方式非常簡單,一般可以使用seq_printf,另外還有seq_putc,seq_puts,seq_escape。例子如下:

#ifdef SCULL_SEQ_FILE

void * scull_seq_start

(struct seq_file * s, loff_t * pos)

{

        printk("==scull_seq_start() enter %p %p %lli/n", s , pos , * pos);

        if( * pos >= SCULL_DEV_NUM)

                return NULL;

        else

                return mydev + * pos;

}

void * scull_seq_next

(struct seq_file * s, void * v, loff_t * pos)

{

       

printk("==scull_seq_next() enter %p %p %p %lli/n", s , v, pos , * pos);

        (* pos ) ++;

        return scull_seq_start(s, pos);

}

void scull_seq_stop

(struct seq_file * s, void * v)

{
       printk("==scull_seq_stop() enter/n");

        return ;

}

int scull_seq_show

(struct seq_file * s, void * v)

{

        struct scull_dev * dev = (struct scull_dev *) v;

        struct scull_qset * qs = NULL;

        int j = 0;

       printk("==scull_seq_show() enter/n");

        if(down_interruptible(&dev->sem))

                return -ERESTARTSYS;

        seq_printf

(s, "/n Device Scull%d: qset %i, q %i sz %li/n", (int) (dev - mydev),dev->qset, dev->quantum, dev->size);
        printk("/n Device Scull%d: qset %i, q %i sz %li/n", (int) (dev - mydev),dev->qset, dev->quantum, dev->size);

        for(qs = dev->data ; qs ; qs=qs->next){

                seq_printf

( s,"  item at %p, qset at %p/n", qs,  qs->data);

                printk("  item at %p, qset at %p/n", qs,  qs->data);

                if(qs->data ){

                        for(; j < dev->qset /* && qs->data[ j ] */ ;j++){

                               seq_printf

(s ,"/t%4i:%8p/n",  j,qs->data[ j ]);
                                printk("/t%4i:%8p/n",  j,qs->data[ j ]);

                        }

                }

        }

        up(&dev->sem);

        return 0;

}

  我們描述一下處理的過程:當我們讀取proc檔案,例如cat /proc/scullseq時,我們假設scull0和scull1都有較多資訊輸入。

  一開始調用start,位移量為0,返回scull0的入口,接著調用show,scull0的入口作為參數輸入,在show中,我們可以遍曆scull0的資料結構,通過seq_printf輸出。完成show後,由於輸出資訊多,進入stop,在例子中stop沒有實際操作,我們只是用來跟蹤處理的流程。

  
再次調用start,位移量步進1,即1,返回scull0的入口,接著調用show,scull1的入口作為參數輸入,在show中,我們可以遍曆scull1的資料結構,通過seq_printf輸出。完成show後,由於輸出資訊多,進入stop。

  再次調用start,位移量步進1,即2,返回scull2的入口,接著調用show,scull2的入口作為參數輸入,在show中,我們可以遍曆scull2的資料結構,通過seq_printf輸出。完成show後,由於輸出資訊非常少,kernel認為可以繼續進行操作,而不需要stop,調用next(),在next參數中輸入的參數loff_t為2,next將其加一,為3,返回scull3的入口。接著調用show,scull3的入口作為參數輸入,在show中,我們可以遍曆scull3的資料結構,通過seq_printf輸出。完成show後,由於輸出資訊非常少,kernel認為可以繼續進行操作,而不需要stop,調用next()。由於已經全部資訊返回,在next中發現沒有資料,返回NULL。系統再次調用start,返回NULL,系統調用stop,結束這次輸出。

  再次調用start,返回NULL,表示已經沒有資料輸出,調用stop,結束所有的輸出。

  值得注意 seq_file 代碼在調用 start 和 stop 之間不睡眠或者進行其他非原子性任務. 你也肯定會看到在調用 start
後馬上有一個 stop 調用. 因此, 對你的 start 方法來說請求訊號量或自旋鎖是安全的. 只要你的其他 seq_file
方法是原子的, 調用的整個序列是原子的.(http://www.deansys.com/doc/ldd3/ch04s03.html)

相關技術文章:

我的與kernel module有關的文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.