4.一樣的精靈,不一樣的API(2)
關於內核空間,我只想說,所有的驅動程序都是運行在內核空間的,內核空間雖然很大,但總是有限的。而在這有限的空間中,其最後一個page是專門保留的,也就是說,一般人不可能用到內核空間最後一個page的指針。
換句話說,你在寫設備驅動程序的過程中,涉及的任何一個指針,必然有三種情況:一種是有效指針,一種是NULL(空指針),還有一種是錯誤指針,或者說無效指針。而所謂的錯誤指針就是指其已經到達了最後一個page。比如對於32bit的系統來說,內核空間最高地址0xffffffff,那麼最後一個page就是指的0xfffff000~0xffffffff(假設4KB一個page)。這段地址是被保留的,一般人不得越雷池半步,如果你發現你的一個指針指向這個範圍中的某個地址,那麼恭喜你,你的代碼肯定出錯了。
那麼你是不是很好奇,好端端的內核空間幹嘛要留出最後一個page?這不是明明自己有1000塊錢,非得對自己說只能用900塊。實在不好意思,你說錯了,這裏不僅不是浪費一個page,反而是充分利用資源,把一個東西當兩個東西來用。
看見16行那個"MAX_ERRNO"了嗎?一個宏,定義爲4095,MAX_ERRNO就是最大錯誤號,Linux內核中,出錯有多種可能,因爲有許許多多種錯誤。關於Linux內核中的錯誤,我們看include/asm-generic/errno-base.h文件:
- #define EPERM 1 /* Operation not permitted */
- #define ENOENT 2 /* No such file or directory */
- #define ESRCH 3 /* No such process */
- #define EINTR 4 /* Interrupted system call */
- #define EIO 5 /* I/O error */
- #define ENXIO 6 /* No such device or address */
- #define E2BIG 7 /* Argument list too long */
- #define ENOEXEC 8 /* Exec format error */
- #define EBADF 9 /* Bad file number */
- #define ECHILD 10 /* No child processes */
- #define EAGAIN 11 /* Try again */
- #define ENOMEM 12 /* Out of memory */
- #define EACCES 13 /* Permission denied */
- #define EFAULT 14 /* Bad address */
- #define ENOTBLK 15 /* Block device required */
- #define EBUSY 16 /* Device or resource busy */
- #define EEXIST 17 /* File exists */
- #define EXDEV 18 /* Cross-device link */
- #define ENODEV 19 /* No such device */
- #define ENOTDIR 20 /* Not a directory */
- #define EISDIR 21 /* Is a directory */
- #define EINVAL 22 /* Invalid argument */
- #define ENFILE 23 /* File table overflow */
- #define EMFILE 24 /* Too many open files */
- #define ENOTTY 25 /* Not a typewriter */
- #define ETXTBSY 26 /* Text file busy */
- #define EFBIG 27 /* File too large */
- #define ENOSPC 28 /* No space left on device */
- #define ESPIPE 29 /* Illegal seek */
- #define EROFS 30 /* Read-only file system */
- #define EMLINK 31 /* Too many links */
- #define EPIPE 32 /* Broken pipe */
- #define EDOM 33 /* Math argument out of domain of func */
- #define ERANGE 34 /* Math result not representable */
最常見的幾個是-EBUSY、-EINVAL、-ENODEV、-EPIPE、-EAGAIN、-ENOMEM,我相信只要你使用過Linux就有可能見過這幾個錯誤,因爲它們確實經常出現。
這些是每個體系結構中都有的,另外各個體系結構也都定義了自己的一些錯誤代碼。這些東西當然也都是宏,實際上對應的是一些數字,這些數字就叫做錯誤號。而對於Linux內核來說,不管任何體系結構,錯誤號最多不會超過4095,而4095又正好是比4KB小1,即4096-1。而我們知道一個page可能是4KB,也可能是更多,比如8KB,但至少它也是4KB,所以留出一個page出來就可以讓我們把內核空間的指針來記錄錯誤了。
什麼意思呢?比如我們這裏的IS_ERR(),它就是判斷kthread_run()返回的指針是否有錯,如果指針並不是指向最後一個page,那麼沒有問題,申請成功了,如果指針指向了最後一個page,那麼說明實際上這不是一個有效的指針,這個指針裏保存的實際上是一種錯誤代碼。而通常很常用的方法就是先用IS_ERR()來判斷是否是錯誤,然後如果是,那麼就調用PTR_ERR()來返回這個錯誤代碼。只不過這裏沒有調用PTR_ERR()而已,因爲起決定作用的還是IS_ERR(),而PTR_ERR()只是返回錯誤代碼,也就是提供一個信息給調用者,如果你只需要知道是否出錯,而不在乎因爲什麼而出錯,那你當然不用調用PTR_ERR()了。當然,這裏如果出錯了的話,最終usb_deregister()會被調用,並且usb_hub_init()會返回-1。