by cszhao1980
前面已经说过,inode指向文件的内容是通过i_addr[]数组来组织和记录的。
本章讨论一下文件内容的读写。
在进行文件读写时,使用了进程u空间的若干变量,先介绍如下:
0418: char u_segflg; /* flag for IO; user or kernel space */
0425: char *u_base; /* base address for IO */
0426: char *u_count; /* bytes remaining for IO */
0427: char *u_offset[2]; /* offset in file for IO */
(1) u_segflg: Kernel/user标志, 0—— user; 1——kernel;
(2) u_base: 起始地址
这两个变量用来确定文件内容要读入的内存的起始地址:
(3) u_count:要读入的长度;
(4) u_offset[2]:文件当前offset(即从该offset处开始读写,读写过程会更新该offset);
offset数组使用2个word模拟一个32位数,offset[0]为高16 bit,offset[1]为高16 bit。
由于文件长度可能超出1个word的限制,故在保存长度相关变量时,往往使用2个word来模拟
32 bit数,如u中的u_offset数组,inode中的i_size0和i_size1等等。unix v6还提供了一套“双字”
操作函数,如:
(1) lshift(): 双字移位;
(2) dmadd(): 双字加法;
(3) dmcmp():双字减法;
等等。
现在让我们看看readi(ip) ——该函数用来读取文件内容。read(ip)仅有一个参数,即inode指针,其
他的参数都通过进程u结构传递进来。下面我们来看看这个函数:
(1) 6232 行将i_flag的IACC标志置位,这会导致iupdat函数在更新inode时,会更新的access time;
(2) 6233~6235是对特殊字符设备(IFCHR)的特殊处理,将来会详细介绍;
(3) 6238 ~ 6262行的do while循环是程序的主体,结束循环的情况有:
i. 读到文件结尾;
ii. 读够要求的字节数(u_count);
6239~6240行用于确定要读取的逻辑块和块内偏移;
6243~6244行的dpcmp调用用于获取文件剩余的字节数;
6255~6258行使用块读取函数bread/breada将块读入缓冲区;
6260行:使用iomove函数将缓冲区内有效字符拷贝至用户指定地址。
【注】:对特殊块设备(IFBLK置位)有特殊的处理,在此不讨论。
下面我们看一下iomove函数,它有两种用途:
(1) 读:即将缓冲区内内容移入用户数据区;
(2) 写:即将用户数据区内容移入缓冲区。
它有四个参数:
(1) bp ——缓冲区指针;
(2) o ——块内偏移(读、写的起始地址);
(3) an ——读、写字符数;
(4) flag ——读/写flag(B_READ/B_WRITE)
iomove使用两套函数来完成数据转移工作:
(1) copyin/ copyout;
(2) passc/cpass;
第1套函数只能用于kernel地址与user地址间的数据转移;
第2套函数既可以用于kernel与kernel又可以用于kernel与user间的数据转移。
之所以分两套函数,是因为在执行kernel对kernel数据传递时,第1套函数按word进行传递,
效率较高。但也正因如此,当数据传递中遇到byte疆界时,该套函数就不适用了。
最后,我们看一下writei函数,与readi正好相反,它的目的是将内存内的数据写回磁盘。
writei函数的实现同readi非常相似,比较难理解的是:
6303: if(n == 512)
6304: bp = getblk(dn, bn); else
6305: bp = bread(dn, bn);
6306: iomove(bp, on, n, B_WRITE);
当n为512时,表示要写一整块盘块,因此,盘块原有的内容就不重要了,所以只需调用
getblk获取一块缓冲区,然后将要写的内容拷入缓冲区即可。
博客地址:http://blog.csdn.net/cszhao1980
博客专栏地址:http://blog.csdn.net/column/details/lions-unix.html