linux-summer-2021
step1.先掛載 vpoll
sudo insmod vpoll.ko
掛載完可用 dmesg
顯示核心訊息
[10056.420070] vpoll: loaded
step2.執行 ./user
檔
step3.使用完卸載 vpoll
sudo rmmod vpoll
注意:若 vpoll 已卸載,則執行./user
無效
此step1-step3步驟呼應
一旦 vpoll
掛載進 Linux 核心後,
將可藉由 ioctl
接受來自使用者層級的命令要求。(因此老師才設計一個測試碼 ./user
==>用來呼叫ioctl
)
vpoll.c
:本次設計的考題
user.c
:測試碼,用來驗證測試 vpoll 模組功能用的
因此我們先分析主要功能 vpoll.c
程式碼原理
vpoll.c
程式碼分析Device charactor 的設計方式參閱
vpoll_devnode()
函式介紹其中
devnode成員來自 include/linux/device/class.h
回傳值變數 vpoll_class,也能註冊一組 callback 函式 char *(*devnode)
vpoll.c
主要功能介紹vpoll.c
主要功能強調在
詳細說明參照 3.3.3.1. file_operations結構體
3.3.3.1. file_operations結構體
file_operation 就是把系統調用 (system call) 和驅動 (Device)程序關聯起來的關鍵數據結構。
這個結構的每一個成員都對應著一個系統調用 (system call)。
讀取 file_operation 中相應的函數指標,接著把控制權轉交給函數指標指向的函數,從而完成了 Linux 設備驅動程序的工作。
根據
我們在 file_operations 只註冊了以下5個函數指標成員
根據 3.3.3.1. file_operations结构体
fcntl()
函數以及 ioctl()
函數。在 kernel 3.0 中已經完全刪除了 struct file_operations 中的 ioctl 函數指標。因此 ioctl()
函數,在 kernel 5.4 已經不存在,此功能已經替換成 unlocked_ioctl()
函數
基礎 Linux Device Driver 驅動程式#9 (IOCTL)
有關 ioctl()
如何變遷替換成 unlocked_ioctl()
功能的演進,參閱
[Linux Kernel慢慢學]Different betweeen ioctl, unlocked_ioctl and compat_ioctl
ioctl是什麼?
ioctl()
是撰寫driver一個很重要的接口,以字元裝置驅動(char device driver)來說,透過這個接口可以讓user來操作driver執行一些行為。在撰寫 driver code 時,我們必須透過
register_chrdev()
來向kernel註冊我們的 driver 。為此我們需要提供該 driver 的 file_operation 相關函數實作來讓 user 可以透過這些接口來操控該 driver。Big Kernel Lock下的舊產物: ioctl
ioctl() 在2.6版本以前是還有這個 function 的,例如你可以在2.5.75的fs.h中看到。但在2.6以後就被替換成
unlocked_ioctl()
了,為什麼呢? 由於ioctl()
是早期仍然在 BKL(Big Kernel Lock) 機制下執行的產物(BKL是甚麼可以再去google,是kernel發展史中蠻有趣的一段歷史),BKL的機制會使得ioctl()
的運作時間較長,可能會造成其他process的延遲。隨著 Kernel 後續的改版,BKL不再是一個需要的機制了,大家開始把被 BKL 保護的 function 移除 BKL。但一下子就把 BKL 完全移除還是會有顧慮,大家應該更加仔細的去審視是否有需要自己加入新的lock來保護自己的程序。所以此時需要一個過渡的機制:
unlocked_ioctl()
如果某個驅動的fops提供了
unlocked_ioctl()
,那麼他將優先調用unlocked_ioctl()
而不是有BKL版本的ioctl()
。
unlocked_ioctl()
不再提供inode參數,但你仍可以透過filp->f_dentry->d_inode
來取得unlocked_ioctl()
不再使用BKL,工程師需要根據自己的需求來決定要不要加入lock的機制所以我們知道了原始
unlocked_ioctl()
的誕生是為了應付一段過渡期,讓大家能夠在這段時間趕快去修改自己的ioctl()
,例如你在2.6.11版本的fs.h就可以看到這兩個接口是同時存在的。
- 而在2.6.36後就正式將ioctl()移除了,大家都必須透過
unlocked_ioctl()
來提供ioctl的接口不過這就只是一段歷史,對於現在的driver開發者也沒什麼影響,就是把自己寫好的
ioctl
接到unlocked_ioctl()
上面去而已。為了相容性而出現的compat_ioctl
在 Michael s. Tsirkin 發布的 patch 提供了
unlocked_ioctl
的同時也提供了另外一個接口:compat_ioctl()
。If this method exists, it will be called (without the BKL) whenever a 32-bit process calls ioctl() on a 64-bit system. It should then do whatever is required to convert the argument to native data types and carry out the request
他出現的目的很簡單,就是相容性: 為了讓32-bit的process可以在64-bit上的system來執行
ioctl()
(沒有BKL版本)。
對應到 成大 The Linux Kernel Module Programming Guide
9 Talking To Device Files
裡面還在介紹過時的 ioctl()
,我得 push request 更新
ioctl()
函數比較 driver
端 ( module.c
驅動模組)和 user space端( user.c
應用程式)
有關 ioctl()
的設定
.unlocked_ioctl
ioctl() 從 user space 如何控制到 driver模組的過程 參閱 ioctl()分析——從用戶空間到設備驅動
一個字符設備驅動通常會實現常規的打開、關閉、讀、寫等功能,但在一些細分的情境下,如果需要擴展新的功能,通常以增設ioctl()命令的方式實現。
用戶空間(user space)的 ioctl()
驅動中的 ioctl()
設計driver模組時(module.c),對應註冊的函數指標成員為
- 在新版內核中,
unlocked_ioctl()
與compat_ioctl()
取代了ioctl()
(此名稱是指在舊版kernel內的 file_operations函數指標成員名稱已經不存在,但是在user space中是用 ioctl()呼叫driver模組中的IO資料)- unlocked_ioctl(): 在無大內核鎖(BKL)的情況下調用
- compat_ioctl(): 為了讓32-bit的process可以在64-bit上的system來執行ioctl()(沒有BKL版本)。
在 Linux 官方文件的 ioctl 介紹
ioctl
In addition to read and write operations, a driver needs the ability to perform certain physical device control tasks.
These operations are accomplished by implementing a ioctl function.
Initially, the ioctl system call used Big Kernel Lock. That's why the call was gradually replaced with its unlocked version called
unlocked_ioctl
. You can read more on LWN: http://lwn.net/Articles/119652/
在 http://lwn.net/Articles/119652/ 連結內的文章
即LWN.net報導 The new way of ioctl()
裡面的介紹內容就是
[Linux Kernel慢慢學]Different betweeen ioctl, unlocked_ioctl and compat_ioctl 中提到 file_operation 內的 unlocked_ioctl()
與 compat_ioctl()
取代了 ioctl()
的緣由
點旁邊的 Big kernel lock 索引
找到 Linux support for ARM big.LITTLE 即老師書上提到的 ARM big.LITTLE
user.c
程式碼分析user.c
程式碼建立 epoll 時是用 epoll_create1(EPOLL_CLOEXEC)
而不是使用
根據 man epoll_create, epoll_create1
epoll_create1()
If flags is 0, then, other than the fact that the obsolete size argument is dropped, epoll_create1() is the same as epoll_create().
The following value can be included in flags to obtain different behavior:EPOLL_CLOEXEC
Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. See the description of the O_CLOEXEC flag in open(2) for reasons why this may be useful.
epollfd: 由 epoll_create1() 函數返回的 epoll 文件描述符
EPOLL_CTL_ADD:要監聽的"/dev/vpoll"
描述符efd註冊到epoll句柄中
efd:指定監聽"/dev/vpoll"
的描述符
&ev: struct epoll_event ev
中的 epoll_event
結構如下
我們是註冊