0%

Linux块设备读写过程

先来想下,块设备涉及到什么,块设备属于IO设备,涉及到读写,与字符流设备不同的点就在于能够根据块来进行读写,仅取自己想要的块就OK,我们忽略块设备和字符流设备的区别,那么块设备作为IO设备,最重要的作用就是IO,即读写数据。体现到我们平常的使用则是read write,这些。学习块设备主要要学习的就两步,一是对于linux来说一切皆文件,那么一个文件的路径,是如何映射到块设备对应的块的,二是块设备上的数据是怎么与内存交互的,即怎么读进来的。

从源码开始,分析linux0.11的源码,从中梳理过程,带着上面我们提到的两个疑问,开始阅读代码

open开始

open都清楚,打开一个文件,那么涉及到的就是文件与IO设备的映射关系。我们从fs/open.c的sys_open函数说起:

fs/open.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* filename: 文件路径
* flag:打开文件的标志,只读,只写,读写,创建
* mode:用于指定文件的许可属性,在创建时需要,指定文件权限。
*/
int sys_open(const char * filename, int flag, int mode) {

//...省略一些代码
/**
* 查找空闲文件句柄
* filp的定义是*struct file * filp[NR_OPEN]*,是一个文件指针数组,保存着当前打开的所有文件的指针,linux0.11最大允许同时打开64个文件
**/
for(fd=0 ; fd<NR_OPEN ; fd++)
if (!current->filp[fd])
break;
if (fd>=NR_OPEN)
return -EINVAL;

/**
* close_on_exec,服务于execve函数,当使用execve时,会检查当前进程的close_on_exec位图,将对应位置为1的文件句柄做关闭处理
* 在使用fork创建子进程时,我们希望保留原有文件句柄,所以默认情况下会把close_on_exec位图对应的位复位,当确实需要在execve后删除句柄,就自己去置位就可以了。
*
*
**/
current->close_on_exec &= ~(1<<fd);

/**
* file_table定义:struct file file_table[NR_FILE],即存放file的数组,最终进程->filp[fd]会指向file_table的空闲项
**/
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++;

/**
*
* 重点放到open_namei
* int open_namei(const char * pathname, int flag, int mode, struct m_inode ** res_inode)
* 返回值是一个状态码,表达本次函数操作是否失败,小于0即失败,等于0即成功
* 成功后获得的信息存放在res_inode中
* m_inode:i节点,保存着当前文件的各种信息,同时也有与块设备存放数据对应块的索引
*
* 接下来我们跳转到open_namei去看
*
**/
if ((i=open_namei(filename,flag,mode,&inode))<0) {
current->filp[fd]=NULL;
f->f_count=0;
return i;
}
}
fs/namei.c
1
2
3
4
5
6
7
8

int open_namei(const char * pathname, int flag, int mode, struct m_inode ** res_inode) {
//...
// 通过dir_namei获取了什么呢
// 举个例子:比如pathname:/home/litao/todo.txt,那么返回的basename即为todo.txt
if (!(dir = dir_namei(pathname,&namelen,&basename)))
return -ENOENT;
}
dir_namei
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

-------- 本文结束 感谢阅读 --------