www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁(yè) > 芯聞號(hào) > 充電吧
[導(dǎo)讀]linux設(shè)備驅(qū)動(dòng)讀書(shū)筆記 設(shè)備驅(qū)動(dòng)簡(jiǎn)介 機(jī)制:提供什么能力 策略:如何使用這些能力 在編寫(xiě)驅(qū)動(dòng)時(shí), 程序員應(yīng)當(dāng)編寫(xiě)內(nèi)核代碼來(lái)存取硬件, 但是不能強(qiáng)加特別的策略給用戶, 因?yàn)椴煌挠脩粲胁煌男枨?

linux設(shè)備驅(qū)動(dòng)讀書(shū)筆記
設(shè)備驅(qū)動(dòng)簡(jiǎn)介 機(jī)制:提供什么能力
策略:如何使用這些能力
在編寫(xiě)驅(qū)動(dòng)時(shí), 程序員應(yīng)當(dāng)編寫(xiě)內(nèi)核代碼來(lái)存取硬件, 但是不能強(qiáng)加特別的策略給用戶, 因?yàn)椴煌挠脩粲胁煌男枨? 驅(qū)動(dòng)應(yīng)當(dāng)做到使硬件可用, 將所有關(guān)于如何使用硬件的事情留給應(yīng)用程序
編寫(xiě)驅(qū)動(dòng)需要注意的地方:
必須注意并發(fā)/重入的問(wèn)題
內(nèi)核空間和用戶空間不能直接操作,必須通過(guò)特別的函數(shù)(copy_from_user/copy_to_user)來(lái)操作
內(nèi)核線程只有一個(gè)非常小的堆棧; 它可能小到一個(gè)4096 字節(jié)的頁(yè). 驅(qū)動(dòng)模塊的函數(shù)必須與內(nèi)核函數(shù)共享這個(gè)堆棧. 因此, 聲明一個(gè)巨大的自動(dòng)變量不是一個(gè)好主意; 如果需要大的結(jié)構(gòu), 應(yīng)當(dāng)在調(diào)用時(shí)動(dòng)態(tài)分配.
以雙下劃線(__)開(kāi)始的函數(shù)通常是一個(gè)低層的接口組件, 應(yīng)當(dāng)小心使用. 本質(zhì)上講, 雙下劃線告訴程序員:" 如果你調(diào)用這個(gè)函數(shù), 確信你知道你在做什么."
內(nèi)核代碼不能做浮點(diǎn)算術(shù)
每個(gè)進(jìn)程的系統(tǒng)??臻g分配的大小為2個(gè)連續(xù)的物理頁(yè)面(通常來(lái)講是8K),而task_struct占了大約1K(在棧的底部), 所以系統(tǒng)空間非常有限,在中斷/軟中斷/驅(qū)動(dòng)程序中不允許嵌套太深或使用大量局部變量
編寫(xiě)缺少進(jìn)程上下文的函數(shù)需要注意:
不允許存取用戶空間. 因?yàn)闆](méi)有進(jìn)程上下文, 沒(méi)有和任何特定進(jìn)程相關(guān)聯(lián)的到用戶空間的途徑.
current指針在原子態(tài)沒(méi)有意義, 并且不能使用因?yàn)橄嚓P(guān)的代碼沒(méi)有和已被中斷的進(jìn)程的聯(lián)系.
不能進(jìn)行睡眠或者調(diào)度. 原子代碼不能調(diào)用 schedule 或者某種 wait_event, 也不能調(diào)用任何其他可能睡眠的函數(shù). 例如, 調(diào)用 kmalloc(..., GFP_KERNEL) 是違犯規(guī)則的. 旗標(biāo)也必須不能使用因?yàn)樗鼈兛赡芩?


重要的數(shù)據(jù)結(jié)構(gòu)的重要成員
struct task_struct {(得到當(dāng)前進(jìn)程task_struct結(jié)構(gòu)指針的宏為: current) volatile long state: 進(jìn)程狀態(tài). -1 unrunnable; 0 runnable; >0 stopped
mm_segment_t addr_limit: 線程地址空間: 0-0xBFFFFFFF for user-thead; 0-0xFFFFFFFF for kernel-thread
struct mm_struct *mm: 虛存管理與映射相關(guān)信息,是整個(gè)用戶空間的抽象
unsigned long sleep_time:
pid_t pid: 進(jìn)程pid
uid_t uid,euid,suid,fsuid:
gid_t gid,egid,sgid,fsgid:
gid_t groups[NGROUPS]:
kernel_cap_t cap_effective, cap_inheritable, cap_permitted:
int keep_capabilities:1:
struct user_struct *user:
char comm[16]: 命令名稱. 由當(dāng)前進(jìn)程執(zhí)行的程序文件的基本名稱( 截短到 15 個(gè)字符, 如果需要 )
struct tty_struct *tty:
unsigned int locks:
struct rlimit rlim[RLIM_NLIMITS]: 當(dāng)前進(jìn)程各種資源分配的限制, 如current->rlim[RLIMIT_STACK]是對(duì)用戶空間堆棧大小的限制
struct files_struct *files: 打開(kāi)的文件
};
struct file_operations{
struct module *owner;是一個(gè)指向擁有這個(gè)結(jié)構(gòu)的模塊的指針. 這個(gè)成員用來(lái)當(dāng)模塊在被使用時(shí)阻止其被卸載. 一般初始化為: THIS_MODULE
loff_t (*llseek) (struct file *, loff_t, int);用作改變文件中的當(dāng)前讀/寫(xiě)位置, 并且新位置作為(正的)返回值.
ssize_t (*read) (struct file *, char *, size_t, loff_t *);從設(shè)備中獲取數(shù)據(jù). 空指針導(dǎo)致read系統(tǒng)調(diào)用返回-EINVAL("Invalid argument") . 非負(fù)返回值代表了成功讀取的字節(jié)數(shù)
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);發(fā)送數(shù)據(jù)給設(shè)備. 空指針導(dǎo)致write 系統(tǒng)調(diào)用返回-EINVAL. 非負(fù)返回值代表成功寫(xiě)的字節(jié)數(shù).
unsigned int (*poll) (struct file *, struct poll_table_struct *);3 個(gè)系統(tǒng)調(diào)用的后端: poll, epoll, 和 select. 都用作查詢對(duì)一個(gè)或多個(gè)文件描述符的讀或?qū)懯欠駮?huì)阻塞. poll 方法應(yīng)當(dāng)返回一個(gè)位掩碼指示是否非阻塞的讀或?qū)懯强赡艿? 如果一個(gè)驅(qū)動(dòng)的 poll 方法為 NULL, 設(shè)備假定為不阻塞地可讀可寫(xiě).
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);提供了發(fā)出設(shè)備特定命令的方法. 注意:有幾個(gè) ioctl 命令被內(nèi)核識(shí)別而不會(huì)調(diào)用此方法.
int (*mmap) (struct file *, struct vm_area_struct *);請(qǐng)求將設(shè)備內(nèi)存映射到進(jìn)程的地址空間. 如果這個(gè)方法是 NULL,系統(tǒng)調(diào)用返回 -ENODEV.
int (*open) (struct inode *, struct file *);open一個(gè)設(shè)備文件. 如果這個(gè)項(xiàng)是 NULL, 設(shè)備打開(kāi)一直成功
int (*release) (struct inode *, struct file *);在文件結(jié)構(gòu)被釋放時(shí)引用這個(gè)操作. 即在最后一個(gè)打開(kāi)設(shè)備文件的文件描述符關(guān)閉時(shí)調(diào)用(而不是每次close時(shí)都調(diào)用)
int (*fsync) (struct file *, struct dentry *, int datasync);fsync系統(tǒng)調(diào)用的后端, 用戶調(diào)用來(lái)刷新任何掛著的數(shù)據(jù). 如果這個(gè)指針是 NULL, 系統(tǒng)調(diào)用返回 -EINVAL.
int (*fasync) (int, struct file *, int);通知設(shè)備它的 FASYNC 標(biāo)志(異步通知)的改變. 這個(gè)成員可以是NULL 如果驅(qū)動(dòng)不支持異步通知.
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);包含多個(gè)內(nèi)存區(qū)的單個(gè)讀操作; 如果為 NULL, read方法被調(diào)用( 可能多于一次 ).
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);包含多個(gè)內(nèi)存區(qū)的單個(gè)寫(xiě)操作; 如果為 NULL, write方法被調(diào)用( 可能多于一次 ).
}
struct file{
struct dentry *f_dentry;關(guān)聯(lián)到文件的目錄入口( dentry )結(jié)構(gòu). 設(shè)備驅(qū)動(dòng)不需要關(guān)心, 除了作為 filp->f_dentry->d_inode 存取 inode 結(jié)構(gòu).
struct file_operations *f_op;和文件關(guān)聯(lián)的操作. 可改變之, 并在返回后新方法會(huì)起作用. 例如, 關(guān)聯(lián)到主編號(hào)1 (/dev/null, /dev/zero...)的open根據(jù)打開(kāi)的次編號(hào)來(lái)更新filp->f_op
unsigned int f_flags;文件標(biāo)志, 例如 O_RDONLY, O_NONBLOCK, 和 O_SYNC. 驅(qū)動(dòng)應(yīng)當(dāng)檢查 O_NONBLOCK 標(biāo)志來(lái)看是否是請(qǐng)求非阻塞操作
mode_t f_mode;文件模式確定文件是可讀的或者是可寫(xiě)的(或者都是), 通過(guò)位 FMODE_READ 和 FMODE_WRITE. 檢查是有內(nèi)核做的,所以驅(qū)動(dòng)里不需要再次檢查
loff_t f_pos;當(dāng)前讀寫(xiě)位置. 驅(qū)動(dòng)可以讀這個(gè)值, 但是正常地不應(yīng)該改變它; 讀和寫(xiě)應(yīng)當(dāng)使用它們的最后一個(gè)參數(shù)來(lái)更新一個(gè)位置. 一個(gè)例外是在 llseek 方法中, 它的目的就是改變文件位置.
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
void *private_data; 可自由使用或者忽略它.
}
struct inode{
kdev_t i_rdev;對(duì)于代表設(shè)備文件的節(jié)點(diǎn), 這個(gè)成員包含實(shí)際的設(shè)備編號(hào). 需要這兩個(gè)函數(shù)來(lái)操作: unsigned int iminor(struct inode *inode)/unsigned int imajor(struct inode *inode)
struct char_device *i_cdev; 內(nèi)核的內(nèi)部結(jié)構(gòu), 代表字符設(shè)備. 當(dāng)節(jié)點(diǎn)是一個(gè)字符設(shè)備文件時(shí), 這個(gè)成員包含一個(gè)指針, 指向這個(gè)結(jié)構(gòu) }

建立和運(yùn)行模塊
下圖展示了函數(shù)調(diào)用和函數(shù)指針在模塊中如何使用來(lái)增加新功能到一個(gè)運(yùn)行中的內(nèi)核.
編譯和加載
內(nèi)核版本的問(wèn)題
linux/version.h中有下面的宏定義:

UTS_RELEASE

這個(gè)宏定義擴(kuò)展成字符串, 描述了這個(gè)內(nèi)核樹(shù)的版本. 例如, "2.6.10".

LINUX_VERSION_CODE

這個(gè)宏定義擴(kuò)展成內(nèi)核版本的二進(jìn)制形式, 版本號(hào)發(fā)行號(hào)的每個(gè)部分用一個(gè)字節(jié)表示. 例如, 2.6.10 的編碼是 132618 ( 就是, 0x02060a ). [4]有了這個(gè)信息, 你可以(幾乎是)容易地決定你在處理的內(nèi)核版本.

KERNEL_VERSION(major,minor,release)

這個(gè)宏定義用來(lái)建立一個(gè)整型版本編碼, 從組成一個(gè)版本號(hào)的單個(gè)數(shù)字. 例如, KERNEL_VERSION(2.6.10) 擴(kuò)展成 132618. 這個(gè)宏定義非常有用, 當(dāng)你需要比較當(dāng)前版本和一個(gè)已知的檢查點(diǎn).

特殊GNU make變量名
obj-m := module.o #最終模塊名
module-objs := file1.o file2.o #最終模塊用到的obj列表
make命令中的"M="選項(xiàng)使 makefile 在試圖建立模塊目標(biāo)(obj-m 變量中指定的)前, 回到你的模塊源碼目錄
Makefile示例:
# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
加載卸載:
insmod:僅加載指定的模塊)
modprobe:加載指定的模塊及其相關(guān)模塊. 它查看要加載的模塊, 看是否它引用了當(dāng)前內(nèi)核沒(méi)有定義的符號(hào). 如果未定義的symbols, modprobe 在模塊搜索路徑(/etc/modprobe.conf)中尋找并加載其他定義了symbols的模塊
rmmod: 從內(nèi)核中去除指定模塊
lsmod: 打印內(nèi)核中當(dāng)前加載的模塊的列表. 通過(guò)讀取/proc/modules或/sys/module 的 sysfs 虛擬文件系統(tǒng)工作
錯(cuò)誤信息:
unresolved symbols: 加載是找不到symbols. 可能是使用了未定義的symbols; 也可能是需要使用modprobe試一下
-1 Invalid module format:編譯模塊用的內(nèi)核源代碼版本與當(dāng)前運(yùn)行的內(nèi)核的版本不匹配

調(diào)試技術(shù)
內(nèi)核的config配置
kernel hacking菜單
CONFIG_DEBUG_KERNEL:
這個(gè)選項(xiàng)只是使其他調(diào)試選項(xiàng)可用; 它應(yīng)當(dāng)打開(kāi), 但是它自己不激活任何的特性.
CONFIG_DEBUG_SLAB:
這個(gè)重要的選項(xiàng)打開(kāi)了內(nèi)核內(nèi)存分配函數(shù)的幾類檢查. 激活這些檢查, 就可能探測(cè)到一些內(nèi)存覆蓋和遺漏初始化的錯(cuò)誤.被分配的每一個(gè)字節(jié)在遞交給調(diào)用者之前都設(shè)成 0xa5, 隨后在釋放時(shí)被設(shè)成 0x6b.內(nèi)核還會(huì)在每個(gè)分配的內(nèi)存對(duì)象的前后放置特別的守護(hù)值; 如果這些值曾被改動(dòng), 內(nèi)核知道有人已覆蓋了一個(gè)內(nèi)存分配區(qū).
CONFIG_DEBUG_PAGEALLOC:
滿的頁(yè)在釋放時(shí)被從內(nèi)核地址空間去除(full pages are removed from the kernel address space when freed)(?). 這個(gè)選項(xiàng)會(huì)顯著拖慢系統(tǒng), 但是它也能快速指出某些類型的內(nèi)存損壞錯(cuò)誤.
CONFIG_DEBUG_SPINLOCK
激活這個(gè)選項(xiàng), 內(nèi)核捕捉對(duì)未初始化的自旋鎖的操作, 以及各種其他的錯(cuò)誤( 例如 2 次解鎖同一個(gè)鎖 ).
CONFIG_DEBUG_SPINLOCK_SLEEP
這個(gè)選項(xiàng)激活對(duì)持有自旋鎖時(shí)進(jìn)入睡眠的檢查. 實(shí)際上, 如果你調(diào)用一個(gè)可能會(huì)睡眠的函數(shù), 它就發(fā)出警告, 即便這個(gè)有疑問(wèn)的調(diào)用沒(méi)有睡眠.
CONFIG_INIT_DEBUG
用__init (或者 __initdata) 標(biāo)志的項(xiàng)在系統(tǒng)初始化或者模塊加載后都被丟棄. 這個(gè)選項(xiàng)激活了對(duì)代碼的檢查, 這些代碼試圖在初始化完成后存取初始化時(shí)內(nèi)存.
CONFIG_DEBUG_INFO
這個(gè)選項(xiàng)使得內(nèi)核在建立時(shí)包含完整的調(diào)試信息. 如果你想使用 gdb 調(diào)試內(nèi)核, 你將需要這些信息. 如果你打算使用 gdb, 你還要激活 CONFIG_FRAME_POINTER.
CONFIG_MAGIC_SYSRQ
激活"魔術(shù) SysRq"鍵.
CONFIG_DEBUG_STACKOVERFLOW
CONFIG_DEBUG_STACK_USAGE
這些選項(xiàng)能幫助跟蹤內(nèi)核堆棧溢出. 堆棧溢出的確證是一個(gè) oops 輸出, 但是沒(méi)有任何形式的合理的回溯. 第1個(gè)選項(xiàng)給內(nèi)核增加了明確的溢出檢查; 第 2 個(gè)使得內(nèi)核監(jiān)測(cè)堆棧使用并作一些統(tǒng)計(jì), 這些統(tǒng)計(jì)可以用魔術(shù) SysRq 鍵得到.
CONFIG_KALLSYMS
這個(gè)選項(xiàng)(在"Generl setup/Standard features"下)使得內(nèi)核符號(hào)信息建在內(nèi)核中; 缺省是激活的. 符號(hào)選項(xiàng)用在調(diào)試上下文中; 沒(méi)有它, 一個(gè) oops 列表只能以 16 進(jìn)制格式給你一個(gè)內(nèi)核回溯, 這不是很有用.
CONFIG_IKCONFIG
CONFIG_IKCONFIG_PROC
這些選項(xiàng)(在"Generl setup"菜單)使得完整的內(nèi)核配置狀態(tài)被建立到內(nèi)核中, 可以通過(guò) /proc 來(lái)使其可用. 大部分內(nèi)核開(kāi)發(fā)者知道他們使用的哪個(gè)配置, 并不需要這些選項(xiàng)(會(huì)使得內(nèi)核更大). 但是如果你試著調(diào)試由其他人建立的內(nèi)核中的問(wèn)題, 它們可能有用.
Power management/ACPI菜單
CONFIG_ACPI_DEBUG
這個(gè)選項(xiàng)打開(kāi)詳細(xì)的 ACPI (Advanced Configuration and Power Interface) 調(diào)試信息, 如果你懷疑一個(gè)問(wèn)題和 ACPI 相關(guān)可能會(huì)用到 Device drivers菜單
CONFIG_DEBUG_DRIVER
打開(kāi)了驅(qū)動(dòng)核心的調(diào)試信息, 可用以追蹤低層支持代碼的問(wèn)題. Device drivers/SCSI device support菜單
CONFIG_SCSI_CONSTANTS
建立詳細(xì)的 SCSI 錯(cuò)誤消息的信息. 如果你在使用 SCSI 驅(qū)動(dòng), 你可能需要這個(gè)選項(xiàng). Device drivers/Input device support菜單
CONFIG_INPUT_EVBUG
如果你使用一個(gè)輸入設(shè)備的驅(qū)動(dòng), 這個(gè)選項(xiàng)可能會(huì)有用. 然而要小心這個(gè)選項(xiàng)的安全性的隱含意義: 它記錄了你鍵入的任何東西, 包括你的密碼.
Profiling support菜單
CONFIG_PROFILING
剖析通常用在系統(tǒng)性能調(diào)整, 但是在追蹤一些內(nèi)核掛起和相關(guān)問(wèn)題上也有用.
strace 命令
顯示所有的用戶空間程序發(fā)出的系統(tǒng)調(diào)用. 并以符號(hào)形式顯示調(diào)用的參數(shù)和返回值.當(dāng)一個(gè)系統(tǒng)調(diào)用失敗, 錯(cuò)誤的符號(hào)值(例如, ENOMEM)和對(duì)應(yīng)的字串(Out of memory) 都會(huì)顯示.
-t: 來(lái)顯示每個(gè)系統(tǒng)調(diào)用執(zhí)行的時(shí)間
-T: 來(lái)顯示調(diào)用中花費(fèi)的時(shí)間
-e: 來(lái)限制被跟蹤調(diào)用的類型
-o: 來(lái)重定向輸出到一個(gè)文件. 缺省地, strace 打印調(diào)用信息到 stderr.

GDB調(diào)試
調(diào)試內(nèi)核:
gdb /usr/src/linux/vmlinux /proc/kcore

第一個(gè)參數(shù)是非壓縮的 ELF 內(nèi)核可執(zhí)行文件的名子, 不是 zImage 或者 bzImage

第二個(gè)參數(shù)是核心文件的名子.

注意事項(xiàng):
無(wú)法檢查module的相關(guān)內(nèi)容
不能修改內(nèi)核數(shù)據(jù), 不能單步,不能設(shè)置斷點(diǎn)
讀到的是內(nèi)核即時(shí)映象,內(nèi)核仍在運(yùn)行,所以有些數(shù)據(jù)可能會(huì)與即時(shí)值不匹配--刷新映象:core-file /proc/kcore
調(diào)試模塊(內(nèi)核版本2.6.7 以上)

Linux 可加載模塊是 ELF 格式的可執(zhí)行映象;ELF被分成幾個(gè)sections. 其中有 3 個(gè)典型的sections與調(diào)試會(huì)話相關(guān):

.text

這個(gè)節(jié)包含有模塊的可執(zhí)行代碼. 調(diào)試器必須知道在哪里以便能夠給出回溯或者設(shè)置斷點(diǎn).

.bss

在編譯時(shí)不初始化的任何變量在 .bss 中

.data

在編譯時(shí)需要初始化的任何變量在 .data 里.

為了gdb能夠調(diào)試可加載模塊需要通知調(diào)試器一個(gè)給定模塊的各個(gè)sections加載在哪里. 這個(gè)信息在 /sys/module/module_name/sections下. 包含名子為 .text , .bss, .data等文件; 每個(gè)文件的內(nèi)容是那個(gè)section的基地址.

gdb的add-symbol-file命令用來(lái)加載模塊相關(guān)信息

add-symbol-file 模塊名 text所在的基地址 -s .bss bss所在基地址 -s .data data所在基地址

add-symbol-file ../scull.ko 0xd0832000 -s .bss 0xd0837100 -s .data 0xd0836be0


KDB調(diào)試
KDB是來(lái)自oss.sgi.com的一個(gè)非官方補(bǔ)丁. 應(yīng)用KDB時(shí)不應(yīng)該運(yùn)行任何程序, 特別的, 不能打開(kāi)網(wǎng)絡(luò). 一般地以單用戶模式啟動(dòng)系統(tǒng)
進(jìn)入KDB:
Pause(或者 Break) 鍵啟動(dòng)調(diào)試器
一個(gè)內(nèi)核 oops(異常?) 發(fā)生時(shí)
命中一個(gè)斷點(diǎn)時(shí)
命令:
bp function_name
在下一次內(nèi)核進(jìn)入function_name時(shí)停止
bt
打印出調(diào)用回溯中每個(gè)函數(shù)的參數(shù)
mds variable_name
mds address
查看變量/內(nèi)存數(shù)據(jù)
mm address value
將value賦給address所指向的內(nèi)存
......



內(nèi)核中的數(shù)據(jù)類型
不同體系結(jié)構(gòu)下各個(gè)類型的大小
arch Size: char short int long ptr long-long u8 u16 u32 u64
i386 1 2 4 4 4 8 1 2 4 8
alpha 1 2 4 8 8 8 1 2 4 8
armv4l 1 2 4 4 4 8 1 2 4 8
ia64 1 2 4 8 8 8 1 2 4 8
m68k 1 2 4 4 4 8 1 2 4 8
mips 1 2 4 4 4 8 1 2 4 8
ppc 1 2 4 4 4 8 1 2 4 8
sparc 1 2 4 4 4 8 1 2 4 8
sparc64 1 2 4 4 4 8 1 2 4 8
x86_64 1 2 4 8 8 8 1 2 4 8
應(yīng)當(dāng)安排有明確類型大小的數(shù)據(jù)類型, 如u8, u16, ... uint8_t, uint16_t, ...
接口特定的類型, 請(qǐng)參考原文
其他移植性問(wèn)題:
Tick: HZ
頁(yè)大小: PAGE_SIZE
頁(yè)偏移: PAGE_SHIFT
字節(jié)序:
條件編譯
#include
#ifdef __BIG_ENDIAN
......
#endif
#ifdef __LITTLE_ENDIAN
......
#endif
轉(zhuǎn)換
#include
#include u32 cpu_to_le32 (u32);
u32 le32_to_cpu (u32);
cpu_to_le16/le16_to_cpu/cpu_to_le64/....
cpus_to_le16/le16_to_cpus/cpus_to_le64/....
帶's'后綴的是有符號(hào)版
數(shù)據(jù)對(duì)齊:
存取不對(duì)齊的數(shù)據(jù)應(yīng)當(dāng)使用下列宏
#include
get_unaligned(ptr);
put_unaligned(val, ptr);
指針?lè)祷?#20540;:
有時(shí)候內(nèi)核函數(shù)會(huì)返回已編碼的指針值來(lái)指示錯(cuò)誤, 這類返回值是否有效的測(cè)試應(yīng)當(dāng)使用下列宏
void *ERR_PTR(long error); 將錯(cuò)誤碼轉(zhuǎn)換成指針形式
long IS_ERR(const void *ptr); 判斷一個(gè)返回值是否有效
long PTR_ERR(const void *ptr); 提取返回的錯(cuò)誤碼, 在提取前需要判斷返回值是否有效
鏈表:
鼓勵(lì)使用內(nèi)核自帶的struct list_head結(jié)構(gòu)來(lái)構(gòu)造雙向鏈表
#include
struct list_head { struct list_head *next, *prev; };
LIST_HEAD(struct list_head);
編譯時(shí)初始化
INIT_LIST_HEAD(struct list_head*)
運(yùn)行時(shí)初始化
list_add(struct list_head *new, struct list_head *head);
在head后鏈入new. 常用來(lái)構(gòu)造FILO
list_add_tail(struct list_head *new, struct list_head *head);
在head前鏈入new, 常用來(lái)構(gòu)造FIFO
list_del(struct list_head *entry);
把entry從鏈表中脫鏈
list_del_init(struct list_head *entry);
把entry從鏈表中脫鏈并重新初始化entry
list_move(struct list_head *entry, struct list_head *head);
將entry移動(dòng)到head后
list_move_tail(struct list_head *entry, struct list_head *head);
將entry移動(dòng)到head前
list_empty(struct list_head *head);
判斷鏈表是否為空
list_splice(struct list_head *list, struct list_head *head);
從鏈表head后斷開(kāi), 將list鏈表鏈接進(jìn)去

list_entry(struct list_head *ptr, type_of_struct, field_name);
從list_head地址得到包含list_head的結(jié)構(gòu)體的開(kāi)始地址.
類似的宏為container_of(pointer, container_type, container_field); 從結(jié)構(gòu)體成員地址得到結(jié)構(gòu)體指針
list_for_each(struct list_head *cursor, struct list_head *list)
這個(gè)宏創(chuàng)建一個(gè)for循環(huán), 執(zhí)行一次, cursor 指向鏈表中的下個(gè)入口項(xiàng)
list_for_each_prev(struct list_head *cursor, struct list_head *list)
這個(gè)版本反向遍歷鏈表.
list_for_each_safe(struct list_head *cursor, struct list_head *next, struct list_head *list)
如果循環(huán)可能刪除鏈表中的項(xiàng), 使用這個(gè)版本.
list_for_each_entry(type *cursor, struct list_head *list, member)
直接得到包含list_head的結(jié)構(gòu)體的地址
list_for_each_entry_safe(type *cursor, type *next, struct list_head *list, member)
如果循環(huán)可能刪除鏈表中的項(xiàng), 使用這個(gè)版本
舉例:
struct list_head todo_list;
...
void todo_add_entry(struct todo_struct *new)
{
struct list_head *ptr;
struct todo_struct *entry;

list_for_each(ptr, &todo_list)
{
entry = list_entry(ptr, struct todo_struct, list);
if (entry->priority < new->priority) {
list_add_tail(&new->list, ptr);
return;
}
}
list_add_tail(&new->list, &todo_struct)
}


模塊特殊宏/函數(shù)(注意大小寫(xiě))

module_init(initialization_function): 聲明模塊初始化函數(shù)
module_exit(cleanup_function): 聲明模塊注銷函數(shù)

EXPORT_SYMBOL(name): 聲明符號(hào)在模塊外可用

EXPORT_SYMBOL_GPL(name): 聲明符號(hào)僅對(duì)使用 GPL 許可的模塊可用.

MODULE_LICENSE("GPL"): 聲明模塊許可
MODULE_AUTHOR: 聲明誰(shuí)編寫(xiě)了模塊
MODULE_DESCRIPION: 一個(gè)人可讀的關(guān)于模塊做什么的聲明
MODULE_VERSION: 一個(gè)代碼修訂版本號(hào); 看 的注釋以便知道創(chuàng)建版本字串使用的慣例
MODULE_ALIAS: 模塊為人所知的另一個(gè)名子
MODULE_DEVICE_TABLE: 來(lái)告知用戶空間, 模塊支持那些設(shè)備

module_param(name, type, perm): 聲明模塊加載時(shí)允許設(shè)置的參數(shù)(2.6.11之前版本中為MODULE_PARM)
module_param_array(name,type,num,perm): 聲明模塊加載時(shí)允許設(shè)置的數(shù)組參數(shù)
name: 是你的參數(shù)(數(shù)組)的名子
type: 是數(shù)組元素的類型
bool/invbool: 一個(gè)布爾型( true 或者 false)值(相關(guān)的變量應(yīng)當(dāng)是 int 類型). invbool 類型顛倒了值, 所以真值變成 false, 反之亦然.
charp: 一個(gè)字符指針值. 需要為其分配內(nèi)存(charp, NOT char)
int/long/short/uint/ulong/ushort: 基本的變長(zhǎng)整型值. 以 u 開(kāi)頭的是無(wú)符號(hào)值.
num: 一個(gè)整型變量
perm: 通常的權(quán)限值, 在 中定義. S_IRUGO: 可以被所有人讀取, 但是不能改變; S_IRUGO|S_IWUSR: 允許 root 改變參數(shù). 注意, 如果一個(gè)參數(shù)被 sysfs 修改, 模塊看到的參數(shù)值也改變了, 但模塊不會(huì)有任何通知
示例:
在模塊中聲明如下:
static char *whom = "world";
static int howmany = 1;
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IRUGO);
調(diào)用時(shí)如下:
insmod moduel_name howmany=10 whom="Mom"
模塊初始化函數(shù)原型為
static int __init function(void);
大部分注冊(cè)函數(shù)以 register_ 做前綴
__init/__initdata: 給定的函數(shù)/數(shù)據(jù)只是在初始化使用. 模塊加載后丟掉這個(gè)初始化函數(shù), 使它的內(nèi)存可做其他用途.
__devinit/__devinitdata: 內(nèi)核沒(méi)有配置支持 hotplug 設(shè)備時(shí)等同于__init/_initdata.
模塊注銷函數(shù)原型為
static void __exit function(void);
__exit/__exitdata: 如果模塊直接建立在內(nèi)核里, 或者如果內(nèi)核配置成不允許模塊卸載, 標(biāo)識(shí)為_(kāi)_exit 的函數(shù)被簡(jiǎn)單地丟棄

container_of(pointer, container_type, container_field);通過(guò)一個(gè)結(jié)構(gòu)體成員的地址得到結(jié)構(gòu)體的地址
比如:
struct test{int a; int b; int c; inte}test_t;
&test_t == container_of(&(test_t.c), struct test, c)
__setup("test=", test_setup);
這個(gè)宏將test_setup這個(gè)函數(shù)放在特定的section中.
在執(zhí)行init/main.c::checksetup()時(shí)會(huì)去kernel boot commandline中尋找字符串"test=xxx": 如果有找到,就用"xxx"作為參數(shù)調(diào)用test_setup; 否則不運(yùn)行
在insmod中如果參數(shù)里帶有"test=xxx"也會(huì)運(yùn)行
void *kmalloc(size_t size, int flags); 試圖分配 size 字節(jié)的內(nèi)存; 返回值是指向那個(gè)內(nèi)存的指針或者如果分配失敗為NULL. flags 參數(shù)用來(lái)描述內(nèi)存應(yīng)當(dāng)如何分配
申請(qǐng)的空間大小限制: 大概為128K
void kfree(void *ptr);分配的內(nèi)存應(yīng)當(dāng)用 kfree 來(lái)釋放. 傳遞一個(gè) NULL 指針給 kfree 是合法的.




get_free_page申請(qǐng)的page數(shù)限制: 2^MAX_ORDER, 2的MAX_ORDER次方個(gè)page. 通常MAX_ORDER=10, 也就是最多2^10=1024個(gè)page, 4Mbyte


int access_ok(int type, const void *addr, unsigned long size): 驗(yàn)證用戶空間有效性
type: VERIFY_READ/VERIFY_WRITE. 如果需要驗(yàn)證讀寫(xiě)許可, 則只要 VERIFY_WRITE
addr: 一個(gè)用戶空間地址,
size: 需要驗(yàn)證的大小.
返回值: 1 是成功(存取沒(méi)問(wèn)題); 0 是失敗(存取有問(wèn)題). 如果它返回0, 驅(qū)動(dòng)應(yīng)當(dāng)返回 -EFAULT
put_user(datum, ptr) 
__put_user(datum, ptr) 
寫(xiě) datum 到用戶空間. 它們相對(duì)(copy_to_user)快. 傳送的數(shù)據(jù)大小依賴 prt 參數(shù)的類型. 比如: prt 是一個(gè)char指針就傳送一個(gè)字節(jié)
put_user 檢查用戶空間確保能寫(xiě). 在成功時(shí)返回 0, 并且在錯(cuò)誤時(shí)返回 -EFAULT.
__put_user 進(jìn)行更少的檢查(它不調(diào)用 access_ok),
驅(qū)動(dòng)應(yīng)當(dāng)調(diào)用put_user來(lái)節(jié)省幾個(gè)周期; 或者拷貝幾個(gè)項(xiàng)時(shí), 在第一次數(shù)據(jù)傳送之前調(diào)用access_ok一次, 之后使用__put_user
get_user(local, ptr) 
__get_user(local, ptr) 
從用戶空間讀單個(gè)數(shù)據(jù), 獲取的值存儲(chǔ)于本地變量 local;
如果使用上述四個(gè)函數(shù)時(shí), 發(fā)現(xiàn)一個(gè)來(lái)自編譯器的奇怪消息, 例如"coversion to non-scalar type requested". 必須使用 copy_to_user 或者 copy_from_user.

unsigned long copy_to_user(void __user *to,const void *from,unsigned long count); 拷貝一整段數(shù)據(jù)到用戶地址空間. 任何存取用戶空間的函數(shù)必須是可重入的. 此函數(shù)可能導(dǎo)致睡眠
unsigned long copy_from_user(void *to,const void __user *from,unsigned long count); 從用戶地址空間拷貝一整段數(shù)據(jù). 任何存取用戶空間的函數(shù)必須是可重入的. 此函數(shù)可能導(dǎo)致睡眠
這兩個(gè)函數(shù)的作用不僅限于拷貝數(shù)據(jù)到和從用戶空間: 它們還檢查用戶空間指針是否有效. 如果指針無(wú)效, 不進(jìn)行拷貝; 如果在拷貝中遇到一個(gè)無(wú)效地址, 只拷貝有效部分的數(shù)據(jù). 在第二種情況下, 返回值是未拷貝的數(shù)據(jù)數(shù). 驅(qū)動(dòng)應(yīng)當(dāng)查看返回值, 并且如果它不是0, 就返回 -EFAULT 給用戶.
int capable(int capability); 
在進(jìn)行一個(gè)特權(quán)操作之前, 一個(gè)設(shè)備驅(qū)動(dòng)應(yīng)當(dāng)檢查調(diào)用進(jìn)程有合適的能力
capability 取值有以下這些:
CAP_DAC_OVERRIDE
這個(gè)能力來(lái)推翻在文件和目錄上的存取的限制(數(shù)據(jù)存取控制, 或者 DAC).
CAP_NET_ADMIN
進(jìn)行網(wǎng)絡(luò)管理任務(wù)的能力, 包括那些能夠影響網(wǎng)絡(luò)接口的.
CAP_SYS_MODULE
加載或去除內(nèi)核模塊的能力.
CAP_SYS_RAWIO
進(jìn)行 "raw" I/O 操作的能力. 例子包括存取設(shè)備端口或者直接和 USB 設(shè)備通訊.
CAP_SYS_ADMIN
一個(gè)捕獲-全部的能力, 提供對(duì)許多系統(tǒng)管理操作的存取.
CAP_SYS_TTY_CONFIG
進(jìn)行 tty 配置任務(wù)的能力.
CAP_SYS_ADMIN
在任務(wù)缺乏一個(gè)更加特定的能力時(shí), 可以選這個(gè)來(lái)測(cè)試
int printk(const char * fmt, ...);向console(而不是虛擬終端)打印一條消息, 并通過(guò)附加不同的記錄級(jí)別或者優(yōu)先級(jí)在消息上對(duì)消息的嚴(yán)重程度分類.沒(méi)有指定優(yōu)先級(jí)的printk語(yǔ)句缺省是DEFAULT_MESSAGE_LOGLEVEL, 在 kernel/printk.c里指定作為一個(gè)整數(shù). 在2.6.10內(nèi)核中, DEFAULT_MESSAGE_LOGLEVEL是KERN_WARNING, 但這個(gè)值在不同的內(nèi)核中可能不一樣.
按消息的嚴(yán)重程度從高到低為:
KERN_EMERG

用于緊急消息, 常常是那些崩潰前的消息.

KERN_ALERT

需要立刻動(dòng)作的情形.

KERN_CRIT

嚴(yán)重情況, 常常與嚴(yán)重的硬件或者軟件失效有關(guān).

KERN_ERR

用來(lái)報(bào)告錯(cuò)誤情況; 設(shè)備驅(qū)動(dòng)常常使用 KERN_ERR 來(lái)報(bào)告硬件故障.

KERN_WARNING

有問(wèn)題的情況的警告, 這些情況自己不會(huì)引起系統(tǒng)的嚴(yán)重問(wèn)題.

KERN_NOTICE

正常情況, 但是仍然值得注意. 在這個(gè)級(jí)別一些安全相關(guān)的情況會(huì)報(bào)告.

KERN_INFO

信息型消息. 在這個(gè)級(jí)別, 很多驅(qū)動(dòng)在啟動(dòng)時(shí)打印它們發(fā)現(xiàn)的硬件的信息.

KERN_DEBUG

用作調(diào)試消息.

使用舉例:
printk(KERN_INFO "hello, worldn");//注意:消息優(yōu)先級(jí)與正文內(nèi)容之間沒(méi)有逗號(hào)
int printk_ratelimit(void); 在你認(rèn)為打印一個(gè)可能會(huì)常常重復(fù)的消息之前調(diào)用來(lái)避免重復(fù)輸出很多相同的調(diào)試信息. 如果這個(gè)函數(shù)返回非零值, 繼續(xù)打印你的消息, 否則跳過(guò)打印.
使用舉例
if (printk_ratelimit())
printk(KERN_NOTICE "The printer is still on firen");
int print_dev_t(char *buffer, dev_t dev); 
char *format_dev_t(char *buffer, dev_t dev);
從一個(gè)驅(qū)動(dòng)打印消息, 你會(huì)想打印與感興趣的硬件相關(guān)聯(lián)的設(shè)備號(hào).兩個(gè)宏定義都將設(shè)備號(hào)編碼進(jìn)給定的緩沖區(qū); 唯一的區(qū)別是 print_dev_t 返回打印的字符數(shù), 而 format_dev_t 返回緩存區(qū)

void set_current_state(int new_state); 設(shè)置當(dāng)前進(jìn)程的運(yùn)行狀態(tài)
new_state: TASK_INTERRUPTIBLE/TASK_RUNNING/TASK_INTERRUPTIBLE/TASK_UNTINTERRUPTIBLE/...
在新代碼中不鼓勵(lì)使用下面這種方式
current->state = TASK_INTERRUPTIBLE
int in_interrupt(void)
如果處理器當(dāng)前在中斷上下文(包括軟中斷和硬中斷)運(yùn)行就返回非零
int in_atomic(void)
若調(diào)度被禁止(即當(dāng)前狀態(tài)是原子態(tài), 包括硬中斷,軟件中斷以及持有自旋鎖時(shí)), 返回值是非零. 在持有自旋鎖這種情況, current 可能是有效的, 但是禁止存取用戶空間, 因?yàn)樗軐?dǎo)致調(diào)度發(fā)生.
無(wú)論何時(shí)使用 in_interrupt(), 應(yīng)當(dāng)真正考慮是否 in_atomic 是你實(shí)際想要的



主次設(shè)備號(hào):
主編號(hào)標(biāo)識(shí)設(shè)備驅(qū)動(dòng); 次編號(hào)被內(nèi)核用來(lái)決定引用哪個(gè)設(shè)備
dev_t 類型(在 中定義)用來(lái)標(biāo)識(shí)設(shè)備編號(hào) -- 同時(shí)包括主次部分
MAJOR(dev_t dev): 從dev_t中取得主設(shè)備號(hào)
MINOR(dev_t dev): 從dev_t中取得次設(shè)備號(hào)

MKDEV(int major, int minor): 講主次設(shè)備號(hào)轉(zhuǎn)換成dev_t

int register_chrdev_region(dev_t first, unsigned int count, char *name): 獲取一個(gè)或多個(gè)設(shè)備編號(hào)來(lái)使用
first 是你要分配的起始設(shè)備編號(hào). first 的次編號(hào)部分常常是 0
count 是你請(qǐng)求的連續(xù)設(shè)備編號(hào)的總數(shù)
name 是應(yīng)當(dāng)連接到這個(gè)編號(hào)范圍的設(shè)備的名子; 它會(huì)出現(xiàn)在 /proc/devices 和 sysfs 中
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);動(dòng)態(tài)分配一個(gè)主編號(hào)
dev 是一個(gè)只輸出的參數(shù), 它在函數(shù)成功完成時(shí)持有你的分配范圍的第一個(gè)數(shù).
fisetminor 應(yīng)當(dāng)是請(qǐng)求的第一個(gè)要用的次編號(hào); 它常常是 0.
count 是你請(qǐng)求的連續(xù)設(shè)備編號(hào)的總數(shù)
name 是應(yīng)當(dāng)連接到這個(gè)編號(hào)范圍的設(shè)備的名子; 它會(huì)出現(xiàn)在 /proc/devices 和 sysfs 中
void unregister_chrdev_region(dev_t first, unsigned int count); 釋放設(shè)備編號(hào)
first 是你要分配的起始設(shè)備編號(hào). first 的次編號(hào)部分常常是 0
count 是你請(qǐng)求的連續(xù)設(shè)備編號(hào)的總數(shù)
設(shè)備注冊(cè):
struct cdev *cdev_alloc(void);為struct cdev申請(qǐng)內(nèi)存空間
void cdev_init(struct cdev *cdev, struct file_operations *fops);初始化struct cdev結(jié)構(gòu). 其成員owner應(yīng)當(dāng)設(shè)置為 THIS_MODULE
cdev 是需要初始化的struct cdev結(jié)構(gòu)
fops: 是關(guān)聯(lián)到這個(gè)驅(qū)動(dòng)的方法集合(read/write等)
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);將設(shè)備注冊(cè)到內(nèi)核

dev 是struct cdev結(jié)構(gòu)

num 是這個(gè)設(shè)備響應(yīng)的第一個(gè)設(shè)備號(hào)

count 是應(yīng)當(dāng)關(guān)聯(lián)到設(shè)備的設(shè)備號(hào)的數(shù)目. 常常 count 是 1, 但是有多個(gè)設(shè)備號(hào)對(duì)應(yīng)于一個(gè)特定的設(shè)備的情形.

void cdev_del(struct cdev *dev);將設(shè)備注銷
設(shè)備注冊(cè)的老方法:
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
major 是感興趣的主編號(hào)
name 是驅(qū)動(dòng)的名子(出現(xiàn)在 /proc/devices)
fops 是缺省的 file_operations 結(jié)構(gòu).
int unregister_chrdev(unsigned int major, const char *name);

major和name 必須和傳遞給register_chrdev的相同, 否則調(diào)用會(huì)失敗.


設(shè)備節(jié)點(diǎn):
devfs_handle_t devfs_register (devfs_handle_t dir,
            const char *name,
            unsigned int flags,
            unsigned int major, unsigned int minor,
            umode_t mode,
            void *ops, void *info);創(chuàng)建設(shè)備節(jié)點(diǎn)
dir:需要?jiǎng)?chuàng)建的設(shè)備文件所在目錄,默認(rèn)為/dev
name: 需要?jiǎng)?chuàng)建的設(shè)備文件名
flags: 通常取DEVFS_FL_DEFAULT
major: 主設(shè)備號(hào)
minor: 次設(shè)備號(hào)
mode: 此設(shè)備文件的讀寫(xiě)權(quán)限
ops: 此設(shè)備的file_operations結(jié)構(gòu)
info: 
#define DEV_ID ((void*)123456)
#define DEV_NAME "XXXXXXXXXXXXXX"
#define DEV_MAJOR 200
#define DEV_IRQ IRQ_XXXX
#define DEV_IRQ_MODE SA_SHIRQ

...

//regist char device
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
ret = register_chrdev(DEV_MAJOR, DEV_NAME, &fops);
#else
cdev_init(&dev_char, &fops);
dev_char.owner = THIS_MODULE;
dev_char.ops = &fops;
ret = cdev_add(&dev_char, MKDEV(DEV_MAJOR, 0), 1);
#endif
if (ret < 0)
goto __mod_init_err1;
//make devfs
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
devfs_handle = devfs_register(NULL, DEV_NAME, DEVFS_FL_DEFAULT,
DEV_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &fops, NULL);
if (NULL == devfs_handle)
{
ret = -1;
goto __mod_init_err2;
}
#else
dev_class = class_create(THIS_MODULE, DEV_NAME);
if(IS_ERR(dev_class))
{
ret = PTR_ERR(dev_class);
goto __mod_init_err2;
}
class_device_create(dev_class, MKDEV(DEV_MAJOR, 0), NULL, DEV_NAME);
ret = devfs_mk_cdev(MKDEV(DEV_MAJOR, 0), S_IFCHR | S_IRUGO | S_IWUSR, DEV_NAME);
if(ret)
goto __mod_init_err3;
#endif

......

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
__mod_init_err3:
class_device_destroy(dev_class, MKDEV(DEV_MAJOR, 0));
class_destroy(dev_class);
#endif
__mod_init_err2:
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
unregister_chrdev(DEV_MAJOR, DEV_NAME);
#else
cdev_del(&dev_char);
#endif
__mod_init_err1:
free_irq(DEV_IRQ, DEV_ID);

#endif//end of "ifndef INPUT_DEVICE"

__mod_init_err0:
return ret;


file_operations函數(shù):
ssize_t read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) 1. 通常應(yīng)當(dāng)更新 *offp 中的文件位置來(lái)表示在系統(tǒng)調(diào)用成功完成后當(dāng)前的文件位置.
2. 如果"沒(méi)有數(shù)據(jù), 但是可能后來(lái)到達(dá)", 在這種情況下, read 系統(tǒng)調(diào)用應(yīng)當(dāng)阻塞.
3. 返回值
如果等于傳遞給 read 系統(tǒng)調(diào)用的 count 參數(shù), 請(qǐng)求的字節(jié)數(shù)已經(jīng)被傳送
如果是正數(shù), 但是小于 count, 只有部分?jǐn)?shù)據(jù)被傳送.
如果值為 0, 到達(dá)了文件末尾(沒(méi)有讀取數(shù)據(jù)).

如果值為負(fù)值表示有一個(gè)錯(cuò)誤. 這個(gè)值指出了什么錯(cuò)誤, 根據(jù) . 出錯(cuò)的典型返回值包括 -EINTR( 被打斷的系統(tǒng)調(diào)用) 或者 -EFAULT( 壞地址 ).

如果一些數(shù)據(jù)成功傳送接著發(fā)生錯(cuò)誤, 返回值必須是成功傳送的字節(jié)數(shù). 在函數(shù)下一次調(diào)用前錯(cuò)誤不會(huì)報(bào)告. 這要求驅(qū)動(dòng)記住錯(cuò)誤已經(jīng)發(fā)生, 以便可以在以后返回錯(cuò)誤狀態(tài).



ssize_t write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)

1. 通常應(yīng)當(dāng)更新 *offp 中的文件位置來(lái)表示在系統(tǒng)調(diào)用成功完成后當(dāng)前的文件位置.

2. 返回值:

如果值等于 count, 要求的字節(jié)數(shù)已被傳送

如果正值, 但是小于 count, 只有部分?jǐn)?shù)據(jù)被傳送

如果值為 0, 什么沒(méi)有寫(xiě). 這個(gè)結(jié)果不是一個(gè)錯(cuò)誤

一個(gè)負(fù)值表示發(fā)生一個(gè)錯(cuò)誤

如果一些數(shù)據(jù)成功傳送接著發(fā)生錯(cuò)誤, 返回值必須是成功傳送的字節(jié)數(shù). 在函數(shù)下一次調(diào)用前錯(cuò)誤不會(huì)報(bào)告. 這要求驅(qū)動(dòng)記住錯(cuò)誤已經(jīng)發(fā)生, 以便可以在以后返回錯(cuò)誤狀態(tài).

ssize_t (*readv) (struct file *filp, const struct iovec *iov, unsigned long count, loff_t *ppos);
ssize_t (*writev) (struct file *filp, const struct iovec *iov, unsigned long count, loff_t *ppos);
struct iovec
{
void __user *iov_base;
__kernel_size_t iov_len;
};
每個(gè) iovec 描述了一塊要傳送的數(shù)據(jù); 它開(kāi)始于 iov_base (在用戶空間)并且有 iov_len 字節(jié)長(zhǎng). count 參數(shù)告訴有多少 iove結(jié)構(gòu).
若未定義此二函數(shù). 內(nèi)核使用 read 和 write 來(lái)模擬它們,

int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
inode 和 filp 指針是對(duì)應(yīng)應(yīng)用程序傳遞的文件描述符 fd 的值, 和傳遞給 open 方法的相同參數(shù).
cmd 參數(shù)從用戶那里不改變地傳下來(lái),
arg 是可選的參數(shù), 無(wú)論是一個(gè)整數(shù)還是指針(按照慣例應(yīng)該用指針), 均以u(píng)nsigned long的形式傳遞進(jìn)來(lái)
返回值:
-ENIVAL("Invalid argument"): 命令參數(shù)不是一個(gè)有效的POSIX 標(biāo)準(zhǔn)(通常會(huì)返回這個(gè))
-ENOTTY: 一個(gè)不合適的 ioctl 命令. 這個(gè)錯(cuò)誤碼被 C 庫(kù)解釋為"設(shè)備的不適當(dāng)?shù)膇octl"(inappropriate ioctl for device)

ioctl的cmd參數(shù)應(yīng)當(dāng)是系統(tǒng)唯一的, 這是出于阻止向錯(cuò)誤的設(shè)備發(fā)出其可識(shí)別但具體內(nèi)容無(wú)法解析的命令的考慮
cmd參數(shù)由這幾部分組成: type, number, direction, size
type
魔數(shù). 為整個(gè)驅(qū)動(dòng)選擇一個(gè)數(shù)(參考ioctl-number.txt). 這個(gè)成員是 8 位寬(_IOC_TYPEBITS).
number
序(順序)號(hào). 它是 8 位(_IOC_NRBITS)寬.
direction
數(shù)據(jù)傳送的方向(如果這個(gè)特殊的命令涉及數(shù)據(jù)傳送).
數(shù)據(jù)傳送方向是以應(yīng)用程序的觀點(diǎn)來(lái)看待的方向
_IOC_NONE: 沒(méi)有數(shù)據(jù)傳輸
_IOC_READ: 從系統(tǒng)到用戶空間
_IOC_WRITE: 從用戶空間到系統(tǒng)
_IOC_READ|_IOC_WRITE: 數(shù)據(jù)在2個(gè)方向被傳送
size
用戶數(shù)據(jù)的大小. 這個(gè)成員的寬度(_IOC_SIZEBITS)是依賴體系的. 通常是13或者14位.
命令號(hào)相關(guān)操作:
_IO(type,nr): 創(chuàng)建沒(méi)有參數(shù)的命令
_IOR(type, nre, datatype): 創(chuàng)建從驅(qū)動(dòng)中讀數(shù)據(jù)的命令
_IOW(type,nr,datatype): 創(chuàng)建寫(xiě)數(shù)據(jù)的命令
_IOWR(type,nr,datatype): 創(chuàng)建雙向傳送的命令
_IOC_TYPE(cmd):得到magic number
_IOC_NR(cmd):得到順序號(hào)
_IOC_DIR(cmd): 得到傳送方向
_IOC_SIZE(cmd): 得到參數(shù)大小
預(yù)定義命令(會(huì)被內(nèi)核自動(dòng)識(shí)別而不會(huì)調(diào)用驅(qū)動(dòng)中定義的ioctl)分為 3 類:
1. 可對(duì)任何文件發(fā)出的(常規(guī), 設(shè)備, FIFO, 或者 socket). 這類的magic number以'T'開(kāi)頭
2. 只對(duì)常規(guī)文件發(fā)出的那些.
3. 對(duì)文件系統(tǒng)類型特殊的那些.
驅(qū)動(dòng)開(kāi)發(fā)只需注意第一類命令,以及以下這些
FIOCLEX
設(shè)置 close-on-exec 標(biāo)志(File IOctl Close on EXec).
FIONCLEX
清除 close-no-exec 標(biāo)志(File IOctl Not CLose on EXec).
FIOASYNC
設(shè)置或者復(fù)位異步通知. 注意直到 Linux 2.2.4 版本的內(nèi)核不正確地使用這個(gè)命令來(lái)修改 O_SYNC 標(biāo)志. 因?yàn)閮蓚€(gè)動(dòng)作都可通過(guò) fcntl 來(lái)完成, 沒(méi)有人真正使用 FIOASYNC 命令, 它在這里出現(xiàn)只是為了完整性.
FIOQSIZE
返回一個(gè)文件或者目錄的大小; 當(dāng)用作一個(gè)設(shè)備文件, 返回一個(gè) ENOTTY 錯(cuò)誤.
FIONBIO(File IOctl Non-Blocking I/O)
修改在 filp->f_flags 中的 O_NONBLOCK 標(biāo)志. 給這個(gè)系統(tǒng)調(diào)用的第 3 個(gè)參數(shù)用作指示是否這個(gè)標(biāo)志被置位或者清除. 注意常用的改變這個(gè)標(biāo)志的方法是使用 fcntl 系統(tǒng)調(diào)用, 使用 F_SETFL 命令.

unsigned int (*poll) (struct file *filp, poll_table *wait);
filp: 文件描述符指針
wait: 用于poll_wait函數(shù)
返回值: 可能不必阻塞就立刻進(jìn)行的操作
void poll_wait(struct file *, wait_queue_head_t *, poll_table *);
驅(qū)動(dòng)通過(guò)調(diào)用函數(shù)poll_wait增加一個(gè)等待隊(duì)列到poll_table結(jié)構(gòu). 這個(gè)等待隊(duì)列是驅(qū)動(dòng)定義并處理的, 進(jìn)程喚醒方式(只喚醒其中一個(gè)還是喚醒所有等待的進(jìn)程)也由驅(qū)動(dòng)(read/write)來(lái)決定

返回的位掩碼:
POLLIN
設(shè)備可不阻塞地讀
POLLRDNORM
可以讀"正常"數(shù)據(jù). 一個(gè)可讀的設(shè)備返回( POLLIN|POLLRDNORM ).
POLLRDBAND
可從設(shè)備中讀取帶外數(shù)據(jù). 當(dāng)前只用在 Linux 內(nèi)核的一個(gè)地方(DECnet代碼), 并且通常對(duì)設(shè)備驅(qū)動(dòng)不可用.
POLLPRI
可不阻塞地讀取高優(yōu)先級(jí)數(shù)據(jù)(帶外). 這個(gè)位使 select 報(bào)告在文件上遇到一個(gè)異常情況, 因?yàn)?selct 報(bào)告帶外數(shù)據(jù)作為一個(gè)異常情況.
POLLHUP
當(dāng)讀這個(gè)設(shè)備的進(jìn)程見(jiàn)到文件尾, 驅(qū)動(dòng)必須設(shè)置POLLUP(hang-up). 一個(gè)調(diào)用 select 的進(jìn)程被告知設(shè)備是可讀的, 如同selcet功能所規(guī)定的.
POLLERR
一個(gè)錯(cuò)誤情況已在設(shè)備上發(fā)生. 當(dāng)調(diào)用 poll, 設(shè)備被報(bào)告為可讀可寫(xiě), 因?yàn)樽x寫(xiě)都返回一個(gè)錯(cuò)誤碼而不阻塞.
POLLOUT
設(shè)備可被寫(xiě)入而不阻塞.
POLLWRNORM
這個(gè)位和POLLOUT有相同的含義, 并且有時(shí)它確實(shí)是相同的數(shù). 一個(gè)可寫(xiě)的設(shè)備返回( POLLOUT|POLLWRNORM).
POLLWRBAND
同POLLRDBAND, 這個(gè)位意思是帶有零優(yōu)先級(jí)的數(shù)據(jù)可寫(xiě)入設(shè)備. 只有 poll 的數(shù)據(jù)報(bào)實(shí)現(xiàn)使用這個(gè)位, 因?yàn)橐粋€(gè)數(shù)據(jù)報(bào)看傳送帶外數(shù)據(jù).
例子:
static unsigned int scull_p_poll(struct file *filp, poll_table *wait)
{
struct scull_pipe *dev = filp->private_data;
unsigned int mask = 0;
/*
* The buffer is circular; it is considered full
* if "wp" is right behind "rp" and empty if the
* two are equal.
*/
down(&dev->sem);
poll_wait(filp, &dev->inq, wait);
poll_wait(filp, &dev->outq, wait);
if (dev->rp != dev->wp)
mask |= POLLIN | POLLRDNORM; /* readable */
if (spacefree(dev))
mask |= POLLOUT | POLLWRNORM; /* writable */
up(&dev->sem);
return mask;
}
這個(gè)代碼簡(jiǎn)單地增加了 2 個(gè) scullpipe 等待隊(duì)列到 poll_table, 接著設(shè)置正確的掩碼位, 根據(jù)數(shù)據(jù)是否可以讀或?qū)?
如果在進(jìn)程A得到poll/select/epoll通知后, 另外一個(gè)進(jìn)程B將數(shù)據(jù)讀走/將緩沖區(qū)填滿, 這時(shí)候進(jìn)程A進(jìn)來(lái)進(jìn)行讀寫(xiě)操作,會(huì)如何?

int (*fasync) (int fd, struct file *filp, int mode);
異步通知. (常常假定異步能力只對(duì) socket 和 tty 可用)
從用戶的角度看異步通知的設(shè)置過(guò)程:
1. 指定一個(gè)進(jìn)程作為文件的擁有者: fcntl 系統(tǒng)調(diào)用發(fā)出 F_SETOWN 命令, 進(jìn)程ID被保存在 filp->f_owner 給以后使用
2. 在設(shè)備中設(shè)置 FASYNC 標(biāo)志: fcntl 系統(tǒng)調(diào)用發(fā)出 F_SETFL 命令
這樣設(shè)置后設(shè)備有 新數(shù)據(jù)到達(dá)/緩沖有空間 的時(shí)候就會(huì)發(fā)送一個(gè)SIGIO信號(hào)到filp->f_owner 中的進(jìn)程(如果值為負(fù)值則發(fā)給整個(gè)進(jìn)程組).
舉例:
signal(SIGIO, &input_handler); /* dummy sample; sigaction() is better */
fcntl(STDIN_FILENO, F_SETOWN, getpid());
oflags = fcntl(STDIN_FILENO, F_GETFL); //get original setting
fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);
從內(nèi)核的角度看用戶設(shè)置過(guò)程
1. 當(dāng)發(fā)出 F_SETOWN, 一個(gè)值被賦值給 filp->f_owner.
2. 當(dāng)發(fā)出 F_SETFL 來(lái)打開(kāi) FASYNC, 驅(qū)動(dòng)的fasync方法被調(diào)用. 無(wú)論何時(shí)filp->f_flags中的FASYNC的值有改變, 都會(huì)調(diào)用驅(qū)動(dòng)中的fasync方法(這個(gè)標(biāo)志在文件打開(kāi)時(shí)缺省地未設(shè)置).
3. 當(dāng)數(shù)據(jù)到達(dá), 向所有的注冊(cè)異步通知的進(jìn)程發(fā)出一個(gè) SIGIO 信號(hào).
從驅(qū)動(dòng)的角度看內(nèi)核響應(yīng)過(guò)程:
1. 內(nèi)核的第一步與驅(qū)動(dòng)無(wú)關(guān)
2. 內(nèi)核的第二步驅(qū)動(dòng)應(yīng)當(dāng)用下列函數(shù)響應(yīng):
int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa);
當(dāng) FASYNC 標(biāo)志因一個(gè)打開(kāi)文件而改變, 這個(gè)函數(shù)用來(lái)從相關(guān)的進(jìn)程列表中添加或去除入口項(xiàng). 它的所有參數(shù)除了最后一個(gè), 都直接來(lái)自fasync方法.
mode: 0: 去除入口項(xiàng); 其他: 添加入口項(xiàng)
fa: 是由驅(qū)動(dòng)提供的一個(gè)struct fasync_struct結(jié)構(gòu). (*fa)在第一次使用之前應(yīng)該初始化成NULL, 不然可能會(huì)出錯(cuò). 從函數(shù)返回的時(shí)候會(huì)被分配一塊內(nèi)存, 在mode=0時(shí)free掉. 所以添加與去除入口項(xiàng)必須配對(duì)使用
void kill_fasync(struct fasync_struct **fa, int sig, int band);
數(shù)據(jù)到達(dá)時(shí)通知相關(guān)的進(jìn)程.
fa: 與fasync_help里的fa同
sig: 被傳遞的信號(hào)(常常是 SIGIO)
band: 異步狀況. 在網(wǎng)絡(luò)代碼里可用來(lái)發(fā)送"緊急"或者帶外數(shù)據(jù)
POLL_IN: 有新數(shù)據(jù)到達(dá). 等同于 POLLIN|POLLRDNORM.
POLL_OUT: 有空間可供寫(xiě)入
舉例: (注意: 若設(shè)備允許多次打開(kāi), 每個(gè)打開(kāi)的filp需要有獨(dú)立的async_queue)
struct fasync_struct *async_queue = NULL;
static int fasync(int fd, struct file *filp, int mode)
{
return fasync_helper(fd, filp, mode, &async_queue);
}
當(dāng)數(shù)據(jù)到達(dá), 用下面的語(yǔ)句來(lái)通知異步讀者.
if (async_queue)
kill_fasync(&async_queue, SIGIO, POLL_IN);
在release方法中應(yīng)該調(diào)用
/* remove this filp from the asynchronously notified filp's */
fasync(-1, filp, 0);
loff_t (*llseek) (struct file *, loff_t, int); 如果未定義llseek方法, 內(nèi)核缺省通過(guò)修改 filp->f_pos來(lái)實(shí)現(xiàn)移位
如果需要禁止lseek操作, 需要在open中調(diào)用
int nonseekable_open(struct inode *inode; struct file *filp);
并把file_operations::llseek設(shè)為no_llseek(loff_t no_llseek(struct file *file, loff_t offset, int whence))
舉例:
loff_t scull_llseek(struct file *filp, loff_t off, int whence)
{
struct scull_dev *dev = filp->private_data;
loff_t newpos;
switch(whence)
{
case 0: /* SEEK_SET */
newpos = off;
break;
case 1: /* SEEK_CUR */
newpos = filp->f_pos + off;
break;
case 2: /* SEEK_END */
newpos = dev->size + off;
break;
default: /* can't happen */
return -EINVAL;
}
if (newpos < 0)
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}

                
        
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉