您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“基于linux的open原理是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
我們操作一個(gè)文件之前都需要先open一下。我們看看open在虛擬文件系統(tǒng)中大致的執(zhí)行過程。不會(huì)分析具體的過程。主要分析一下虛擬文件系統(tǒng)的實(shí)現(xiàn)原理。
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的代碼。
// 打開一個(gè)文件
int do_open(const char * filename,int flags,int mode)
{
struct inode * inode;
struct file * f;
int flag,error,fd;
// 找到一個(gè)可用的文件描述符
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標(biāo)記位
FD_CLR(fd,¤t->files->close_on_exec);
// 獲取一個(gè)可用的file結(jié)構(gòu)體
f = get_empty_filp();
if (!f)
return -ENFILE;
// 建立fd到file結(jié)構(gòu)體的映射
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;
// 找到文件對(duì)應(yīng)的inode節(jié)點(diǎn),放到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結(jié)構(gòu)體和inode的映射關(guān)系
f->f_inode = inode;
// 初始化文件偏移
f->f_pos = 0;
f->f_reada = 0;
f->f_op = NULL;
// 賦值操作file結(jié)構(gòu)體的函數(shù)集
if (inode->i_op)
f->f_op = inode->i_op->default_file_ops;
if (f->f_op && f->f_op->open) {
// 調(diào)用底層文件系統(tǒng)的open函數(shù)
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找到文件對(duì)應(yīng)的inode節(jié)點(diǎn),然后執(zhí)行底層的open。最后建立fd到file到inode的關(guān)系。在虛擬文件系統(tǒng)中,超級(jí)塊、inode、目錄、文件都是一個(gè)抽象的概念,具體的操作函數(shù)由各個(gè)文件系統(tǒng)實(shí)現(xiàn)。所以我們首先找到一個(gè)結(jié)構(gòu)體,然后根據(jù)實(shí)際的操作值調(diào)用具體文件系統(tǒng)的操作函數(shù)就行。
所以我們先看open_namei,再看具體的open函數(shù)。
下面是open_namei的核心代碼。
// 找出最后一級(jí)目錄的inode
error = dir_namei(pathname,&namelen,&basename,base,&dir);
// 在最后一級(jí)目錄下找出某文件對(duì)應(yīng)的inode
error = lookup(dir,basename,namelen,&inode);
大致思路就是先找到最后一級(jí)目錄的inode,然后在最后一級(jí)目錄下找某個(gè)文件。我們看看怎么找到最后一級(jí)目錄對(duì)應(yīng)的inode。
// 找出pathname中最后一級(jí)目錄對(duì)應(yīng)的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則從進(jìn)程的當(dāng)前工作目錄開始找
if (!base) {
base = current->fs->pwd;
base->i_count++;
}
// 絕對(duì)路徑,則從根目錄開始找
if ((c = *pathname) == '/') {
iput(base);
base = current->fs->root;
pathname++;
base->i_count++;
}
// 找出pathname最后一級(jí)目錄對(duì)應(yīng)的inode節(jié)點(diǎn)
while (1) {
thisname = pathname;
// 以/分割路徑部分,遇到/則得到某級(jí)路徑名稱,thisname指向當(dāng)前路徑首地址,len代表該部分路徑對(duì)應(yīng)的長度
for(len=0;(c = *(pathname++))&&(c != '/');len++)
/* nothing */ ;
// c為空說明到最后一個(gè)字符,即路徑結(jié)束,不找最后一級(jí)的文件或者目錄了
if (!c)
break;
base->i_count++;
// 在base目錄下查找某個(gè)文件
error = lookup(base,thisname,len,&inode);
if (error) {
iput(base);
return error;
}
// 設(shè)置base為inode,繼續(xù)找
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;
}
就是通過以/分割字符串,在開始的目錄下,即根目錄或者給定的目錄。找到下一級(jí)目錄的inode,不斷地迭代就能找到最后一級(jí)目錄的inode了。我們發(fā)現(xiàn)在查找最后一級(jí)目錄的inode和在某個(gè)目錄下找某個(gè)文件的inode都用到lookup函數(shù)。我們看看他的實(shí)現(xiàn)。核心代碼。
return dir->i_op->lookup(dir,name,len,result);
以ext文件系統(tǒng)為例。看看lookup的實(shí)現(xiàn)。
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
// 讀取目錄的文件內(nèi)容,然后比較
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;
}
代碼比較多,可以不用細(xì)究,主要是把目錄的文件內(nèi)容從硬盤讀取進(jìn)來,然后遍歷每一個(gè)文件名是否和要找的一致就行。
通過上面的分析我們已經(jīng)找到了一個(gè)文件對(duì)應(yīng)的inode節(jié)點(diǎn)了。一般文件系統(tǒng)沒有實(shí)現(xiàn)open函數(shù)。所以直接返回inode,建立fd到file到inode的關(guān)系即可。后面對(duì)文件的操作也是通過inode節(jié)點(diǎn)的操作函數(shù)集進(jìn)行。
“基于linux的open原理是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。