這篇文章的目的是為了解釋 Linux 驅動程式課程中,經常被詢問的部份,我把這個地方以紅色來標示:
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_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 (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); };
是的,就是 *owner 這個 field 常被問起。*owner 的用途倒底是什麼?(以下以 kernel 2.4.x 為主做說明)
我用流程的方式來說明。不過在這之前,先請第 128 號系統呼叫 sys_init_module 出場。sys_init_module system call 的主要用途是載入 kernel module,以下是他的原型宣告:
long sys_init_module(const char *name_user, struct module *mod_user);
在這裡我們看到第 2 個參數 *mod_user 就是此行的重點 struct module。另外,sys_init_module 系統呼叫透過 glibc 的 init_module() system call 來叫用。以下是 INIT_MODULE(2):
NAME init_module - initialize a loadable module entry SYNOPSIS #include <linux/module.h> int init_module(const char *name, struct module *image); DESCRIPTION init_module loads the relocated module image into kernel space and runs the module's init function. The module image begins with a module structure and is followed by code and data as appropriate. The module structure is defined as follows:
另外,INIT_MODULE(2) 提到「 This system call is only open to the superuser.」,因此 insmod 只能以 root 身份執行。insmod 的原始碼包含在 modutils 套件裡,insmod 實作呼叫 sys_init_module system call 的程式位於 util/sys_nim.c,函數名稱為 sys_init_module,程式碼內容簡單易懂,節錄如下:
#ifndef CONFIG_USE_SYSCALL extern int init_module(const char *name, const struct module *info); int sys_init_module(const char *name, const struct module *info) { return init_module(name, info); } #else #define __NR_sys_init_module __NR_init_module _syscall2(int, sys_init_module, const char *, name, const struct module *, info) #endif
請了解這裡的 sys_init_module 是一個 modutils 自行實作的函數,用來提供給 insmod 指令呼叫,與 kernel 的 sys_init_module system call 是二個不同的東西。insmod 指令的原始碼位於 modutils 套件裡的 insmod/insmod.c,我們已經了解到,insmod.c 會呼叫 util/sys_nim.c:sys_init_module() 來載入 kernel module。
insmod 的程式碼中,載入 kernel module 的工作主要是由 init_module() 來執行,我們透過 init_module() 的原始碼來說明幾件事件:
static int init_module(const char *m_name, struct obj_file *f, unsigned long m_size, const char *blob_name, unsigned int noload, unsigned int flag_load_map) { struct module *module; struct obj_section *sec; void *image; int ret = 0; tgt_long m_addr; sec = obj_find_section(f, ".this"); module = (struct module *) sec->contents; m_addr = sec->header.sh_addr; module->size_of_struct = sizeof(*module); module->size = m_size; module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0; sec = obj_find_section(f, "__ksymtab"); if (sec && sec->header.sh_size) { module->syms = sec->header.sh_addr; module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p); } if (n_ext_modules_used) { sec = obj_find_section(f, ".kmodtab"); module->deps = sec->header.sh_addr; module->ndeps = n_ext_modules_used; } module->init = obj_symbol_final_value(f, obj_find_symbol(f, "init_module")); module->cleanup = obj_symbol_final_value(f, obj_find_symbol(f, "cleanup_module")); sec = obj_find_section(f, "__ex_table"); if (sec) { module->ex_table_start = sec->header.sh_addr; module->ex_table_end = sec->header.sh_addr + sec->header.sh_size; } sec = obj_find_section(f, ".text.init"); if (sec) { module->runsize = sec->header.sh_addr - m_addr; } sec = obj_find_section(f, ".data.init"); if (sec) { if (!module->runsize || module->runsize > sec->header.sh_addr - m_addr) module->runsize = sec->header.sh_addr - m_addr; } sec = obj_find_section(f, ARCHDATA_SEC_NAME); if (sec && sec->header.sh_size) { module->archdata_start = sec->header.sh_addr; module->archdata_end = module->archdata_start + sec->header.sh_size; } sec = obj_find_section(f, KALLSYMS_SEC_NAME); if (sec && sec->header.sh_size) { module->kallsyms_start = sec->header.sh_addr; module->kallsyms_end = module->kallsyms_start + sec->header.sh_size; } if (!arch_init_module(f, module)) return 0; /* * Whew! All of the initialization is complete. * Collect the final module image and give it to the kernel. */ image = xmalloc(m_size); obj_create_image(f, image); if (flag_load_map) print_load_map(f); if (blob_name) { int fd, l; fd = open(blob_name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (fd < 0) { error("open %s failed %m", blob_name); ret = -1; } else { if ((l = write(fd, image, m_size)) != m_size) { error("write %s failed %m", blob_name); ret = -1; } close(fd); } } if (ret == 0 && !noload) { fflush(stdout); /* Flush any debugging output */ ret = sys_init_module(m_name, (struct module *) image); if (ret) { error("init_module: %m"); lprintf("Hint: insmod errors can be caused by incorrect module parameters, " "including invalid IO or IRQ parameters.\n" " You may find more information in syslog or the output from dmesg"); } } free(image); return ret == 0; }
重要的觀念如下:
1. struct module 是「module descriptor」,kernel 的 module manager 以 module descriptor 來維護 kernel module。我們把 struct module 稱為 module object。
2. 看藍色的部份,運算子的左邊。init_module() 負責「setup module object」。
3. 看紅色的部份,module object 的內容主要是讀取自 kernel module object file 裡的特定 section。
4. 最後是綠色的部份,讀完 object file 裡的特定 section,並 setup 好 module object 後,就呼叫 sys_init_moddule() 來載入 module object 至 kernel-space。
了解主要的觀念與流程後,再來就是重要的小結了!
insmod 指令使用 sys_init_module 系統服務,將 kernel module 載入到 kernel 的 address space。例如,載入 hello.o 的命令為:
# insmod hello.o
要了解 kernel module 的載入過程,必須由 insmod 的原始碼開始討論。 這裡只是一開始,所以沒有對程式碼的細節做太多介紹,當然這是後面才需要做的功課。
透過以上的說明,我們就可以來回答「*owner 的用途倒底是什麼?」的問題了。
*owner 的 data type 是 struct module,即 module object 的資料結構。在整個 kernel module 的載入過程中,module object 是由 insmod 指令所設置,並且是代表 kernel module object file。
所以,*owner 是 kernel module 的觀念,與「Linux 驅動程式」沒有關係。fops 才是Linux 驅動程式的觀念。
最後,為什麼 fops 裡要有 *owner 呢?答案就很明顯了:Linux 驅動以 fops->owner 來表示「我這個驅動程式是屬於(is owned)那一個 kernel module」。
關於更多 *owner,我們可以想到的是,驅動程式的 usage count 是紀錄在 module object 裡,而不是 file operation(思考為什麼),所以當我們對 usage count 做 increment/decrement 時,就需要 module object。
另外,關於寫 Linux 驅動程式時,「要不要管 *owner」的答案也是很清楚了,基本上,我們可以先不用理會這個 field,但是如果要給 *owner 欄位值的話,根據 Linux kernel 的說明,我們只要這麼寫即可:
static struct file_operations xxx_ops = { owner: THIS_MODULE, ... };
THIS_MODULE 的定義是(include/linux/module.h):
#define THIS_MODULE (&__this_module)
__this_module 是一個外部符號,用來表示「這個驅動程式所屬的 module object」。如果是分層的驅動程式,那麼這個 field 就要填。
課堂上我們再以 busmouse.c / logibusmouse.c 來說明這個部份。
Also See |
|
Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue
您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw