从open系统调用的源码看文件的打开过程
open系統調用:創建file結構體,(指針)放入進程打開文件表,返回表下標(文件描述符)
?
?
?
?
?
轉自:http://blog.csdn.net/qiang81020/archive/2010/06/20/5681481.aspx
?
我們常常使用系統調用open來打開一個文件,例如:
fd = open( "/mnt/data/myfile",O_RDWR|O_CREAT);
下面來看看Linux是如何完成的,首先是系統調用的代碼:
sys_open的源程序
view plaincopy to clipboardprint?
asmlinkage long sys_open(const char * filename, int flags, int mode)??
{??
??????? char * tmp;??
??????? int fd, error;??
#if BITS_PER_LONG != 32??
??????? flags |= O_LARGEFILE;??
#endif??
??????? tmp = getname(filename);??
??????? fd = PTR_ERR(tmp);??
??????? if (!IS_ERR(tmp)) {??
??????????????? fd = get_unused_fd();/*<-----------------------------------(1) */?
??????????????? if (fd >= 0) {??
??????????????????????? struct file *f = filp_open(tmp, flags, mode);/*<---(2) */?
??????????????????????? error = PTR_ERR(f);??
??????????????????????? if (IS_ERR(f))??
??????????????????????????????? goto out_error;??
??????????????????????? fd_install(fd, f);/*<------------------------------(3) */?
??????????????? }??
out:??
??????????????? putname(tmp);??
??????? }??
??????? return fd;??
out_error:??
??????? put_unused_fd(fd);??
??????? fd = error;??
??????? goto out;??
}?
asmlinkage long sys_open(const char * filename, int flags, int mode)
{
??????? char * tmp;
??????? int fd, error;
#if BITS_PER_LONG != 32
??????? flags |= O_LARGEFILE;
#endif
??????? tmp = getname(filename);
??????? fd = PTR_ERR(tmp);
??????? if (!IS_ERR(tmp)) {
??????????????? fd = get_unused_fd();/*<-----------------------------------(1) */
??????????????? if (fd >= 0) {
??????????????????????? struct file *f = filp_open(tmp, flags, mode);/*<---(2) */
??????????????????????? error = PTR_ERR(f);
??????????????????????? if (IS_ERR(f))
??????????????????????????????? goto out_error;
??????????????????????? fd_install(fd, f);/*<------------------------------(3) */
??????????????? }
out:
??????????????? putname(tmp);
??????? }
??????? return fd;
out_error:
??????? put_unused_fd(fd);
??????? fd = error;
??????? goto out;
}
?
這里面完成的幾個工作
1.注意到返回一個整數fd,所以在(1)的位置獲得一個整數fd。
2.根據路徑/mnt/data/myfile找到(或創建)文件,并且創建一個結構file(見(2))。
3.將整數fd和file結構指針f聯系起來(見(3))。
注意:
結構file實際上是系統打開文件表中的表項,目的是實現不同進程共享文件的讀寫指針的情形。(另外一種文件共享是不同的進程有各自的文件讀寫指針的情形)。
文件打開過程的總結:
?
系統在進程打開文件表中找到一個未使用的fd;
根據open中提供的路徑字符串找到(創建)對應的dentry(從而找到inode);
創建系統打開文件表中的一個打開文件表項(創建一個file結構,使用指針filp指向file結構),并使該表項指向dentry(file的字段之一就是f_dentry,指向被打開文件在其目錄中的表項dentry);
在fd所指示的進程打開文件表表項中設置指向file結構的指針;
系統使用filp->f_dentry->d_inode的方式訪問磁盤inode,將其讀入內存中,創建對應的vnode。因此,可以認為系統打開文件表表項有指針指向vnode結構
進一步了解:
Linux 2.6.11內核文件IO系統調用
Linux 2.6.11內核文件IO系統調用
1.??????? 引言
??? 從事Linux環境工作2年有余,一直懵懵懂懂,1年前拜讀了《萊昂氏UNIX源代碼分析》一書,感覺自己的學習道路漫漫且修遠。最近受chinaunix的精華文帖啟發,擬將近來的部分內核調用分析筆記拿出來與各前輩先進共同探討學習,以壯個人學習之路。
??? 本部分主要講述的是文件I/O操作的2.6.11內核版本實現,包括了主要的數據結構、宏定義和函數流程。以下分別講述open,create,close,read,write,lseek系統調用。
2.??????? 主要參考
《萊昂氏UNIX源代碼分析》
《UNIX環境高級編程》
? www.kernel.org
3.??????? 主要數據結構
3.1.??????? FD
????? 對于內核而言,所有打開文件都由文件描述符引用。文件描述符是一個非負整數。當打開一個現存文件或創建一個新文件時,內核向進程返回一個文件描述符。
??? 當讀、寫一個文件時,用open或creat返回的文件描述符標識該文件,將其作為參數傳送給read或write。在POSIX.1應用程序中,文件描述符為常數0、1和2分別代表STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO,意即標準輸入,標準輸出和標準出錯輸出,這些常數都定義在頭文件
;中。
文件描述符的范圍是0~OPEN_MAX,在目前常用的linux系統中,是32位整形所能表示的整數,即65535,64位機上則更多。
3.2.??????? File
struct file {
?? struct list_head??????? f_list; //文件鏈表指針
? struct dentry??????????????? *f_dentry; // 文件對應的目錄結構
? struct vfsmount???????? *f_vfsmnt; // 虛擬文件系統掛載點
? struct file_operations??????? *f_op; // 文件操作函數指針
? atomic_t??????????????? f_count;
?? unsigned int???????????????? f_flags;
?? mode_t??????????????????????? f_mode; // 文件模式
? int??????????????????????? f_error;
?? loff_t??????????????????????? f_pos; // 文件offset
?? struct fown_struct??????? f_owner; //文件owner 結構
? unsigned int??????????????? f_uid, f_gid;
?? struct file_ra_state??????? f_ra; // 跟蹤上次文件操作狀態的結構指針
? size_t??????????????????????? f_maxcount; // 文件大小
? unsigned long??????????????? f_version;
?? void??????????????????????? *f_security; // hook 文件操作的security結構指針
? void??????????????????????? *private_data; // tty 驅動器所需數據
#ifdef CONFIG_EPOLL
?? struct list_head??????? f_ep_links; // EPOLL 機制檢測所需鏈表結構
? spinlock_t??????????????? f_ep_lock; // 兼容早期gcc bug 的標志
#endif /* #ifdef CONFIG_EPOLL */
?? struct address_space??????? *f_mapping; // 地址映射表
}
3.3.??????? File_struct
File_struct結構保存了進程打開的所有文件表數據。
struct files_struct {
?? atomic_t count; // 自動增量
? spinlock_t file_lock; // 低位成員保護標識
? int max_fds; // 最大文件句柄數目
? int max_fdset; // 最大的fd集合容量
? int next_fd; // 下一個空閑fd
?? struct file ** fd; // 當前fd對應的文件結構指針列表
? fd_set *close_on_exec; // 可執行close的fd集合
? fd_set *open_fds; // 打開的fd集合
? fd_set close_on_exec_init; //
?? fd_set open_fds_init;
?? struct file * fd_array[NR_OPEN_DEFAULT]; // 默認打開的fd隊列
};
4.??????? open 函數
4.1.??????? 原型與參數
?? int open(const char * pathname,? int oflag, .../*, mode_t mode * / )? -1代表錯誤。
?? 這里的oflag是一個整形,主要供open 函數使用,部分fcntl函數也會使用。詳細的說明請用man 2 open就可以看到了。以下列出了2.6內核定義的open和fcntl函數所使用的flag宏定義,說明的格式如宏定義名稱<實際常數值>;: 描述。
?? O_ACCMODE??????? <0003>;: 讀寫文件操作時,用于取出flag的低2位。
?? O_RDONLY<00>;: 只讀打開
?? O_WRONLY<01>;: 只寫打開
?? O_RDWR<02>;: 讀寫打開
?? O_CREAT<0100>;: 文件不存在則創建,需要mode_t,not fcntl
??? O_EXCL<0200>;: 如果同時指定了O_CREAT,而文件已經存在,則出錯, not fcntl
??? O_NOCTTY<0400>;: 如果pathname指終端設備,則不將此設備分配作為此進程的控制終端。not fcntl O_TRUNC<01000>;: 如果此文件存在,而且為只讀或只寫成功打開,則將其長度截短為0。not fcntl
??? O_APPEND<02000>;: 每次寫時都加到文件的尾端
?? O_NONBLOCK<04000>;: 如果p a t h n a m e指的是一個F I F O、一個塊特殊文件或一個字符特殊文件,則此選擇項為此文件的本次打開操作和后續的I / O操作設置非阻塞方式。
O_NDELAY;;
?? O_SYNC<010000>;: 使每次write都等到物理I/O操作完成。
?? FASYNC<020000>;: 兼容BSD的fcntl同步操作
?? O_DIRECT<040000>;: 直接磁盤操作標識
?? O_LARGEFILE<0100000>;: 大文件標識
?? O_DIRECTORY<0200000>;:??????? 必須是目錄
?? O_NOFOLLOW<0400000>;: 不獲取連接文件
?? O_NOATIME<01000000>;: 暫無
?? 當新創建一個文件時,需要指定mode 參數,以下說明的格式如宏定義名稱<實際常數值>;: 描述。
?? S_IRWXU<00700>;:文件擁有者有讀寫執行權限
?? S_IRUSR (S_IREAD)<00400>;:文件擁有者僅有讀權限
?? S_IWUSR (S_IWRITE)<00200>;:文件擁有者僅有寫權限
?? S_IXUSR (S_IEXEC)<00100>;:文件擁有者僅有執行權限
?? S_IRWXG<00070>;:組用戶有讀寫執行權限
?? S_IRGRP<00040>;:組用戶僅有讀權限
?? S_IWGRP<00020>;:組用戶僅有寫權限
?? S_IXGRP<00010>;:組用戶僅有執行權限
?? S_IRWXO<00007>;:其他用戶有讀寫執行權限
?? S_IROTH<00004>;:其他用戶僅有讀權限
?? S_IWOTH<00002>;:其他用戶僅有寫權限
?? S_IXOTH<00001>;:其他用戶僅有執行權限
4.2.??????? 實現分析
4.2.1.??????? 主要函數調用關系圖
?? sys_open( 見4.2.2 節)
?? | ----------- getname( 見4.2.3 節 )
?? | ----------- filp_open( 見4.2.4節 )
?? |??????????????????????? | ------------ open_namei( 見4.2.4.1節 )
?? |??????????????????????? |????????????????? | ----------- may_open( 見4.2.4.1.1節 )
?? |??????????????????????? | ------------ dentry_open( 見4.2.4.2節 )
4.2.2.??????? 主調用函數sys_open
asmlinkage long sys_open(const char __user * filename, int flags, int mode){
??????? char * tmp;
??????? int fd, error;
// 如果不是32位處理器,則增加大文件標識
#if BITS_PER_LONG != 32
??????? flags |= O_LARGEFILE;
#endif
??????? // 為了提高使用效率,在使用之前先將文件名拷貝到內核數據區。見3.2.2說明
??????? tmp = getname(filename);
??????? // 獲取到返回值,如果出錯,則返回,否則執行打開操作。
??????? fd = PTR_ERR(tmp);
??????? if (!IS_ERR(tmp)) {
??????????????? // 從進程的文件表中找出一個空閑的文件表指針,如果出錯,則返回
??????????????? fd = get_unused_fd();
??????????????? if (fd >;= 0) {
??????????????????????? // 執行打開操作。見3.2.3說明
??????????????????????? struct file *f = filp_open(tmp, flags, mode);
??????????????????????? // 獲取返回結果,如果出錯,則跳轉至out_error,否則執行fd_install
??????????????????????? error = PTR_ERR(f);
??????????????????????? if (IS_ERR(f))
??????????????????????????????? goto out_error;
??????????????????????? // 添加打開的文件表 f 到當前進程的文件表隊列中。見3.2.4說明
??????????????????????? fd_install(fd, f);
??????????????? }
out:
??????????????? // 釋放getname分配的內存空間
??????????????? putname(tmp);
??????? }
??????? return fd;
out_error:
??????????????? // 將文件表指針返回到進程的文件表中,并返回錯誤代碼。
??????? put_unused_fd(fd);
??????? fd = error;
??????? goto out;
}
4.2.3.??????? sys_open子函數getname
getname函數主要功能是在使用文件名之前將其拷貝到內核數據區,正常結束時返回內核分配的空間首地址,出錯時返回錯誤代碼。其調用了函數do_getname來實現。
static inline int do_getname(const char __user *filename, char *page){
??????? int retval;
??????? unsigned long len = PATH_MAX; // 內核允許的最大路徑長度
??????? // 如果進程的地址限制是否和KERNEL_DS相等,則檢查文件名是否小于用戶進程空間
??????? if (!segment_eq(get_fs(), KERNEL_DS)) {
??????????????? // 文件名地址大于用戶進程空間,則返回錯誤-EFAULT
??????????????? if ((unsigned long) filename >;= TASK_SIZE)
??????????????????????? return -EFAULT;
??????????????? // 獲取較小的地址長度
??????????????? if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
??????????????????????? len = TASK_SIZE - (unsigned long) filename;
??????? }
??????? // 將filename拷貝len長度到page中,返回實際拷貝長度
??????? retval = strncpy_from_user(page, filename, len);
??????? if (retval >; 0) {
??????????????? // 如果retval大于等于len,則返回-ENAMETOOLONG
??????????????? if (retval < len)
??????????????????????? return 0;
??????????????? return -ENAMETOOLONG;
??????? } else if (!retval)
??????????????? // filename 為空,則返回-ENOENT
??????????????? retval = -ENOENT;
??????? return retval;
}
char * getname(const char __user * filename){
??????? char *tmp, *result;
??????? result = ERR_PTR(-ENOMEM);
??????? // 從內核緩存中分配空間,如果成功,則執行do_getname
??????? tmp = __getname();
??????? if (tmp)? {
??????????????? // 執行文件名拷貝操作
??????????????? int retval = do_getname(filename, tmp);
??????????????? result = tmp;
??????????????? if (retval < 0) {
??????????????????????? // do_getname出錯,則釋放空間,并返回錯誤代碼
??????????????????????? __putname(tmp);
??????????????????????? result = ERR_PTR(retval);
??????????????? }
??????? }
??????? // 如果前面操作成功,且audit_context不為空,則將當前文件名添加到audit列表中
??????? if (unlikely(current->;audit_context) && !IS_ERR(result) && result)
??????????????? audit_getname(result);
??????? // 返回處理結果
??????? return result;
??? }
?
總結
以上是生活随笔為你收集整理的从open系统调用的源码看文件的打开过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Web架构
- 下一篇: 内存映射(mmap系统调用)