Linux0.11 文件打开open函数(五)
系列文章目录
Linux 0.11启动过程分析(一)
Linux 0.11 fork 函数(二)
Linux0.11 缺页处理(三)
Linux0.11 根文件系统挂载(四)
Linux0.11 文件打开open函数(五)
Linux0.11 execve函数(六)
Linux0.11 80X86知识(七)
Linux0.11 内核体系结构(八)
Linux0.11 系统调用进程创建与执行(九)
Linux0.11 进程切换(十)
Linux0.11 管道(十一)
Linux0.11 信号(十二)
文章目录
- 系列文章目录
- 前言
- 一、由来
- 1、open函数
- 二、sys_open 函数
- 1、open_namei 函数
- dir_namei 函数
- get_dir 函数
- find_entry 函数
前言
文件系统根目录下的所有文件名信息则保持在指定 i 节点(即1号节点)的数据块中。每个目录项只包括一个长度为 14 字节的文件名字字符串和该文件名对应的 2 字节的 i 节点号。有关文件的其它信息则被保存在该 i 节点号指定的 i 节点结构中,该结构中主要包括文件访问属性、宿主、长度、访问保存时间以及所在磁盘块等信息。
#define NAME_LEN 14 // 名字长度值
#define ROOT_INO 1 // 根 i 节点struct dir_entry {unsigned short inode; // i 节点号(0.11版本可理解为存储序号)char name[NAME_LEN]; // 文件名
};
在打开一个文件时,文件系统会根据给定的文件名找到其 i 节点号,从而找到文件所在的磁盘块位置。例如对于要查找文件名 /usr/bin/vi 的 i 节点号,文件系统首先会从具有固定 i 节点号(1)的根目录开始操作,即从 i 节点号 1 的数据块中查找到名称为 usr 的目录项,从而得到文件 /usr 的 i 节点号。根据该 i 节点号文件系统可以顺利地取得目录 /usr,并在其中可以查找到文件名 bin 的目录项。这样也就知道了 /usr/bin 的 i 节点号,因而我们可以知道目录 /usr/bin 的目录所在位置,并在该目录中查找到 vi 文件的目录项。最终我们获得了文件路径名 /usr/bin/vi 的 i 节点号,从而可以从磁盘上得到该 i 节点号的 i 节点结构信息。
一、由来
在文件 init/main.c 的 init 函数中调用了 open 函数:
void init(void) {// ... (void)open("/dev/tty0", O_RDWR, 0);(void)dup(0);(void)dup(0);// ...
}
接下来我们分析下其调用过程。
1、open函数
// lib/open.c
int open(const char * filename, int flag, ...)
{register int res;va_list arg;va_start(arg,flag);__asm__("int $0x80":"=a" (res):"0" (__NR_open),"b" (filename),"c" (flag),"d" (va_arg(arg,int)));if (res>=0)return res;errno = -res;return -1;
}
从上面可知,这是一个系统调用,其中断响应为 kernel/system_call.s 文件中 system_call 函数,最终调用 sys_open 函数(具体可参考 Linux 0.11 fork 函数(二))。
二、sys_open 函数
函数位于文件 fs/open.c 中。
// fs/open.c
int sys_open(const char * filename,int flag,int mode)
{struct m_inode * inode;struct file * f;int i,fd;mode &= 0777 & ~current->umask;for(fd=0 ; fd<NR_OPEN ; fd++)if (!current->filp[fd])break;if (fd>=NR_OPEN)return -EINVAL;current->close_on_exec &= ~(1<<fd);f=0+file_table;for (i=0 ; i<NR_FILE ; i++,f++)if (!f->f_count) break;if (i>=NR_FILE)return -EINVAL;(current->filp[fd]=f)->f_count++;if ((i=open_namei(filename,flag,mode,&inode))<0) {current->filp[fd]=NULL;f->f_count=0;return i;}
/* ttys are somewhat special (ttyxx major==4, tty major==5) */if (S_ISCHR(inode->i_mode)) {if (MAJOR(inode->i_zone[0])==4) {if (current->leader && current->tty<0) {current->tty = MINOR(inode->i_zone[0]);tty_table[current->tty].pgrp = current->pgrp;}} else if (MAJOR(inode->i_zone[0])==5)if (current->tty<0) {iput(inode);current->filp[fd]=NULL;f->f_count=0;return -EPERM;}}
/* Likewise with block-devices: check for floppy_change */if (S_ISBLK(inode->i_mode))check_disk_change(inode->i_zone[0]);f->f_mode = inode->i_mode;f->f_flags = flag;f->f_count = 1;f->f_inode = inode;f->f_pos = 0;return (fd);
}
1、open_namei 函数
函数位于文件 fs/namei.c 中。
// fs/namei.c
// 文件打开函数
// pathname是文件名,flag是打开文件标志,可为:O_RDONLY(只读)、O_WRONLY(只写)或
// O_RDWR(读写),以及O_CREAT(创建)、O_EXCL(被创建文件必须不存在)、O_APPEND(在文件尾添加数据)
// 等其他一些标志的组合。
// 成功返回0,否则返回出错码,res_node返回对应文件路径名的i节点指针
int open_namei(const char * pathname, int flag, int mode,struct m_inode ** res_inode)
{const char * basename;int inr,dev,namelen;struct m_inode * dir, *inode;struct buffer_head * bh;struct dir_entry * de;if ((flag & O_TRUNC) && !(flag & O_ACCMODE))flag |= O_WRONLY;mode &= 0777 & ~current->umask;mode |= I_REGULAR;if (!(dir = dir_namei(pathname,&namelen,&basename)))return -ENOENT;if (!namelen) { /* special case: '/usr/' etc */if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {*res_inode=dir;return 0;}iput(dir);return -EISDIR;}bh = find_entry(&dir,basename,namelen,&de);if (!bh) {if (!(flag & O_CREAT)) {iput(dir);return -ENOENT;}if (!permission(dir,MAY_WRITE)) {iput(dir);return -EACCES;}inode = new_inode(dir->i_dev);if (!inode) {iput(dir);return -ENOSPC;}inode->i_uid = current->euid;inode->i_mode = mode;inode->i_dirt = 1;bh = add_entry(dir,basename,namelen,&de);if (!bh) {inode->i_nlinks--;iput(inode);iput(dir);return -ENOSPC;}de->inode = inode->i_num;bh->b_dirt = 1;brelse(bh);iput(dir);*res_inode = inode;return 0;}inr = de->inode;dev = dir->i_dev;brelse(bh);iput(dir);if (flag & O_EXCL)return -EEXIST;if (!(inode=iget(dev,inr)))return -EACCES;if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) ||!permission(inode,ACC_MODE(flag))) {iput(inode);return -EPERM;}inode->i_atime = CURRENT_TIME;if (flag & O_TRUNC)truncate(inode);*res_inode = inode;return 0;
}
dir_namei 函数
函数位于文件 fs/namei.c 中。
该函数返回指定目录名的 i 节点指针,以及在最顶层目录的名称。
static struct m_inode * dir_namei(const char * pathname,int * namelen, const char ** name)
{char c;const char * basename;struct m_inode * dir;if (!(dir = get_dir(pathname)))return NULL;basename = pathname;while ((c=get_fs_byte(pathname++)))if (c=='/')basename=pathname;*namelen = pathname-basename-1;*name = basename;return dir;
}
get_dir 函数
函数位于文件 fs/namei.c 中。
该函数根据给出的路径名进行搜索,直到达到最顶端的目录。
static struct m_inode * get_dir(const char * pathname)
{char c;const char * thisname;struct m_inode * inode;struct buffer_head * bh;int namelen,inr,idev;struct dir_entry * de;// 搜索操作会从当前进程任务结构中设置的根(或伪根)i 节点或当前工作目录 i 节点开始。// 因此首先需要判断进程的根 i 节点指针和当前工作目录 i 节点指针是否有效。如果当前进程// 没有设定根 i 节点,或者该进程根 i 节点指向是一个空闲i节点(引用为0),则系统出错停机。// 如果进程的当前工作目录i节点指针为空,或者当前工作目录 i 节点指向是一个空闲i节点,// 这也是系统有问题,停机。if (!current->root || !current->root->i_count)panic("No root inode");if (!current->pwd || !current->pwd->i_count)panic("No cwd inode");// 如果用户指定的路径名的第1个字符是'/',则说明路径是绝对路径名。则从根i节点开始操作。// 否则表示给定的是相对路径名。应从进程的当前工作目录开始操作。则取进程当前工作目录的// i节点。if ((c=get_fs_byte(pathname))=='/') {inode = current->root;pathname++;} else if (c)inode = current->pwd;elsereturn NULL; /* empty name is bad */// 然后针对路径名中的各个目录名部分和文件名进行循环处理。首先把得到的i节点引用计数增1,// 表示我们正在使用。在循环处理过程中,我们先要对当前正在处理的目录名部分(或文件名)的i// 节点进行有效性判断,并且把变量thisname指向当前正在处理的目录名部分(或文件名)。// 如果该i节点不是目录类型的i节点,或者没有可进入该目录的访问许可,则放回该i节点,并返回// NULL退出。当然,刚进入循环时,当前的i节点就是进程根i节点或者是当前工作目录的i节点。// 注意!如果路径名中最后一个名称也是一个目录名,但其后面没有加上'/'字符,则函数不会// 返回该最后目录的i节点!例如:对于路径名/usr/src/linux,该函数将只返回src目录名的i节点。inode->i_count++;while (1) {thisname = pathname;if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {iput(inode);return NULL;}// 每次循环我们处理路径名中一个目录名(或文件名)部分。因此在每次循环中我们都要从路径// 名字符串中分离出一个目录名(或文件名)。方法是从当前路径名指针pathname开始处搜索// 检测字符,直到字符是一个结尾符(NULL)或者是一个'/'字符。此时变量namelen正好是当前// 处理目录名部分的长度,而变量thisname正指向该目录名部分的开始处。此时如果字符是结尾符// NULL,则表明已经搜索到路径名末尾,并已到达最后指定目录名或文件名,则返回该i节点指针退出for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++)/* nothing */ ;if (!c)return inode;// 在得到当前目录名部分(或文件名)后,我们调用查找目录项函数find_entry在当前处理的目录中// 寻找指定名称的目录项。如果没找到,则返回NULL退出。否则在找到的目录项中取出其i节点号inr// 和设备号idev,然后取节点号inr的i节点inode,并以该目录项为当前目录继续循环处理路径名中的// 下一目录名部分(或文件名)if (!(bh = find_entry(&inode,thisname,namelen,&de))) {iput(inode);return NULL;}inr = de->inode;idev = inode->i_dev;brelse(bh);iput(inode);if (!(inode = iget(idev,inr)))return NULL;}
}
find_entry 函数
/* 查找指定目录和文件名的目录项。* dir 指定目录i节点的指针,name 文件名,namelen文件名长度。* 返回:成功则返回函数告诉缓冲区指针,并在*res_dir处返回的目录项结构指针。失败则返回空指针。
*/
static struct buffer_head * find_entry(struct m_inode ** dir,const char * name, int namelen, struct dir_entry ** res_dir)
{//...
}
Linux0.11 文件打开open函数(五)相关推荐
- linux0.11文件分析
在kernel包中有几个重要的文件夹和文件,他们各司其职,处理着有关内核的一些功能操作.其中文件夹有三个:blk_drv(块设备驱动),chr_drv(字符设备驱动),math(数学协处理器) 文件 ...
- linux睡眠当前进程,linux-0.11中进程睡眠函数sleep_on()解析
sleep_on()用于进程睡眠. 其原型为 void sleep_on(struct task_struct **p); 比如某个资源是互斥的,当资源被某一个进程占用时,其他进程便无法访问此资源. ...
- Linux0.11 execve函数(六)
系列文章目录 Linux 0.11启动过程分析(一) Linux 0.11 fork 函数(二) Linux0.11 缺页处理(三) Linux0.11 根文件系统挂载(四) Linux0.11 文件 ...
- 编译linux0.11内核
编译linux0.11内核 一.实验环境 二.下载文件 三.配置Linux0.11所需环境 四.编译内核 五.运行linux0.11 六.说明 1.setup.sh脚本里进行了什么操作? 2.最后弹出 ...
- C语言文件打开方式及说明
ANSI C规定文件打开用函数fopen,关闭为fclose. 1.调用方式通常为: FILE *fp; fp=fopen(文件名, 打开方式); 2.参数说明: 文件名: 形如"myfil ...
- main函数解析(一)——Linux-0.11 学习笔记(五)
main()函数解析(一)--Linux-0.11 学习笔记(五) 经过了前面的各种铺垫,终于来到了main函数.这篇博客的任务是把init/main.c讲清楚.由于牵扯到很多的函数调用,要想一次就说 ...
- main 函数解析(二)—— Linux-0.11 学习笔记(六)
main函数解析(二)--Linux-0.11 学习笔记(六) 4.6 blk_dev_init函数 void blk_dev_init(void) {int i;for (i=0 ; i<NR ...
- Linux0.11 创建进程的过程分析--fork函数的使用
/* * linux/kernel/fork.c * * (C) 1991 Linus Torvalds */ /* 注意:signal.c和fork.c文件的编译选项内不能有vc变量优化选项/Og, ...
- main() 函数解析(一)——Linux-0.11 剖析笔记(六)
文章目录 1. 宏定义`_syscall0` 2. `setup.s`读取的参数 3. 读取CMOS实时时钟信息 3.1 `outb_p(value,port)` 3.2 `inb_p(port)` ...
最新文章
- 关于端口映射的一个命令
- 解读Java 8 中为并发而生的 ConcurrentHashMap
- 通过命令行使用 JAX-WS调用webservice
- 使用ajax实现无刷新邮箱验证码,AJAX和WebService实现邮箱验证(无刷新)
- 1.1.1 从简单的数据类型开始
- html5的网络书店图书网站代码_【技能提升】10个编写HTML5的实用小技巧
- Collecting Bugs POJ - 2096(基础概率dp+期望模板)
- 将PostgreSQL PL / Java安装为PostgreSQL扩展
- 现代程序设计 作业4
- Redis夺命连环11问
- NAT (PAT)地址转换技术(讲解+配置)
- ssis 映射列 使用变量_SSIS中的动态列映射:SqlBulkCopy类与数据流
- c程序设计语言如何补零,C语言程序设计(补)-中国大学mooc-题库零氪
- End-to-End Object Detection with Transformers的部分解读
- Swift 反射 API 及用法
- UE4分支的Git Flow
- 【日本软件外包】设计书中常用到的文型
- 拥塞避免算法、快重传、快恢复、慢启动
- IOS开发 百度语音实现播报及IOS12.1后的播报功能问题与实现
- 软件测试简历中的项目应该如何准备?