本篇內容介紹了“基于linux的open原理是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
我們操作一個文件之前都需要先open一下。我們看看open在虛擬文件系統中大致的執行過程。不會分析具體的過程。主要分析一下虛擬文件系統的實現原理。
asmlinkage int sys_open(const char * filename,int flags,int mode)
{
char * tmp;
int error;
error = getname(filename, &tmp);
if (error)
return error;
error = do_open(tmp,flags,mode);
putname(tmp);
return error;
}
下面是do_open的代碼。
// 打開一個文件
int do_open(const char * filename,int flags,int mode)
{
struct inode * inode;
struct file * f;
int flag,error,fd;
// 找到一個可用的文件描述符
for(fd=0; fd<NR_OPEN && fd<current->rlim[RLIMIT_NOFILE].rlim_cur; fd++)
// 還沒被使用則找到可用的
if (!current->files->fd[fd])
break;
// 找不到可用的
if (fd>=NR_OPEN || fd>=current->rlim[RLIMIT_NOFILE].rlim_cur)
return -EMFILE;
// 清除close_on_exec標記位
FD_CLR(fd,¤t->files->close_on_exec);
// 獲取一個可用的file結構體
f = get_empty_filp();
if (!f)
return -ENFILE;
// 建立fd到file結構體的映射
current->files->fd[fd] = f;
f->f_flags = flag = flags;
f->f_mode = (flag+1) & O_ACCMODE;
if (f->f_mode)
flag++;
if (flag & (O_TRUNC | O_CREAT))
flag |= 2;
// 找到文件對應的inode節點,放到inode變量中
error = open_namei(filename,flag,mode,&inode,NULL);
if (!error && (f->f_mode & 2)) {
error = get_write_access(inode);
if (error)
iput(inode);
}
if (error) {
current->files->fd[fd]=NULL;
f->f_count--;
return error;
}
// 建立file結構體和inode的映射關系
f->f_inode = inode;
// 初始化文件偏移
f->f_pos = 0;
f->f_reada = 0;
f->f_op = NULL;
// 賦值操作file結構體的函數集
if (inode->i_op)
f->f_op = inode->i_op->default_file_ops;
if (f->f_op && f->f_op->open) {
// 調用底層文件系統的open函數
error = f->f_op->open(inode,f);
if (error) {
if (f->f_mode & 2) put_write_access(inode);
iput(inode);
f->f_count--;
current->files->fd[fd]=NULL;
return error;
}
}
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
return (fd);
}
主要工作是通過open_namei找到文件對應的inode節點,然后執行底層的open。最后建立fd到file到inode的關系。在虛擬文件系統中,超級塊、inode、目錄、文件都是一個抽象的概念,具體的操作函數由各個文件系統實現。所以我們首先找到一個結構體,然后根據實際的操作值調用具體文件系統的操作函數就行。
所以我們先看open_namei,再看具體的open函數。
下面是open_namei的核心代碼。
// 找出最后一級目錄的inode
error = dir_namei(pathname,&namelen,&basename,base,&dir);
// 在最后一級目錄下找出某文件對應的inode
error = lookup(dir,basename,namelen,&inode);
大致思路就是先找到最后一級目錄的inode,然后在最后一級目錄下找某個文件。我們看看怎么找到最后一級目錄對應的inode。
// 找出pathname中最后一級目錄對應的inode
static int dir_namei(const char * pathname, int * namelen, const char ** name,
struct inode * base, struct inode ** res_inode)
{
char c;
const char * thisname;
int len,error;
struct inode * inode;
*res_inode = NULL;
// 沒有傳base則從進程的當前工作目錄開始找
if (!base) {
base = current->fs->pwd;
base->i_count++;
}
// 絕對路徑,則從根目錄開始找
if ((c = *pathname) == '/') {
iput(base);
base = current->fs->root;
pathname++;
base->i_count++;
}
// 找出pathname最后一級目錄對應的inode節點
while (1) {
thisname = pathname;
// 以/分割路徑部分,遇到/則得到某級路徑名稱,thisname指向當前路徑首地址,len代表該部分路徑對應的長度
for(len=0;(c = *(pathname++))&&(c != '/');len++)
/* nothing */ ;
// c為空說明到最后一個字符,即路徑結束,不找最后一級的文件或者目錄了
if (!c)
break;
base->i_count++;
// 在base目錄下查找某個文件
error = lookup(base,thisname,len,&inode);
if (error) {
iput(base);
return error;
}
// 設置base為inode,繼續找
error = follow_link(base,inode,0,0,&base);
if (error)
return error;
}
if (!base->i_op || !base->i_op->lookup) {
iput(base);
return -ENOTDIR;
}
*name = thisname;
*namelen = len;
*res_inode = base;
return 0;
}
就是通過以/分割字符串,在開始的目錄下,即根目錄或者給定的目錄。找到下一級目錄的inode,不斷地迭代就能找到最后一級目錄的inode了。我們發現在查找最后一級目錄的inode和在某個目錄下找某個文件的inode都用到lookup函數。我們看看他的實現。核心代碼。
return dir->i_op->lookup(dir,name,len,result);
以ext文件系統為例??纯磍ookup的實現。
int ext_lookup(struct inode * dir,const char * name, int len,
struct inode ** result)
{
int ino;
struct ext_dir_entry * de;
struct buffer_head * bh;
*result = NULL;
if (!dir)
return -ENOENT;
if (!S_ISDIR(dir->i_mode)) {
iput(dir);
return -ENOENT;
}
if (!(bh = ext_find_entry(dir,name,len,&de,NULL,NULL))) {
iput(dir);
return -ENOENT;
}
ino = de->inode;
brelse(bh);
if (!(*result = iget(dir->i_sb,ino))) {
iput(dir);
return -EACCES;
}
iput(dir);
return 0;
}
static struct buffer_head * ext_find_entry(struct inode * dir,
const char * name, int namelen, struct ext_dir_entry ** res_dir,
struct ext_dir_entry ** prev_dir, struct ext_dir_entry ** next_dir)
{
long offset;
struct buffer_head * bh;
struct ext_dir_entry * de;
*res_dir = NULL;
if (!dir)
return NULL;
#ifdef NO_TRUNCATE
if (namelen > EXT_NAME_LEN)
return NULL;
#else
if (namelen > EXT_NAME_LEN)
namelen = EXT_NAME_LEN;
#endif
// 讀取目錄的文件內容,然后比較
bh = ext_bread(dir,0,0);
if (!bh)
return NULL;
if (prev_dir)
*prev_dir = NULL;
if (next_dir)
*next_dir = NULL;
offset = 0;
de = (struct ext_dir_entry *) bh->b_data;
while (offset < dir->i_size) {
if ((char *)de >= BLOCK_SIZE+bh->b_data) {
brelse(bh);
bh = NULL;
bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,0);
if (!bh)
continue;
de = (struct ext_dir_entry *) bh->b_data;
if (prev_dir)
*prev_dir = NULL;
}
if (de->rec_len < 8 || de->rec_len % 8 != 0 ||
de->rec_len < de->name_len + 8 ||
(((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) {
printk ("ext_find_entry: bad dir entry\n");
printk ("dev=%d, dir=%ld, offset=%ld, rec_len=%d, name_len=%d\n",
dir->i_dev, dir->i_ino, offset, de->rec_len, de->name_len);
de = (struct ext_dir_entry *) (bh->b_data+BLOCK_SIZE);
offset = ((offset / BLOCK_SIZE) + 1) * BLOCK_SIZE;
continue;
/* brelse (bh);
return NULL; */
}
// 是否找到了想要的文件
if (ext_match(namelen,name,de)) {
*res_dir = de;
if (next_dir)
if (offset + de->rec_len < dir->i_size &&
((char *)de) + de->rec_len < BLOCK_SIZE+bh->b_data)
*next_dir = (struct ext_dir_entry *)
((char *) de + de->rec_len);
else
*next_dir = NULL;
return bh;
}
offset += de->rec_len;
if (prev_dir)
*prev_dir = de;
de = (struct ext_dir_entry *) ((char *) de + de->rec_len);
}
brelse(bh);
return NULL;
}
代碼比較多,可以不用細究,主要是把目錄的文件內容從硬盤讀取進來,然后遍歷每一個文件名是否和要找的一致就行。
通過上面的分析我們已經找到了一個文件對應的inode節點了。一般文件系統沒有實現open函數。所以直接返回inode,建立fd到file到inode的關系即可。后面對文件的操作也是通過inode節點的操作函數集進行。
“基于linux的open原理是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。