insmod[轉]



在Linux下,驅動程序是內核的一部分,運行在內核態下,你可以將驅動靜態的和內核編譯在一起,這樣的缺點是內核會比較大,而且如果驅動出錯,會導致整個系統崩潰;也可以以module的方式編譯,在需要的時候動態的載入。如果你編譯過內核,應該記得在make menuconfig中,選項前面是可以選擇和的,就分別表示"編譯到內核中"和"編譯成模塊"。
.
下面介紹下模塊,一個簡單的“helloworld module”如下所示:

/*  hello-1.c - The simplest kernel module.
 */
#include
  /* Needed by all modules */
#include
  /* Needed for KERN_ALERT */
 
int init_module(void)
{
   printk("<1>Hello world 1.\n");
 
   // A non 0 return means init_module failed; module can't be loaded.
   return 0;
}
 
void cleanup_module(void)
{
  printk(KERN_ALERT "Goodbye world 1.\n");
}

其中,init_module函數是加載模塊時會被調用的,一般作一些初始化的工作;cleanup_module函數是卸載模塊時會被調用的,做一些清理的工作。因為模塊是運行在內核態的,你自然不能使用庫函數,因此要打印信息,需要使用printk函數而不是printf函數。另外,你可以使用任意函數名(只要同內核函數名不沖突)來替換init_module和cleanup_module這兩個函數名,但必須使用module_init(初始化函數名),module_exit(卸載時函數名)這兩個宏來聲明一下,也就是說,下面這個模塊和上面的模塊是等價的:

/*  hello-1.c - The simplest kernel module.
 */
#include
  /* Needed by all modules */
#include
  /* Needed for KERN_ALERT */
 
int helloworld(void)
{
   printk("<1>Hello world 1.\n");
 
   // A non 0 return means init_module failed; module can't be loaded.
   return 0;
}
 
void goodbyeworld(void)
{
  printk(KERN_ALERT "Goodbye world 1.\n");
}
module_init(helloworld);
module_exit(goodbyeworld);

.
編譯和加載一個模塊也很容易:

從上面的圖可以看到,我們通過make編譯生成一個模塊文件".ko"之后,使用"insmod"命令來加載模塊,那么“insmod”具體做了什么呢?
.
下面讓我們來介紹下insmod這個工具:
insmod是linux下加載模塊的工具,路徑一般是/sbin/insmod,當你調用這個工具后,它的工作基本如下:

  • 在用戶空間打開待安裝的module
  • 調用query_module()系統調用詢問無法落實的符號在內核或其他模塊中的地址
  • 鏈接操作,落實模塊中的符號引用
  • 調用create_module()系統調用在內核中創建module數據結構,並申請所需的內核空間
  • 調用init_module()系統調用將鏈接好的module映像裝入內核空間,然后調用模塊中的init_module()函數(注意:這里面的兩個init_module函數不一樣,一個是系統調用,一個是你寫在模塊里面的函數

.
內核導出的符號清單可以由下面的命令來查看:

more /proc/k[all]syms
c0400000 T _text
c0400000 T startup_32
c040007b t default_entry
c04000d0 T startup_32_smp
c0400152 t checkCPUtype
c04001d3 t is486
c04001da t is386
c0400247 t check_x87
c040027a t setup_idt
…
c06adb25 T printk
…

.
從已加載模塊中卸載模塊使用的是“rmmod”,"rmmod"所做工作如下:

  • 調用delete_module()系統調用釋放模塊的module結構,同時釋放模塊所占的內核空間
  • 調用模塊中的cleanup_module()的函數

.

那么,一般情況下,驅動程序會在init_module()和cleanu_module()函數中做些什么呢?

 

  • init_module(): 向內核登記本模塊中一些包含着函數指針的數據結構(file_operations)
  • cleanup_module(): 向內核撤銷本模塊提供的數據結構的登記,使內核在模塊拆卸后不至於再企圖訪問這些數據結構

.

一個字符型驅動的注冊函數如下:

#include
 
#inlcude
 
int register_chrdev(unsigned int major, const char *name,
                struct file_operations *fops);
  • major: 設備的主設備號,若為空則系統動態分配
  • name: 設備名
  • fops: 函數指針結構,各個調用的入口
  • 操作成功,設備名出現在/proc/devices文件

至於怎么創建一個有設備名和設備號的文件,可以通過man mknod獲取信息。
.
file_operantions的結構如下:

Linux-2.6.27.25
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
……
}

.
此外,初始化部分還負責為設備驅動申請系統資源,如:內存,時鍾,中斷,I/O端口等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM