/*
* linux/fs/file_dev.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h>
#include <fcntl.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
/* 根据i 节点和文件结构,读设备数据,为上层的read函数提供函数 */
int file_read(struct m_inode * inode, struct file * filp, char * buf, int count)
{
// 由i 节点可以知道设备号,由filp 结构可以知道文件中当前读写指针位置。buf 指定用户态中
// 缓冲区的位置,count 为需要读取的字节数。返回值是实际读取的字节数,或出错号(小于0)
int left,chars,nr;
struct buffer_head * bh;
// 若需要读取的字节计数值小于等于零,则返回
if ((left=count)<=0)
return 0;
while (left)
{
// 根据i 节点和文件表结构信息,取数据块文件当前读写位置在设备上对应的逻辑块号nr。若nr 不
// 为0,则从i 节点指定的设备上读取该逻辑块
if (nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE)) // 得到逻辑块号
{
if (!(bh=bread(inode->i_dev,nr)))
break;
} else
bh = NULL;
// 计算文件读写指针在数据块中的偏移值nr
nr = filp->f_pos % BLOCK_SIZE;
// 然后与还需读取的字节数left 作比较,其中小值即为本次需读的字节数chars
chars = MIN( BLOCK_SIZE-nr , left );
// 调整读写文件指针
filp->f_pos += chars;
// 剩余字节计数相应减去chars
left -= chars;
if (bh) // 如果读出了数据
{
char * p = nr + bh->b_data; // 将p 指向读出数据块缓冲区中开始读取的位置
// 复制chars字节到用户的缓冲区
while (chars-->0)
put_fs_byte(*(p++),buf++);
brelse(bh);
}
// 否则将用户的缓冲区全部设置成0
else
{
/*************************************/
while (chars-->0)
put_fs_byte(0,buf++);
/**************************************/
}
}
// 修改该i 节点的访问时间为当前时间。返回读取的字节数
inode->i_atime = CURRENT_TIME;
return (count-left)?(count-left):-ERROR;
/*
* 该函数实现的是根据i节点和文件的struct file来读取实际设备
* 上的数据到用户的缓冲区。调用的函数bread来读取设备上的数据。
* bread函数的参数设备号是有参数inode得到,而文件的偏移量是
* 由struct file结构中的f_pos计算出来,有这两个参数将设备上的
* 数据得到,然后复制到用户区域中
*/
}
/* 根据i 节点和文件结构信息,将用户数据写入指定设备,只是将数据写入 */
/* 缓冲区,然后由内核在合适的时候将数据刷新到设备文件上 */
int file_write(struct m_inode * inode, struct file * filp, char * buf, int count)
{
off_t pos;
int block,c;
struct buffer_head * bh;
char * p;
int i=0;
/*
* ok, append may not work when many processes are writing at the same time
* but so what. That way leads to madness anyway.
*/
// 如果是要向文件后添加数据,则将文件读写指针移到文件尾部
if (filp->f_flags & O_APPEND)
pos = inode->i_size;
// 否则就将在文件读写指针处写入
else
pos = filp->f_pos;
while (i<count)
{
// 创建数据块号(pos/BLOCK_SIZE)在设备上对应的逻辑块,并返回在设备上的逻辑块号。如果逻辑
// 块号=0,则表示创建失败,退出循环
if (!(block = create_block(inode,pos/BLOCK_SIZE)))
break;
// 根据该逻辑块号读取设备上的相应数据块
if (!(bh=bread(inode->i_dev,block)))
break;
// 求出文件读写指针在数据块中的偏移值c
c = pos % BLOCK_SIZE;
// 将p 指向读出数据块缓冲区中开始读取的位置
p = c + bh->b_data;
// 缓冲区已修改标志
bh->b_dirt = 1;
// 从开始读写位置到块末共可写入c=(BLOCK_SIZE-c)个字节。若c 大于剩余还需写入的字节数
// (count-i),则此次只需再写入c=(count-i)即可
c = BLOCK_SIZE-c;
if (c > count-i) c = count-i;
// 文件读写指针前移此次需写入的字节数。如果当前文件读写指针位置值超过了文件的大小,则
// 修改i 节点中文件大小字段,并置i 节点已修改标志
pos += c;
if (pos > inode->i_size) {
inode->i_size = pos;
inode->i_dirt = 1;
}
// 已写入字节计数累加此次写入的字节数c
i += c;
// 从用户缓冲区buf 中复制c 个字节到高速缓冲区中p指向开始的位置处
/***********************************/
while (c-->0)
*(p++) = get_fs_byte(buf++);
/***********************************/
// 然后释放该缓冲区
brelse(bh);
}
inode->i_mtime = CURRENT_TIME; // // 更改文件修改时间为当前时间
// 如果此次操作不是在文件尾添加数据,则把文件读写指针调整到当前读写位置,并更改i 节点修改
// 时间为当前时间
if (!(filp->f_flags & O_APPEND))
{
filp->f_pos = pos;
inode->i_ctime = CURRENT_TIME;
}
// 返回写入的字节数,若写入字节数为0,则返回出错号-1
return (i?i:-1);
/*
* 该函数实现的是将用户程序区的数据复制到设备文件上。函数的做法是
* 首先建立一个block(调用函数create_block在磁盘上建立一个逻辑块),
* 然后调用函数bread将新申请到的逻辑块读入到缓冲区中,然后将数据
* 写入到该缓冲区中,设置该缓冲区的i_dirt,然后释放该缓冲区,内核在
* 合适的时候将数据刷新到设备上
*/
}