http://blog.sina.com.cn/s/blog_61869e800100ek8w.html
读写文件,是作为一个操作系统所提供的最基本接口之一。
我们就从写文件过程:open,write,close这几个接口来说起,描述写文件的那些事儿。
平时,我们做应用程序的时候,常常用到读写文件的函数接口,就拿写文件来说,我们用C/C++编写时,用到了以下的函数接口:
1>
FILE* fopen(const char*
restrict filename,const char* restrict mode);
2> size_t fwrite(const void*
restrict buffer,size_t size,size_t n,FILE * restrict fp);
3> int fclose(FILE * fp) ;
以上这几个函数接口大家都比较熟悉,如果按照这个来分析似乎更加明了。然而,上面的这些接口已经是现代版本的接口,其实现依赖于现在的成熟系统,分析现行
系统的庞大代码我还嫩了点,所以就拿过去版本的linux系统和一些原始接口进行分析吧。(其实大家都知道,现行操作系统内核的代码量已经不是一个人一辈
子能看完的了,我们主要是借鉴linux的系统思想,去作我们自己的嵌入式操作系统)
老版本的接口是这个样子的:
1> int open(const char*
filename,int flag,...) ;
2> int write(int fildes,const
char* buf,off_t count) ;
3> int close(int fildes) ;
这几个接口的声明在头文件中,实现在系统的LIB库文件中,所以使用的时候,我们只需要包含几个相应的头文件,然后使用接口,在编译的时候,编译器把
LIB库文件中的二进制实现链接进去,这样就行了。
当然,仅仅是使用不是本文的目的,我们是要探究的是这个使用的背后是什么,操作系统为我们做了什么。
首先,库文件中的open是怎么实现的呢?
int open(const char * filename,int flag,...){
register int res ;
va_list arg ;
va_start(arg,flag) ;
__asm__("int $0x80"
:"=a"(res)
:""(__NR_open),"b"(filename),"c"(flag),"d"(va_arg(arg,int))
);
if(res>=0)
return res ;
errno = -res ;
return -1 ;
}
库文件中的open函数封装了汇编代码“调用系统
”,这个系
统调用的返回值
通过eax寄存器
传递给了res
,
系统调用的输入参数
分别存放在ebx,ecx,edx寄存器
中。
系统调用是一个中断,是由汇编语言int
中断号
促发,所以好多教材上称其为软中断或软件中断。
系统中断中断发生,cpu停止当前任务的处理,把用户态的五个关键信息保存在内核态栈中,分别是:eflag,ss,esp,eip和cs寄存器,他们记
录着进程用户态的关键信息(恢复用户态运行时用到),把他们压栈到内核栈中。当然,内核栈地址在进程结构信息中早有记录,上边的五个寄存器的用户态信息保存与赋予内核态信息
这个过程是由CPU自动完成的,只要我们
在前边的任务数据结构中设置好了就行。
任务运行在内核态中,这里有一切系统的代码(包括各种中断处理程序和文件系统以及各种设备的驱动程序)。
呃。。。写博客好费时间啊,不过也是个再次详细学习的过程,值了,毕竟能说出来才说明掌握的透彻,不像现在,边写边翻资料。。。吃饭去了,回来再写。。。
呵呵,再次拿起来这个帖子,都过去一周了。接着写,总比玩游戏强。
依据中断向量表的设置,程序运行到软中断处理程序的入口处(此时,用户态的关键信息eflag,ss,esp,eip和cs都已经保存到内核栈中了),在
这里(是用汇编写的)手工压栈保存用户态的其他信息,注意,这里的保存,在中断退出时,还要手工退栈恢复原:
push %ds
push %es
push %fs
pushl %eax
pushl %edx
pushl %ecx
pushl %ebx
movl $0x10
,%edx
#0x10即0001,0
0
00
(绿色
两位是请求特权级,红色
一
位是GDT(0)/LDT(1),蓝色
四位是第几项),这么
说,edx寄存器中放的是GDT表的第0x001,0
+1项
(即第3项,0x0000是第一项),是系统数据段的选择符
。
#把
系统数据段的
选择符
放入ds和es寄存器中,则用到的数据都将是系统数据段(ds指示)中的数据。
mov
%dx,%ds
mov %dx,%es
movl $0x17,
%edx
#0x17即0001,0
1
11
(绿色
两位是请求特权级,红色
一
位是GDT(0)/LDT(1),蓝色
四位是第几项),这么
说,edx寄存器中存放的是LDT表的第
0x001,0
+1
项(即第3项,0x0000是第一项),是用户态数据段的选择符
。
#把
用户态数据段
的选择符
放入fs
寄
存器中,则可以通过fs寄存器来实现从内核态
读/写用户态
的数据(在内核中,有好多这样的操作,诸
如:get_fs_long(),set_fs_long(),get_fs_word(),set_fs_word()等等)。
mov %dx,%fs
......
call
_sys_call_table(,%eax,4)
pushl %eax
#把eax中的返回值压入栈
......
#中断返回时候,手工恢复各寄存器成用户态时的内容
popl %eax
#保存着系统调用的返回值,放入eax,在用户态的库函数open中的返回值就是通过这里的eax传递的。
popl %ebx
popl %ecx
popl %edx
#此时,理论上应该 popl %eax 了,但是。。。
addl
$4,%esp
#放弃 系统中断调用时的 压栈保存的eax(上边红色eax
保
存着软
中断的向量号码)
pop %fs
pop %es
pop %ds
iret
#中断返回
上边的call
_sys_call_table(,%eax,4)就是调用具体的系统调用
中断处理函数,_sys_call_table是定义在其他文件中定义的函数指针
数组
:
fn_ptr
sys_call_table[sys_setup,sys_exit,sys_fork,sys_read,sys_write,sys_open
,......];
fn_ptr :typedef int (*fn_ptr)() ;
sys_open:extern int sys_open() ;
可以看到,sys_open在数组的第六项,这就对应了上边在用户态定义的
#define __NR_open
5
而
extern int sys_open对应的
实现函数在另外的文件fs/open.c
所实现:
int sys_open(const char* filename,int flag,int mode){
struct m_inode * inode
;
struct file * f ;
int i,fd ;
for(fd=0;fd<NR_OPEN;fd++){
if( !current->filp[fd] ) break
;
}
f = 0 + file_table ;
for(i=0;i<NR_FILE;i++,f++){
if(!f->f_count) break ;
}
current->filp[fd] = f ;
open_namei(
filename,flag,mode,&inode
)
;
f->f_mode = inode->i_mode ;
f->f_flags = flag ;
f->f_count = 1 ;
f->f_inode =
inode ;
f->f_pos = 0 ;
return (fd)
;
}
int open_namei(const char* pathname,int flag,int mod,struct m_inode
** res_inode){
struct m_inode * dir,* inode ;
struct buffer_head * bh ;
struct dir_entry * de ;
dir = dir_namei(
pathname,&namelen,&basename,NULL)
;
bh = find_empty(
&dir,basename,namelen,&de)
;
if(!bh){
inode = new_inode(dir->i_dev) ;
inode->i_uid = current->euid ;
inode->i_mode = mode ;
inode->i_dirt = 1 ;
bh = add_entry(dir,basename,namelen,&de) ;
de->inode = inode->i_num ;
de->b_dirt = 1 ;
brelse(bh) ;
iput(dir) ;
* res_inode = inode ;
return 0 ;
}
inr =
de->inode ;
dev =
dir->i_dev ;
brelse(bh)
;
inode = follow_link(dir,iget(dev,inr)) ;
*res_inode = inode ;
return 0 ;
}
呵呵,文件系统是操作系统最复杂的部分,所以代码量也最大,先写到这里,再有空了接着写,总不能不工作,天天写这个东西呀。这是学习,学习就是为了更好的
工作,不工作了哪行。下次接着写,呵呵
分享到:
相关推荐
在文件名称列表中,我们看到有“230530031230.psd”、“鸟瞰素材.psd”和“00鸟瞰配景集.psd”这三个文件。这些文件名可能代表不同的设计主题或者内容分类。例如,“鸟瞰素材.psd”很可能包含了多种鸟瞰图常用的设计...
鸟瞰图各类树木PSD文件
linux 系统架构图 鸟瞰linux的系统架构 我从linux学习书上 剪下来的
首先,3D模型的制作过程涉及到多个技术环节。设计师通常会使用专业的3D建模软件,如Autodesk 3ds Max或Maya,来构建湿地场景的各个元素,包括水面、植物、建筑物、照明设备等。这些元素都需要精确的几何形状和纹理...
在VS2017中,项目包含了解决方案文件,使得用户可以直接编译和运行代码。实验报告则提供了项目实施的详细步骤、结果分析以及可能遇到的问题和解决策略,这对于理解和改进算法非常有帮助。 OpenCV是一个强大的开源...
首先,PSD(Photoshop Document)是Adobe Photoshop的原生文件格式,它保存了图像的所有层、通道、蒙版、颜色模式等详细信息,允许设计师在不破坏原始元素的情况下进行修改和调整。"PSD格式常用树 鸟瞰树素材...
为了解决这个问题,我们需要对鱼眼图像进行校正,将其转换成更接近人眼观察的图像形式,也就是鸟瞰图(或称俯视图)。在这个过程中,我们将使用纯C语言实现,不依赖其他库。 鱼眼图像校正的核心在于找到一个合适的...
2. "max3026.jpg" - 这是一个JPEG图像文件,很可能就是那张新农村合作社路牌景观的鸟瞰图,以2D形式呈现3D模型的渲染结果。"max"可能表示这个图像来源于3ds Max软件,因为它经常用作3D模型渲染输出的文件格式。 3. ...
参考示例 19-1,根据求得的内参实现鸟瞰图(俯视)转换,测试图片见群文件 Learning OpenCV/LearningOpenCV_Code/LearningOpenCV_Code/birdseye 详细介绍参考:...
综上所述,"niaokantu.rar_鸟瞰图"这个压缩包可能包含了一个实现鸟瞰图功能的示例项目,其中niaokantu.txt文件可能是关键的说明或数据文件。实际应用中,这样的功能有助于提升地图应用的用户体验,使用户能快速导航...
在本章《学习OpenCV》中,我们聚焦于一个特定的图像处理技术——鸟瞰图变换,这是一种将高视角图像转换为类似鸟在空中俯视地面的平面视图的技术。OpenCV是一个强大的计算机视觉库,提供了丰富的功能来处理图像和视频...
5. **渲染**:max2568.max可能是原始的3D模型文件,包含所有模型数据、材质、纹理和灯光设置。渲染是将3D模型转化为2D图像的过程,3ds Max内置的高级渲染引擎可以生成高质量的静态图像或动画序列。 6. **后期处理**...
文件“niaokan”可能是项目源码或者相关结果图像,通过阅读和分析这些文件,可以深入了解作者是如何利用OpenCV实现鸟瞰图转换的具体细节。对于学习者而言,结合源码和博客内容,可以更深入地掌握OpenCV在鸟瞰图工程...
IT行业方向鸟瞰图,群里看到的,发上来共享。
"园林景观花草树林透视鸟瞰PSD分层效果图.zip"这个资源就是专门为园林景观设计师准备的一套高清素材,包含了一个完整的园林场景的PSD分层文件。 首先,让我们深入了解一下PSD文件。PSD是Adobe Photoshop的默认文件...
在提供的"(鸟瞰脚本)环视+拖拽平移+远近缩放+视角限制"文件中,可能包含了一个完整的脚本,实现了上述所有功能。这个脚本通常会包含以下核心部分: - 初始化:设置相机的基本参数,如初始位置、旋转等。 - 输入...
很经典的一组鸟瞰人物 ,很难找,适合初学者和ps后期使用
鸟瞰视角提供了一个全局的视野,让玩家能够观察整个游戏场景。本教程将深入讲解如何实现一个具备环视、拖拽平移、远近缩放、范围限制和阻尼感的Unity鸟瞰相机脚本。 首先,我们来讨论"环视"功能。环视允许玩家通过...
这是一个python课程大作业,内容是一个图片查看器,用pyqt5制作桌面端软件,其中的大图查看器支持鸟瞰图的功能。main.py是主窗口运行函数,除此以外还有exe文件可以直接运行,里面还包含5000字的课程报告。
本项目中,通过使用MATLAB进行坐标变换,将来自不同传感器(如相机和毫米波雷达)的数据整合成统一的鸟瞰图,从而实现更有效的路径规划和障碍物检测。 首先,我们来了解一下坐标变换的概念。在自动驾驶系统中,存在...