Linux裝置驅動--系統呼叫
1 開發環境
Host:Ubuntu14.04(64bit)
Target: smdk2410
Kernel: linux-2.6.39.4
2 系統呼叫表
所有系統呼叫都定義在系統呼叫表中,當系統呼叫中斷髮生時,系統就根據系統呼叫號在該表中查詢需要執行的系統呼叫函式。這裡先說明系統呼叫表是如何定義的:
(1)sys_call_table
.type sys_call_table, #object ENTRY(sys_call_table) #include "calls.S" #undef ABI #undef OBSOLETE /* arch/arm/kernel/entry-common.S */
(2)calls.S
特定平臺的calls.S中定義了該平臺的系統呼叫表,arm平臺的系統呼叫表如下所示(部分):
/* 0 */ CALL(sys_restart_syscall) CALL(sys_exit) CALL(sys_fork_wrapper) CALL(sys_read) CALL(sys_write) /* 5 */ CALL(sys_open) CALL(sys_close) CALL(sys_ni_syscall) /* was sys_waitpid */ CALL(sys_creat) CALL(sys_link) /* 10 */ CALL(sys_unlink) CALL(sys_execve_wrapper) CALL(sys_chdir) CALL(OBSOLETE(sys_time)) /* used by libc4 */ /* 原始檔:arch/arm/kernel/calls.S */
(3)CALL()
上述巨集CALL()定義如下:
#define CALL(x) .long x
/* 原始檔:arch/arm/kernel/entry-common.S */
因此,CALL(sys_exit)可展開為:
.long sys_exit
其它的類似。3 系統呼叫入口
系統呼叫是從什麼地方開始的呢?答案是sys_syscall標號處,每當出發系統呼叫中斷時,就自動跳轉到該標號處繼續執行:
/*============================================================================ * Special system call wrappers */ @ r0 = syscall number @ r8 = syscall table sys_syscall: bic scno, r0, #__NR_OABI_SYSCALL_BASE cmp scno, #__NR_syscall - __NR_SYSCALL_BASE cmpne scno, #NR_syscalls @ check range stmloia sp, {r5, r6} @ shuffle args movlo r0, r1 movlo r1, r2 movlo r2, r3 movlo r3, r4 ldrlo pc, [tbl, scno, lsl #2] b sys_ni_syscall ENDPROC(sys_syscall) /* 原始檔:arch/arm/kernel/entry-common.S */
由上原始碼可見,系統呼叫入口為sys_syscall標號,r0暫存器儲存系統呼叫號,r8暫存器儲存系統呼叫表,程式根據r0和r8確定系統呼叫函式(例如sys_read()),然後跳轉到該系統呼叫函式繼續執行。
4 系統呼叫號
上述系統呼叫表是使用匯編定義的,為了便於使用,使用C語言定義了系統呼叫號。
特定平臺的unstd.h中定義了該平臺的系統呼叫號,arm平臺的系統呼叫號如下所示(部分):
/*
* This file contains the system call numbers.
*/
#define __NR_restart_syscall (__NR_SYSCALL_BASE+ 0)
#define __NR_exit (__NR_SYSCALL_BASE+ 1)
#define __NR_fork (__NR_SYSCALL_BASE+ 2)
#define __NR_read (__NR_SYSCALL_BASE+ 3)
#define __NR_write (__NR_SYSCALL_BASE+ 4)
#define __NR_open (__NR_SYSCALL_BASE+ 5)
#define __NR_close (__NR_SYSCALL_BASE+ 6)
/* 7 was sys_waitpid */
#define __NR_creat (__NR_SYSCALL_BASE+ 8)
#define __NR_link (__NR_SYSCALL_BASE+ 9)
#define __NR_unlink (__NR_SYSCALL_BASE+ 10)
#define __NR_execve (__NR_SYSCALL_BASE+ 11)
#define __NR_chdir (__NR_SYSCALL_BASE+ 12)
/* 標頭檔案:arch/arm/include/asm/unistd.h */
注:不同平臺的系統呼叫號不一定相同,但是必須和系統呼叫表對應。5 系統呼叫函式宣告
在syscalls.h標頭檔案中,聲明瞭所有系統呼叫(平臺無關):
asmlinkage long sys_time(time_t __user *tloc);
asmlinkage long sys_stime(time_t __user *tptr);
asmlinkage long sys_gettimeofday(struct timeval __user *tv,
struct timezone __user *tz);
asmlinkage long sys_settimeofday(struct timeval __user *tv,
struct timezone __user *tz);
asmlinkage long sys_adjtimex(struct timex __user *txc_p);
asmlinkage long sys_times(struct tms __user *tbuf);
/* 標頭檔案:include/linux/syscalls.h */
6 系統呼叫函式實現
上述標頭檔案只是聲明瞭系統呼叫,這裡以sys_read()為例重點分析系統呼叫函式的實現。
試圖通過“grep -rnw sys_read”來搜尋sys_read()系統呼叫的實現時,卻找不到!
通過分析原始碼發現,系統呼叫都通過類似於SYSCALL_DEFINE3()的巨集(數字3表示系統呼叫引數個數為3)來定義,例如:
(1)sys_read()
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
struct file *file;
ssize_t ret = -EBADF;
int fput_needed;
file = fget_light(fd, &fput_needed);
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_read(file, buf, count, &pos);
file_pos_write(file, pos);
fput_light(file, fput_needed);
}
return ret;
}
/* 原始檔:fs/read_write.c */
展開上述巨集SYSCALL_DEFINE3()得:
asmlinkage long sys_read(unsigned int, fd, char __user *, buf, size_t, count)
{
struct file *file;
ssize_t ret = -EBADF;
int fput_needed;
file = fget_light(fd, &fput_needed);
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_read(file, buf, count, &pos); /* 呼叫vfs_read()! */
file_pos_write(file, pos);
fput_light(file, fput_needed);
}
return ret;
}
/* 原始檔:fs/read_write.c */
其它巨集如下所示:
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
/* 標頭檔案:include/linux/syscalls.h */
(2)vfs_read()
通過分析上述sys_read()函式發現,它呼叫的一個關鍵函式是vfs()_read():
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
ssize_t ret;
if (!(file->f_mode & FMODE_READ))
return -EBADF;
if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
return -EINVAL;
if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
return -EFAULT;
ret = rw_verify_area(READ, file, pos, count);
if (ret >= 0) {
count = ret;
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, pos); /* 呼叫file->f_op->read()!*/
else
ret = do_sync_read(file, buf, count, pos);
if (ret > 0) {
fsnotify_access(file);
add_rchar(current, ret);
}
inc_syscr(current);
}
return ret;
}
EXPORT_SYMBOL(vfs_read);
/* 原始檔:fs/read_write.c */
分析上述vfs_read()可知,它最終呼叫了f_op->read()。在編寫裝置驅動程式時,很重要的一步就是實現f_op->read(),這裡就是呼叫該f_op->read()的地方!參考資料