struct file_operations中 ioctl 和 unlocked_ioctl
轉載自 "http://blog.chinaunix.net/uid-20543672-id-3015637.html"
很久都沒有寫驅動程式碼了,對於一些驅動相關的核心變化也沒有怎麼關心。這次重遊《LDD3》獲益良多,其值對於struct file_operations中ioctl的消失也讓我長了不少見識。
當年看《LDD3》的時候已經注意到了書中對ioctl的評價不是很好:“ioctl呼叫的非結構化本質導致眾多核心開發者傾向於放棄它。” ,而在這次閱讀3.0程式碼的時候,這個成員在struct file_operations中早已消失了。這個激起了我學習的興趣,以下是對這個ioctl的學習小結:
1、消失的確切時間
ioctl的消失到底是從哪個版本開始的?網上給出的時間是2.6.36開始。網上就是這麼說,但是自己必須找到程式碼中的證據。於是我通過git搜尋主線核心程式碼,找到的刪除ioctl的那個提交:
- commit b19dd42faf413b4705d4adb38521e82d73fa4249
- Author: Arnd Bergmann
- Date: Sun Jul 4 00:15:10 2010 +0200
- bkl: Remove locked .ioctl file operation
- The last user is gone, so we can safely remove this
- Signed-off-by: Arnd Bergmann
- Cc: John Kacur
- Cc: Al Viro
- Cc: Thomas Gleixner
- Signed-off-by: Frederic Weisbecker
好不容易找到了這個提交,好的,這樣就可以確定消失的時間了:
- git tag --contains b19dd42
- v2.6.36
- v2.6.36-rc1
- v2.6.36-rc2
- v2.6.36-rc3
- v2.6.36-rc4
- v2.6.36-rc5
- v2.6.36-rc6
- v2.6.36-rc7
- v2.6.36-rc8
- ......以下省略ooxx行
可以證明ioctl消失的版本是v2.6.35到v2.6.36-rc1間,於是我匯出了v2.6.35到v2.6.36-rc1的補丁,果真在其中!
git diff v2.6.35..v2.6.36-rc1 >
補丁過大不易上傳,請自行生成。
2、消失的原因
簡單的概括:這次ioctl的消失,並不是要把ioctl清理出去,而是要逐步的清理大核心鎖(BKL)。
這個讓ioctl消失的過渡期長達5年,從2005年開始核心黑客就開始替換ioctl了。具體的原因在lwn.net中有一篇很好的文章:The new way of ioctl()。我將他翻譯了一下:
3、ioctl的替代者
對於原來的ioctl,其實可以叫做locked ioctl。這個其實是相對於他的替代方法來講的。我們來看看2.6.35以前在struct file_operations中有關ioctl的成員:
- /*
- * NOTE:
- * read, write, poll, fsync, readv, writev,
unlocked_ioctl and compat_ioctl
- * can be called without the big kernel lock heldin all filesystems.
- */
- 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*);
- ssize_t (*aio_read)(struct kiocb*,const
struct iovec*, unsigned long, loff_t);
- ssize_t (*aio_write)(struct kiocb*,const
struct iovec*, unsigned long, 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);
- long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
- long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
- int (*mmap)(struct file*,
struct vm_area_struct*);
- int (*open)(struct inode*,
struct file*);
- int (*flush)(struct file*,
fl_owner_t id);
- int (*release)(struct inode*,
struct file*);
- int (*fsync)(struct file*,
struct dentry*,int datasync);
- int (*aio_fsync)(struct kiocb*,int
datasync);
- int (*fasync)(int,
struct file*,int);
- int (*lock)(struct file*,int,
struct file_lock*);
- 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);
- int (*check_flags)(int);
- int (*flock)(struct file*,int,
struct file_lock*);
- ssize_t (*splice_write)(struct pipe_inode_info*,
struct file*, loff_t*, size_t, unsignedint);
- ssize_t (*splice_read)(struct file*, loff_t*,
struct pipe_inode_info*, size_t, unsignedint);
- int (*setlease)(struct file*,
long, struct file_lock**);
- };
這個結構體其實是在過渡期的結構體,unlocked_ioctl就是ioctl的替代者。對於新的驅動,不要再使用ioctl了,而是使用unlocked_ioctl。
4、呼叫ioctl與unlocked_ioctl在核心程式碼上的不同
其實ioctl與unlocked_ioctl所對應的系統呼叫都是ioctl。但是在應用層呼叫ioctl的時候,對於我們實現ioctl或者unlocked_ioctl有什麼不同呢?這裡我們可以追溯一下ioctl系統呼叫程式碼的執行過程,這裡我簡單的寫出這個系統呼叫對於裝置驅動(一般是裝置驅動使用ioctl)的執行順序:(fs/ioctl.c)
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)--->do_vfs_ioctl--->vfs_ioctl
而ioctl與unlocked_ioctl的區別就體現在了這個vfs_ioctl中,我們先來看ioctl被刪除前的函式(Linux-2.6.36之前的核心版本):
- /**
- * vfs_ioctl -call filesystem specific ioctl methods
- * @filp: open fileto invoke ioctl methodon
- * @cmd: ioctl commandto execute
- * @arg: command-specific argumentfor ioctl
- *
- * Invokes filesystem specific
->unlocked_ioctl,if one exists; otherwise
- * invokes filesystem specific
->ioctl method.If neither method exists,
- * returns -ENOTTY.
- *
- * Returns 0 on success,-errnoon
error.
- */
- static long vfs_ioctl(struct file
*filp, unsigned
int cmd,
- unsigned long arg)
- {
- int error=-ENOTTY;
- if (!filp->f_op)
- goto out;
- if (filp->f_op->unlocked_ioctl){
- error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
- if (error==-ENOIOCTLCMD)
- error
= -EINVAL;
- goto out;
- } elseif(filp->f_op->ioctl){
- lock_kernel();
- error = filp->f_op->ioctl(filp->f_path.dentry->d_inode,
- filp, cmd, arg);
- unlock_kernel();
- }
- out:
- return error;
- }
從這個函式中我們可以看出:
- ioctl是受到大核心鎖保護的,而unlocked_ioctl是直接執行的。
- unlocked_ioctl優先順序高於ioctl,如果存在unlocked_ioctl,則執行unlocked_ioctl,否則才執行ioctl。這個優先順序的產生明顯是為了過渡。
而在ioctl被刪除後,vfs_ioctl函式也做了相應的改變(Linux-2.6.36):
- /**
- * vfs_ioctl -call filesystem specific ioctl methods
- * @filp: open fileto invoke ioctl methodon
- * @cmd: ioctl commandto execute
- * @arg: command-specific argumentfor ioctl
- *
- * Invokes filesystem specific
->unlocked_ioctl,if one exists; otherwise
- * returns -ENOTTY.
- *
- * Returns 0 on success,-errnoon
error.
- */
- static long vfs_ioctl(struct file
*filp, unsigned
int cmd,
- unsigned long arg)
- {
- int error=-ENOTTY;
- if (!filp->f_op||!filp->f_op->unlocked_ioctl)
- goto out;
- error = filp->f_op->unlocked_ioctl(filp,
cmd, arg);
- if (error==-ENOIOCTLCMD)
- error
= -EINVAL;
- out:
- return error;
- }
5、在驅動程式設計時的注意事項
- 在註冊檔案操作方法的結構體struct file_operations的時候原先的.ioctl=OOXX;替換為 .unlocked_ioctl=OOXX;