我們在操作一個調(diào)和時都 是通過open系統(tǒng)調(diào)用先去打開這個設(shè)備,不管是設(shè)備還是文件,我們要訪問它都要稱通過open函數(shù)來先打開, 這樣才能調(diào)用其它的函數(shù)如read、write來操作它,即通知內(nèi)核新建一個代表該文件的結(jié)構(gòu),并且返回該文件的描述符(一個整數(shù)),該描述符在進(jìn)程內(nèi)唯一。
在Linux系統(tǒng)進(jìn)程中,分為內(nèi)核空間和用戶空間,當(dāng)一個任務(wù)(進(jìn)程)執(zhí)行系統(tǒng)調(diào)用而陷入內(nèi)核代碼中執(zhí)行時,我們就稱進(jìn)程處于內(nèi)核運(yùn)行態(tài)(內(nèi)核態(tài))。在內(nèi)核態(tài)下,CPU可執(zhí)行任何指令。當(dāng)進(jìn)程在執(zhí)行用戶自己的代碼時,則稱其處于用戶運(yùn)行態(tài)(用戶態(tài))。用戶態(tài)不能訪問內(nèi)核空間,包括代碼和數(shù)據(jù)。所有進(jìn)程的內(nèi)核空間(3G-4G)都是共享的。當(dāng)我們在用戶空間調(diào)用open之后,會產(chǎn)生一個軟中斷,然后通過系統(tǒng)調(diào)用進(jìn)入內(nèi)核空間。通過系統(tǒng)調(diào)用號,我們就可以跳轉(zhuǎn)到該中斷例程的入口地址。
這里分析一下open的調(diào)用流程.
open()的格式如下:
int open(const char * pathname,int oflag, mode_t mode )
pathname:代表需要打開的文件的文件名;
oflag:表示打開的標(biāo)識,具體的內(nèi)核支持如下標(biāo)記位(includeasm-genericfcntl.h):
[cpp]?view plain?copy
#define?O_ACCMODE???00000003??
#define?O_RDONLY????00000000??//只讀打開??
#define?O_WRONLY????00000001??//只寫打開??
#define?O_RDWR??????00000002??//讀寫打開??
#ifndef?O_CREAT??
#define?O_CREAT?????00000100????//文件不存在則創(chuàng)建,需要mode_t??
#endif??
#ifndef?O_EXCL??
#define?O_EXCL??????00000200????//如果同時指定了O_CREAT,而文件已經(jīng)存在,則出錯???
#endif??
#ifndef?O_NOCTTY??
#define?O_NOCTTY????00000400????//如果pathname代表終端設(shè)備,則不將此設(shè)備分配作為此進(jìn)程的控制終端??
#endif??
#ifndef?O_TRUNC??
#define?O_TRUNC?????00001000????//如果此文件存在,而且為只讀或只寫成功打開,則將其長度截短為0???
#endif??
#ifndef?O_APPEND??
#define?O_APPEND????00002000????//每次寫時都加到文件的尾端??
#endif??
#ifndef?O_NONBLOCK??
#define?O_NONBLOCK??00004000?????//如果pathname指的是一個FIFO、一個塊特殊文件或一個字符特殊文件,則此選擇項(xiàng)為此文件的本次打開操作和后續(xù)的I?/?O操作設(shè)置非阻塞??
#endif??
#ifndef?O_SYNC??
#define?O_SYNC??????00010000????//使每次write都等到物理I/O操作完成??
#endif??
#ifndef?FASYNC??
#define?FASYNC??????00020000????//兼容BSD的fcntl同步操作??
#endif??
#ifndef?O_DIRECT??
#define?O_DIRECT????00040000????//直接磁盤操作標(biāo)識,每次讀寫都不使用內(nèi)核提供的緩存,直接讀寫磁盤設(shè)備??
#endif??
#ifndef?O_LARGEFILE??
#define?O_LARGEFILE?00100000????//?大文件標(biāo)識??
#endif??
#ifndef?O_DIRECTORY??
#define?O_DIRECTORY?00200000????//必須是目錄??
#endif??
#ifndef?O_NOFOLLOW??
#define?O_NOFOLLOW??00400000????//不獲取連接文件??
#endif??
#ifndef?O_NOATIME??
#define?O_NOATIME???01000000??
#endif??
#ifndef?O_CLOEXEC??
#define?O_CLOEXEC???02000000????/*?set?close_on_exec?*/??
#endif??
#ifndef?O_NDELAY??
#define?O_NDELAY????O_NONBLOCK??
#endif??
當(dāng)新創(chuàng)建一個文件時,需要指定mode參數(shù),以下說明的格式如宏定義名稱<實(shí)際常數(shù)值>:
?描述如下(includelinuxstat.h):
[cpp]?view plain?copy
#define?S_IRWXU?00700???//文件擁有者有讀寫執(zhí)行權(quán)限??
#define?S_IRUSR?00400???//文件擁有者僅有讀權(quán)限??????
#define?S_IWUSR?00200???//文件擁有者僅有寫權(quán)限??
#define?S_IXUSR?00100???//文件擁有者僅有執(zhí)行權(quán)限??
#define?S_IRWXG?00070???//組用戶有讀寫執(zhí)行權(quán)限??
#define?S_IRGRP?00040???//組用戶僅有讀權(quán)限??
#define?S_IWGRP?00020???//組用戶僅有寫權(quán)限??
#define?S_IXGRP?00010???//組用戶僅有執(zhí)行權(quán)限??
#define?S_IRWXO?00007???//其他用戶有讀寫執(zhí)行權(quán)限??
#define?S_IROTH?00004???//其他用戶僅有讀權(quán)限??
#define?S_IWOTH?00002???//其他用戶僅有寫權(quán)限??
#define?S_IXOTH?00001???//其他用戶僅有執(zhí)行權(quán)限??
系統(tǒng)調(diào)用號定義在arch/x86/include/asm/unistd_32.h中:
[cpp]?view plain?copy
#define?__NR_restart_syscall??????0??
#define?__NR_exit?????????1??
#define?__NR_fork?????????2??
#define?__NR_read?????????3??
#define?__NR_write????????4??
#define?__NR_open?????????5??
#define?__NR_close????????6??
#define?__NR_waitpid??????????7??
#define?__NR_creat????????8??
#define?__NR_link?????????9??
#define?__NR_unlink??????10??
#define?__NR_execve??????11??
#define?__NR_chdir???????12??
#define?__NR_time????????13??
#define?__NR_mknod???????14??
#define?__NR_chmod???????15??
當(dāng)open系統(tǒng)調(diào)用產(chǎn)生時,就會進(jìn)入下面這個函數(shù)():
[cpp]?view plain?copy
SYSCALL_DEFINE3(open,?const?char?__user?*,?filename,?int,?flags,?int,?mode)??
{??
long?ret;??
/*檢查是否應(yīng)該不考慮用戶層傳遞的標(biāo)志、總是強(qiáng)行設(shè)置??
O_LARGEFILE標(biāo)志。如果底層處理器的字長不是32位,就是這種??
情況*/???
if?(force_o_largefile())??
flags?|=?O_LARGEFILE;??
/*實(shí)際工作*/???
ret?=?do_sys_open(AT_FDCWD,?filename,?flags,?mode);??
/*?avoid?REGPARM?breakage?on?x86:?*/??
asmlinkage_protect(3,?ret,?filename,?flags,?mode);??
return?ret;??
}??
我們看下SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode) 展開是怎么樣的
首先看下宏SYSCALL_DEFINE3
[cpp]?view plain?copy
#define?SYSCALL_DEFINE3(name,?...)?SYSCALL_DEFINEx(3,?_##name,?__VA_ARGS__)??
再看下SYSCALL_DEFINEx
[cpp]?view plain?copy
#define?SYSCALL_DEFINEx(x,?sname,?...)????????????????
__SYSCALL_DEFINEx(x,?sname,?__VA_ARGS__)??
再看下__SYSCALL_DEFINEx ??
[cpp]?view plain?copy
#define?__SYSCALL_DEFINEx(x,?name,?...)???????????????????
asmlinkage?long?sys##name(__SC_DECL##x(__VA_ARGS__))??
這里對對應(yīng)__SC_DECL3
[cpp]?view plain?copy
#define?__SC_DECL1(t1,?a1)??t1?a1??
#define?__SC_DECL2(t2,?a2,?...)?t2?a2,?__SC_DECL1(__VA_ARGS__)??
#define?__SC_DECL3(t3,?a3,?...)?t3?a3,?__SC_DECL2(__VA_ARGS__)??
這們一步步展開SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)代替進(jìn)去,可以得到?
[cpp]?view plain?copy
SYSCALL_DEFINE3(open,?const?char?__user?*,?filename,?int,?flags,?int,?mode)??
=?SYSCALL_DEFINEx(3,?_##name,?__VA_ARGS__)???
=asmlinkage?long?sys_open(__SC_DECL3(__VA_ARGS__))??
=asmlinkage?long?sys_open(const?char?__user*?filename,?int?flags,?int?mode)??
這個才是真正的函數(shù)原型
在sys_open里面繼續(xù)調(diào)用do_sys_open完成 open操作
[cpp]?view plain?copy
long?do_sys_open(int?dfd,?const?char?__user?*filename,?int?flags,?int?mode)??
{??
/*從進(jìn)程地址空間讀取該文件的路徑名*/????
char?*tmp?=?getname(filename);??
int?fd?=?PTR_ERR(tmp);??
if?(!IS_ERR(tmp))?{??
/*在內(nèi)核中,每個打開的文件由一個文件描述符表示??
該描述符在特定于進(jìn)程的數(shù)組中充當(dāng)位置索引(數(shù)組是??
task_struct->files->fd_arry),該數(shù)組的元素包含了file結(jié)構(gòu),其中??
包括每個打開文件的所有必要信息。因此,調(diào)用下面??
函數(shù)查找一個未使用的文件描述符,返回的是上面??
說的數(shù)組的下標(biāo)*/????
fd?=?get_unused_fd_flags(flags);??
if?(fd?>=?0)?{??
/*fd獲取成功則開始打開文件,此函數(shù)是主要完成打開功能的函數(shù)*/??
//如果分配fd成功,則創(chuàng)建一個file對象??
struct?file?*f?=?do_filp_open(dfd,?tmp,?flags,?mode,?0);??
if?(IS_ERR(f))?{??
put_unused_fd(fd);??
fd?=?PTR_ERR(f);??
}?else?{??
/*文件如果打開成功,調(diào)用fsnotify_open()函數(shù),根據(jù)inode所指定的信息進(jìn)行打開?
函數(shù)(參數(shù)為f)將該文件加入到文件監(jiān)控的系統(tǒng)中。該系統(tǒng)是用來監(jiān)控文件被打開,創(chuàng)建,?
讀寫,關(guān)閉,修改等操作的*/???
fsnotify_open(f->f_path.dentry);??
/*將文件指針安裝在fd數(shù)組中?
將struct?file?*f加入到fd索引位置處的數(shù)組中。如果后續(xù)過程中,有對該文件描述符的?
操作的話,就會通過查找該數(shù)組得到對應(yīng)的文件結(jié)構(gòu),而后在進(jìn)行相關(guān)操作。*/??
fd_install(fd,?f);??
}??
}??
putname(tmp);??
}??
return?fd;??
}??
該函數(shù)主要分為如下幾個步驟來完成打開文件的操作:
1.將文件名參數(shù)從用戶態(tài)拷貝至內(nèi)核,調(diào)用函數(shù)get_name();
2.從進(jìn)程的文件表中找到一個空閑的文件表指針,調(diào)用了函數(shù)get_unused_fd_flgas();
3.完成真正的打開操作,調(diào)用函數(shù)do_filp_open();
4.將打開的文件添加到進(jìn)程的文件表數(shù)組中,調(diào)用函數(shù)fd_install();
getname函數(shù)主要的任務(wù)是將文件名filename從用戶態(tài)拷貝至內(nèi)核態(tài)
[cpp]?view plain?copy
char?*?getname(const?char?__user?*?filename)??
{??
char?*tmp,?*result;??
result?=?ERR_PTR(-ENOMEM);??
tmp?=?__getname();?//從內(nèi)核緩存中分配空間;????
if?(tmp)??{??
//將文件名從用戶態(tài)拷貝至內(nèi)核態(tài);???
int?retval?=?do_getname(filename,?tmp);??
result?=?tmp;??
if?(retval?
__putname(tmp);??
result?=?ERR_PTR(retval);??
}??
}??
audit_getname(result);??
return?result;??
}??
get_unused_fd_flags實(shí)際調(diào)用的是alloc_fd
[cpp]?view plain?copy
#define?get_unused_fd_flags(flags)?alloc_fd(0,?(flags))??
[cpp]?view plain?copy
/*?
*?allocate?a?file?descriptor,?mark?it?busy.?
*/??
int?alloc_fd(unsigned?start,?unsigned?flags)??
{??
struct?files_struct?*files?=?current->files;//獲得當(dāng)前進(jìn)程的files_struct?結(jié)構(gòu)??
unsigned?int?fd;??
int?error;??
struct?fdtable?*fdt;??
spin_lock(&files->file_lock);??
repeat:??
fdt?=?files_fdtable(files);??
fd?=?start;??
if?(fd?next_fd)?//從上一次打開的fd的下一個fd開始搜索空閑的fd????
fd?=?files->next_fd;??
if?(fd?max_fds)//尋找空閑的fd,返回值為空閑的fd????
fd?=?find_next_zero_bit(fdt->open_fds->fds_bits,??
fdt->max_fds,?fd);??
//如果有必要,即打開的fd超過max_fds,則需要expand當(dāng)前進(jìn)程的fd表;????
//返回值error<0表示出錯,error=0表示無需expand,error=1表示進(jìn)行了expand;??
error?=?expand_files(files,?fd);??
if?(error?
goto?out;??
/*?
*?If?we?needed?to?expand?the?fs?array?we?
*?might?have?blocked?-?try?again.?
*/??
//error=1表示進(jìn)行了expand,那么此時需要重新去查找空閑的fd;????
if?(error)??
goto?repeat;??
//設(shè)置下一次查找的起始fd,即本次找到的空閑的fd的下一個fd,記錄在files->next_fd中;????
if?(start?<=?files->next_fd)??
files->next_fd?=?fd?+?1;??
FD_SET(fd,?fdt->open_fds);??
if?(flags?&?O_CLOEXEC)??
FD_SET(fd,?fdt->close_on_exec);??
else??
FD_CLR(fd,?fdt->close_on_exec);??
error?=?fd;??
#if?1??
/*?Sanity?check?*/??
if?(rcu_dereference(fdt->fd[fd])?!=?NULL)?{??
printk(KERN_WARNING?"alloc_fd:?slot?%d?not?NULL! ",?fd);??
rcu_assign_pointer(fdt->fd[fd],?NULL);??
}??
#endif??
out:??
spin_unlock(&files->file_lock);??
return?error;??
}??
該函數(shù)為需要打開的文件在當(dāng)前進(jìn)程內(nèi)分配一個空閑的文件描述符fd,該fd就是open()系統(tǒng)調(diào)用的返回值
do_filp_open函數(shù)的一個重要作用就是根據(jù)傳遞近來的權(quán)限進(jìn)行分析,并且分析傳遞近來的路徑名字,根據(jù)路徑名逐個解析成dentry,并且通過dentry找到inode,inode就是記錄著該文件相關(guān)的信息, 包括文件的創(chuàng)建時間和文件屬性所有者等等信息,根據(jù)這些信息就可以找到對應(yīng)的文件操作方法。在這個過程當(dāng)中有一個臨時的結(jié)構(gòu)體用于保存在查找過程中的相關(guān)信息,就是
[cpp]?view plain?copy
struct?nameidata?{??
struct?path?path;//當(dāng)前目錄的dentry數(shù)據(jù)結(jié)構(gòu)??
struct?qstr?last;//這個結(jié)構(gòu)體也是臨時性的,主要用來保存當(dāng)前目錄的名稱,雜湊值。??
unsigned?int????flags;??
int?????last_type;??
unsigned????depth;//連接文件的深度(可能一個連接文件跟到最后還是一個了連接文件)??
//用來保存連接文件的一些信息,下標(biāo)表示連接文件的深度??
char?*saved_names[MAX_NESTED_LINKS?+?1];??
/*?Intent?data?*/??
union?{??
struct?open_intent?open;??
}?intent;??
};??
[cpp]?view plain?copy
struct?file?*do_filp_open(int?dfd,?const?char?*pathname,??
int?open_flag,?int?mode,?int?acc_mode)??
{??
struct?file?*filp;??
struct?nameidata?nd;??
int?error;??
struct?path?path;??
int?count?=?0;??
int?flag?=?open_to_namei_flags(open_flag);?/*改變參數(shù)flag的值,具體做法是flag+1*/???
int?force_reval?=?0;??
if?(!(open_flag?&?O_CREAT))??
mode?=?0;??
/*?
*?O_SYNC?is?implemented?as?__O_SYNC|O_DSYNC.??As?many?places?only?
*?check?for?O_DSYNC?if?the?need?any?syncing?at?all?we?enforce?it's?
*?always?set?instead?of?having?to?deal?with?possibly?weird?behaviour?
*?for?malicious?applications?setting?only?__O_SYNC.?
*/??
if?(open_flag?&?__O_SYNC)/*根據(jù)__O_SYNC標(biāo)志來設(shè)置O_DSYNC?標(biāo)志,用以防止惡意破壞程序*/???
open_flag?|=?O_DSYNC;??
if?(!acc_mode)/*設(shè)置訪問權(quán)限*/???
acc_mode?=?MAY_OPEN?|?ACC_MODE(open_flag);??
/*?O_TRUNC?implies?we?need?access?checks?for?write?permissions?*/??
if?(open_flag?&?O_TRUNC)/*根據(jù)?O_TRUNC標(biāo)志設(shè)置寫權(quán)限?*/???
acc_mode?|=?MAY_WRITE;??
/*?Allow?the?LSM?permission?hook?to?distinguish?append??
access?from?general?write?access.?*/??
if?(open_flag?&?O_APPEND)/*?設(shè)置O_APPEND?標(biāo)志*/????
acc_mode?|=?MAY_APPEND;??
/*?find?the?parent?*/??
reval:??
error?=?path_init(dfd,?pathname,?LOOKUP_PARENT,?&nd);//初始化nd??
if?(error)??
return?ERR_PTR(error);??
if?(force_reval)??
nd.flags?|=?LOOKUP_REVAL;??
current->total_link_count?=?0;??
error?=?link_path_walk(pathname,?&nd);//路徑名解析函數(shù),將一個路徑名最終轉(zhuǎn)化為一個dentry??
if?(error)?{??
filp?=?ERR_PTR(error);??
goto?out;??
}??
if?(unlikely(!audit_dummy_context())?&&?(open_flag?&?O_CREAT))??
audit_inode(pathname,?nd.path.dentry);??
/*?
*?We?have?the?parent?and?last?component.?
*/??
error?=?-ENFILE;??
filp?=?get_empty_filp();//?從進(jìn)程文件表中獲取一個未使用的文件結(jié)構(gòu)指針,空則出錯返回??
if?(filp?==?NULL)??
goto?exit_parent;??
nd.intent.open.file?=?filp;??
filp->f_flags?=?open_flag;??
nd.intent.open.flags?=?flag;??
nd.intent.open.create_mode?=?mode;??
nd.flags?&=?~LOOKUP_PARENT;??
nd.flags?|=?LOOKUP_OPEN;??
if?(open_flag?&?O_CREAT)?{??
nd.flags?|=?LOOKUP_CREATE;??
if?(open_flag?&?O_EXCL)??
nd.flags?|=?LOOKUP_EXCL;??
}??
if?(open_flag?&?O_DIRECTORY)??
nd.flags?|=?LOOKUP_DIRECTORY;??
if?(!(open_flag?&?O_NOFOLLOW))??
nd.flags?|=?LOOKUP_FOLLOW;??
filp?=?do_last(&nd,?&path,?open_flag,?acc_mode,?mode,?pathname);//返回一個file結(jié)構(gòu)??
while?(unlikely(!filp))?{?/*?trailing?symlink?*///符號鏈接??
struct?path?holder;??
struct?inode?*inode?=?path.dentry->d_inode;??
void?*cookie;??
error?=?-ELOOP;??
/*?S_ISDIR?part?is?a?temporary?automount?kludge?*/??
if?(!(nd.flags?&?LOOKUP_FOLLOW)?&&?!S_ISDIR(inode->i_mode))??
goto?exit_dput;??
if?(count++?==?32)??
goto?exit_dput;??
/*?
*?This?is?subtle.?Instead?of?calling?do_follow_link()?we?do?
*?the?thing?by?hands.?The?reason?is?that?this?way?we?have?zero?
*?link_count?and?path_walk()?(called?from?->follow_link)?
*?honoring?LOOKUP_PARENT.??After?that?we?have?the?parent?and?
*?last?component,?i.e.?we?are?in?the?same?situation?as?after?
*?the?first?path_walk().??Well,?almost?-?if?the?last?component?
*?is?normal?we?get?its?copy?stored?in?nd->last.name?and?we?will?
*?have?to?putname()?it?when?we?are?done.?Procfs-like?symlinks?
*?just?set?LAST_BIND.?
*/??
nd.flags?|=?LOOKUP_PARENT;??
error?=?security_inode_follow_link(path.dentry,?&nd);??
if?(error)??
goto?exit_dput;??
error?=?__do_follow_link(&path,?&nd,?&cookie);//查找符號鏈接對應(yīng)的目錄中的最后一項(xiàng)??
if?(unlikely(error))?{??
/*?nd.path?had?been?dropped?*/??
if?(!IS_ERR(cookie)?&&?inode->i_op->put_link)??
inode->i_op->put_link(path.dentry,?&nd,?cookie);??
path_put(&path);??
release_open_intent(&nd);??
filp?=?ERR_PTR(error);??
goto?out;??
}??
holder?=?path;??
nd.flags?&=?~LOOKUP_PARENT;??
filp?=?do_last(&nd,?&path,?open_flag,?acc_mode,?mode,?pathname);??
if?(inode->i_op->put_link)??
inode->i_op->put_link(holder.dentry,?&nd,?cookie);??
path_put(&holder);??
}??
out:??
if?(nd.root.mnt)??
path_put(&nd.root);??
if?(filp?==?ERR_PTR(-ESTALE)?&&?!force_reval)?{??
force_reval?=?1;??
goto?reval;??
}??
return?filp;//成功,返回??
exit_dput:??
path_put_conditional(&path,?&nd);??
if?(!IS_ERR(nd.intent.open.file))??
release_open_intent(&nd);??
exit_parent:??
path_put(&nd.path);??
filp?=?ERR_PTR(error);??
goto?out;??
}??
當(dāng)內(nèi)核要訪問一個文件的時候,第一步要做的是找到這個文件,而查找文件的過程在vfs里面是由link_path_walk函數(shù)來完成的,在path_init的時候我們可以看到傳進(jìn)去的參數(shù)有一個LOOKUP_PARENT,它的含義是查找最后一個分量名所在的目錄。也就是當(dāng)這個函數(shù)返回的時候,我們得到了一個路徑名中最后一個分量所在的目錄。
接著調(diào)用do_last返回最后一個分量對應(yīng)的file指針,我們關(guān)注一下這個函數(shù)
[cpp]?view plain?copy
static?struct?file?*do_last(struct?nameidata?*nd,?struct?path?*path,??
int?open_flag,?int?acc_mode,??
int?mode,?const?char?*pathname)??
{??
struct?dentry?*dir?=?nd->path.dentry;??
struct?file?*filp;??
int?error?=?-EISDIR;??
switch?(nd->last_type)?{//?檢查最后一段文件或目錄名的屬性情況??
case?LAST_DOTDOT:??
follow_dotdot(nd);??
dir?=?nd->path.dentry;??
case?LAST_DOT:??
if?(nd->path.mnt->mnt_sb->s_type->fs_flags?&?FS_REVAL_DOT)?{??
if?(!dir->d_op->d_revalidate(dir,?nd))?{??
error?=?-ESTALE;??
goto?exit;??
}??
}??
/*?fallthrough?*/??
case?LAST_ROOT:??
if?(open_flag?&?O_CREAT)??
goto?exit;??
/*?fallthrough?*/??
case?LAST_BIND:??
audit_inode(pathname,?dir);??
goto?ok;??
}??
/*?trailing?slashes??*/??
if?(nd->last.name[nd->last.len])?{??
if?(open_flag?&?O_CREAT)??
goto?exit;??
nd->flags?|=?LOOKUP_DIRECTORY?|?LOOKUP_FOLLOW;??
}??
/*?just?plain?open??*/??
if?(!(open_flag?&?O_CREAT))?{//沒有創(chuàng)建標(biāo)志,即文件存在??
error?=?do_lookup(nd,?&nd->last,?path);//找到路徑中最后一項(xiàng)對應(yīng)的目錄項(xiàng)??
if?(error)??
goto?exit;??
error?=?-ENOENT;??
if?(!path->dentry->d_inode)??
goto?exit_dput;??
if?(path->dentry->d_inode->i_op->follow_link)??
return?NULL;??
error?=?-ENOTDIR;??
if?(nd->flags?&?LOOKUP_DIRECTORY)?{??
if?(!path->dentry->d_inode->i_op->lookup)??
goto?exit_dput;??
}??
path_to_nameidata(path,?nd);//賦值到nd結(jié)構(gòu)??
audit_inode(pathname,?nd->path.dentry);??
goto?ok;??
}??
/*?OK,?it's?O_CREAT?*/??
//文件不存在,需要創(chuàng)建??
mutex_lock(&dir->d_inode->i_mutex);??
path->dentry?=?lookup_hash(nd);//獲取最后路徑名中最后一項(xiàng)對應(yīng)的目錄項(xiàng)??
path->mnt?=?nd->path.mnt;??
error?=?PTR_ERR(path->dentry);??
if?(IS_ERR(path->dentry))?{??
mutex_unlock(&dir->d_inode->i_mutex);??
goto?exit;??
}??
if?(IS_ERR(nd->intent.open.file))?{??
error?=?PTR_ERR(nd->intent.open.file);??
goto?exit_mutex_unlock;??
}??
/*?Negative?dentry,?just?create?the?file?*/??
if?(!path->dentry->d_inode)?{//沒有索引節(jié)點(diǎn)與目錄項(xiàng)關(guān)聯(lián)??
/*?
*?This?write?is?needed?to?ensure?that?a?
*?ro->rw?transition?does?not?occur?between?
*?the?time?when?the?file?is?created?and?when?
*?a?permanent?write?count?is?taken?through?
*?the?'struct?file'?in?nameidata_to_filp().?
*/??
error?=?mnt_want_write(nd->path.mnt);??
if?(error)??
goto?exit_mutex_unlock;??
error?=?__open_namei_create(nd,?path,?open_flag,?mode);//創(chuàng)建相應(yīng)的索引節(jié)點(diǎn)??
if?(error)?{??
mnt_drop_write(nd->path.mnt);??
goto?exit;??
}??
filp?=?nameidata_to_filp(nd);/*根據(jù)nameidata?得到相應(yīng)的file結(jié)構(gòu)*/??
mnt_drop_write(nd->path.mnt);??
if?(!IS_ERR(filp))?{??
error?=?ima_file_check(filp,?acc_mode);??
if?(error)?{??
fput(filp);??
filp?=?ERR_PTR(error);??
}??
}??
return?filp;??
}??
/*?
*?It?already?exists.?
*/??
mutex_unlock(&dir->d_inode->i_mutex);??
audit_inode(pathname,?path->dentry);??
error?=?-EEXIST;??
if?(open_flag?&?O_EXCL)??
goto?exit_dput;??
if?(__follow_mount(path))?{??
error?=?-ELOOP;??
if?(open_flag?&?O_NOFOLLOW)??
goto?exit_dput;??
}??
error?=?-ENOENT;??
if?(!path->dentry->d_inode)??
goto?exit_dput;??
if?(path->dentry->d_inode->i_op->follow_link)??
return?NULL;??
path_to_nameidata(path,?nd);??
error?=?-EISDIR;??
if?(S_ISDIR(path->dentry->d_inode->i_mode))??
goto?exit;??
ok:??
filp?=?finish_open(nd,?open_flag,?acc_mode);//完成文件打開操作??
return?filp;??
exit_mutex_unlock:??
mutex_unlock(&dir->d_inode->i_mutex);??
exit_dput:??
path_put_conditional(path,?nd);??
exit:??
if?(!IS_ERR(nd->intent.open.file))??
release_open_intent(nd);??
path_put(&nd->path);??
return?ERR_PTR(error);??
}??
首先進(jìn)行一些判斷,然后看是否需要創(chuàng)建文件,如果需要創(chuàng)建的,則創(chuàng)建文件。如果文件存在的話,直接調(diào)用finish_open完成文件打開,我們這里關(guān)注下打開文件的
[cpp]?view plain?copy
static?struct?file?*finish_open(struct?nameidata?*nd,??
int?open_flag,?int?acc_mode)??
{??
struct?file?*filp;??
int?will_truncate;??
int?error;??
/*檢測是否截斷文件標(biāo)志*/??
will_truncate?=?open_will_truncate(open_flag,?nd->path.dentry->d_inode);??
if?(will_truncate)?{/*要截斷的話就要獲取寫權(quán)限*/??
error?=?mnt_want_write(nd->path.mnt);??
if?(error)??
goto?exit;??
}??
//may_open執(zhí)行權(quán)限檢測、文件打開和truncate的操作??
error?=?may_open(&nd->path,?acc_mode,?open_flag);??
if?(error)?{??
if?(will_truncate)??
mnt_drop_write(nd->path.mnt);??
goto?exit;??
}??
filp?=?nameidata_to_filp(nd);???/*根據(jù)nameidata?得到相應(yīng)的file結(jié)構(gòu)*/??
if?(!IS_ERR(filp))?{??
error?=?ima_file_check(filp,?acc_mode);??
if?(error)?{??
fput(filp);??
filp?=?ERR_PTR(error);??
}??
}??
if?(!IS_ERR(filp))?{??
if?(will_truncate)?{//?//處理截斷??
error?=?handle_truncate(&nd->path);??
if?(error)?{??
fput(filp);??
filp?=?ERR_PTR(error);??
}??
}??
}??
/*?
*?It?is?now?safe?to?drop?the?mnt?write?
*?because?the?filp?has?had?a?write?taken?
*?on?its?behalf.?
*/??
if?(will_truncate)??//安全的放棄寫權(quán)限??
mnt_drop_write(nd->path.mnt);??
return?filp;??
exit:??
if?(!IS_ERR(nd->intent.open.file))??
release_open_intent(nd);??
path_put(&nd->path);??
return?ERR_PTR(error);??
}??
這里主要調(diào)用nameidata_to_filp得到相應(yīng)的file結(jié)構(gòu)
[cpp]?view plain?copy
struct?file?*nameidata_to_filp(struct?nameidata?*nd)??
{??
const?struct?cred?*cred?=?current_cred();??
struct?file?*filp;??
/*?Pick?up?the?filp?from?the?open?intent?*/??
filp?=?nd->intent.open.file;///?把相關(guān)?file結(jié)構(gòu)的指針賦予?filp??
/*?Has?the?filesystem?initialised?the?file?for?us??*/??
if?(filp->f_path.dentry?==?NULL)??
filp?=?__dentry_open(nd->path.dentry,?nd->path.mnt,?filp,??
NULL,?cred);??
else??
path_put(&nd->path);??
return?filp;??
}??
調(diào)用__dentry_open
[cpp]?view plain?copy
static?struct?file?*__dentry_open(struct?dentry?*dentry,?struct?vfsmount?*mnt,??
struct?file?*f,??
int?(*open)(struct?inode?*,?struct?file?*),??
const?struct?cred?*cred)??
{??
struct?inode?*inode;??
int?error;??
f->f_mode?=?OPEN_FMODE(f->f_flags)?|?FMODE_LSEEK?|//初始化f_mode??
FMODE_PREAD?|?FMODE_PWRITE;??
inode?=?dentry->d_inode;??
if?(f->f_mode?&?FMODE_WRITE)?{??
error?=?__get_file_write_access(inode,?mnt);??
if?(error)??
goto?cleanup_file;??
if?(!special_file(inode->i_mode))??
file_take_write(f);??
}??
f->f_mapping?=?inode->i_mapping;??
f->f_path.dentry?=?dentry;//初始化目錄項(xiàng)對象??
f->f_path.mnt?=?mnt;//初始化文件系統(tǒng)對象??
f->f_pos?=?0;??
f->f_op?=?fops_get(inode->i_fop);//為文件操作建立起所有方法??
file_move(f,?&inode->i_sb->s_files);//把文件對象插入到文件系統(tǒng)超級塊的s_files字段所指向的打開文件的鏈表。??
error?=?security_dentry_open(f,?cred);??
if?(error)??
goto?cleanup_all;??
if?(!open?&&?f->f_op)//傳進(jìn)來的open為NULL??
open?=?f->f_op->open;??
if?(open)?{??
error?=?open(inode,?f);??
if?(error)??
goto?cleanup_all;??
}??
ima_counts_get(f);??
f->f_flags?&=?~(O_CREAT?|?O_EXCL?|?O_NOCTTY?|?O_TRUNC);//初始化f_f_flags??
file_ra_state_init(&f->f_ra,?f->f_mapping->host->i_mapping);//初始化預(yù)讀的數(shù)據(jù)結(jié)構(gòu)??
/*?NB:?we're?sure?to?have?correct?a_ops?only?after?f_op->open?*/??
if?(f->f_flags?&?O_DIRECT)?{//檢查直接IO操作是否可以作用于文件??
if?(!f->f_mapping->a_ops?||??
((!f->f_mapping->a_ops->direct_IO)?&&??
(!f->f_mapping->a_ops->get_xip_mem)))?{??
fput(f);??
f?=?ERR_PTR(-EINVAL);??
}??
}??
return?f;??
cleanup_all:??
fops_put(f->f_op);??
if?(f->f_mode?&?FMODE_WRITE)?{??
put_write_access(inode);??
if?(!special_file(inode->i_mode))?{??
/*?
*?We?don't?consider?this?a?real?
*?mnt_want/drop_write()?pair?
*?because?it?all?happenend?right?
*?here,?so?just?reset?the?state.?
*/??
file_reset_write(f);??
mnt_drop_write(mnt);??
}??
}??
file_kill(f);??
f->f_path.dentry?=?NULL;??
f->f_path.mnt?=?NULL;??
cleanup_file:??
put_filp(f);??
dput(dentry);??
mntput(mnt);??
return?ERR_PTR(error);??
}??
這里主要是進(jìn)行一些賦值操作
對應(yīng)于這里,傳進(jìn)來的open指針為NULL, 如果相應(yīng)file_operations結(jié)構(gòu)存在的話就調(diào)用它的open函數(shù)
對于每個文件在創(chuàng)建 的時候會賦值對其進(jìn)行操作的file_operations結(jié)構(gòu),這個結(jié)構(gòu)對于一類文件是一樣的,例如 對應(yīng)于字符設(shè)備是chrdev_open
[cpp]?view plain?copy
const?struct?file_operations?def_chr_fops?=?{??
.open?=?chrdev_open,??
};??
但打開之后,我們可以重新獲取它們的file_operations結(jié)構(gòu),這個是在注冊設(shè)備驅(qū)動的時候?yàn)樵擃愒O(shè)備賦予的,也就是我們在驅(qū)動里面實(shí)現(xiàn)的,而前面的缺省file_operations就是為了完成這個轉(zhuǎn)換的,def_chr_fops只起過渡作用,它的open方法要去尋找硬件驅(qū)動的支撐。
[cpp]?view plain?copy
static?int?chrdev_open(struct?inode?*inode,?struct?file?*filp)??
{??
struct?cdev?*p;??
struct?cdev?*new?=?NULL;??
int?ret?=?0;??
spin_lock(&cdev_lock);??
p?=?inode->i_cdev;??
if?(!p)?{?/*?很顯然,第一次打開的時候是NULL?*/??
struct?kobject?*kobj;??
int?idx;??
spin_unlock(&cdev_lock);??
kobj?=?kobj_lookup(cdev_map,?inode->i_rdev,?&idx);/*?找到和設(shè)備號i_rdev對應(yīng)的kobj,其實(shí)就是cdev了,因?yàn)閏dev中包含kobj;idx保存的是次設(shè)備號,后面會分析kobj_lookup()函數(shù)?*/??
if?(!kobj)??
return?-ENXIO;??
new?=?container_of(kobj,?struct?cdev,?kobj);//得到cdev??
spin_lock(&cdev_lock);??
/*?Check?i_cdev?again?in?case?somebody?beat?us?to?it?while?
we?dropped?the?lock.?*/??
p?=?inode->i_cdev;??
if?(!p)?{??
inode->i_cdev?=?p?=?new;/*?把找到的cdev保存到inode的icdev中?*/??
list_add(&inode->i_devices,?&p->list);?/*?inode加入到cdev的鏈表中?*/??
new?=?NULL;??
}?else?if?(!cdev_get(p))??
ret?=?-ENXIO;??
}?else?if?(!cdev_get(p))??
ret?=?-ENXIO;??
spin_unlock(&cdev_lock);??
cdev_put(new);??
if?(ret)??
return?ret;??
ret?=?-ENXIO;??
/*??
保存用戶的fops,以后你再調(diào)用read,?write,?ioctl系統(tǒng)調(diào)用的時候就直接使用了,你懂的??
*/??
filp->f_op?=?fops_get(p->ops);??
if?(!filp->f_op)?//?如果你沒有注冊fops??
goto?out_cdev_put;??
if?(filp->f_op->open)?{//判斷open函數(shù)是否存在??
ret?=?filp->f_op->open(inode,filp);//*?調(diào)用用戶的open函數(shù),我們前面寫的驅(qū)動??
if?(ret)??
goto?out_cdev_put;??
}??
return?0;??
out_cdev_put:??
cdev_put(p);??
return?ret;??
}??
在這個函數(shù)里,我們重新 對f_op賦值了,這里的f_op就是我們在寫驅(qū)動時寫的系統(tǒng)調(diào)用函數(shù)了。后面還調(diào)用了open方法
這里調(diào)用?kobj_lookup找到前面我們在注冊驅(qū)動添加設(shè)備時添加的相應(yīng)的kobj
[cpp]?view plain?copy
struct?kobject?*kobj_lookup(struct?kobj_map?*domain,?dev_t?dev,?int?*index)??
{??
struct?kobject?*kobj;??
struct?probe?*p;??
unsigned?long?best?=?~0UL;??
retry:??
mutex_lock(domain->lock);??
/*?根據(jù)主設(shè)備號和設(shè)備號查找它的一畝三分地。因?yàn)橐С?^12次方也就是4096個主設(shè)備號,?
但只使用了前255個主設(shè)備號索引,所以這255個索引對應(yīng)的probe結(jié)構(gòu)都有一個單向?
鏈表保存著大于255的主設(shè)備號(被255整除后的索引相等)??*/??
for?(p?=?domain->probes[MAJOR(dev)?%?255];?p;?p?=?p->next)?{??
struct?kobject?*(*probe)(dev_t,?int?*,?void?*);??
struct?module?*owner;??
void?*data;??
//?比較,看是否真找到了,因?yàn)橛墟湵泶嬖??
if?(p->dev?>?dev?||?p->dev?+?p->range?-?1?
continue;??
if?(p->range?-?1?>=?best)??
break;??
if?(!try_module_get(p->owner))??
continue;??
owner?=?p->owner;??
data?=?p->data;//data就是cdev??
probe?=?p->get;??
best?=?p->range?-?1;??
*index?=?dev?-?p->dev;//得到次設(shè)備號??
/*?調(diào)用的lock就是exact_lock()函數(shù),增加對該字符設(shè)備驅(qū)動的引用,防止被卸載什么的?*/??
if?(p->lock?&&?p->lock(dev,?data)?
module_put(owner);??
continue;??
}??
mutex_unlock(domain->lock);??
/*調(diào)用的probe就是exact_match()函數(shù),獲取cdev的kobj指針?*/??
kobj?=?probe(dev,?index,?data);??
/*?Currently?->owner?protects?_only_?->probe()?itself.?*/??
module_put(owner);??
if?(kobj)??
return?kobj;??
goto?retry;??
}??
mutex_unlock(domain->lock);??
return?NULL; ?
} ?
到這里do_filp_open的流程就基本完成了,即返回了一個file結(jié)構(gòu)
[cpp]?view plain?copy
void?fd_install(unsigned?int?fd,?struct?file?*file)??
{??
struct?files_struct?*files?=?current->files;??
struct?fdtable?*fdt;??
spin_lock(&files->file_lock);??
fdt?=?files_fdtable(files);//獲取fdtbale??
BUG_ON(fdt->fd[fd]?!=?NULL);??
rcu_assign_pointer(fdt->fd[fd],?file);//fd和file關(guān)系到fdtbale??
spin_unlock(&files->file_lock);??
}??
這樣,我們的open系統(tǒng)調(diào)用就基本上完成了,我們得到一個fd,這個fd關(guān)聯(lián)著一個file結(jié)構(gòu),這樣以后,我們就可以通過這個fd結(jié)構(gòu)操作相應(yīng)的文件了
最后還是一樣看下整個簡略的流程圖
?
評論