实验8:Proc文件系统的实现
本章为 哈工大操作系统实验8 的实验过程记录。本次实验不算太难,本章将按照 linux0.11 读 proc 文件的顺序介绍实验过程。本次实验所对应的课程笔记链接如下:
1、生磁盘的使用
2、通过文件使用磁盘
3、目录与文件系统
关于实验内容和实验提示请参考:哈工大操作系统实验手册
1 创建 proc 文件
本次实验需要创建三个文件:/proc/psinfo
,/proc/hdinfo
,/proc/inodeinfo
。Linux0.11 中没有实现 proc 文件系统,因此先让 Linux0.11 支持读取 proc 文件,对 include/sys/stat.h
进行如下修改:
#define S_ISGID 0002000
#define S_ISVTX 0001000
//添加 proc 类型文件
#define S_IFPROC 0030000 //
#define S_ISPROC(m) (((m) & S_IFMT) == S_IFPROC) //
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
然后创建/proc/psinfo
,/proc/hdinfo
,/proc/inodeinfo
三个文件,这三个文件不在磁盘中,而是在内存中,因此只需要先将文件的目录项和 inode 创建后就行,具体文件的内容在读取文件时再在内存中创建。这三个文件在系统初始化的时候创建,对 init/main.c
进行如下修改:
......
static inline _syscall1(int,setup,void *,BIOS)
static inline _syscall0(int,sync)
/*新增两个系统调用 mkdir 和 mknod */
_syscall2(int,mkdir,const char*,name,mode_t,mode)
_syscall3(int,mknod,const char*,filename,mode_t,mode,dev_t,dev)
......
void init(void)
{
int pid,i;
setup((void *) &drive_info);
mkdir("/proc", 0755);/*创建 /proc 目录 */
mknod("/proc/psinfo", S_IFPROC|0444, 0);
mknod("/proc/hdinfo", S_IFPROC|0444, 1);
mknod("/proc/inodeinfo", S_IFPROC|0444, 2);
......
}
......
2 读 proc 文件
本实验需要用 cat 命令来显示 proc 文件的内容,cat 核心实现大体如下:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
char buf[513] = {'\0'};
int nread;
int fd = open(argv[1], O_RDONLY, 0);
//可以看出:一次只读取 512 个字节,不断读取文件,直到读完文件
while(nread = read(fd, buf, 512))
{
buf[nread] = '\0';
puts(buf);
}
return 0;
}
cat 会调用 read() 读取文件,而 read() 的是通过 sys_read() 实现其功能的:
......
extern int proc_read(int dev, char * buf, int count, off_t *pos); /*添加函数声明*/
......
int sys_read(unsigned int fd,char * buf,int count)
{
struct file * file;
struct m_inode * inode;
if (fd>=NR_OPEN || count<0 || !(file=current->filp[fd]))
return -EINVAL;
if (!count)
return 0;
verify_area(buf,count);
inode = file->f_inode;
if (inode->i_pipe)
return (file->f_mode&1)?read_pipe(inode,buf,count):-EIO;
if (S_ISCHR(inode->i_mode))
return rw_char(READ,inode->i_zone[0],buf,count,&file->f_pos);
if (S_ISBLK(inode->i_mode))
return block_read(inode->i_zone[0],&file->f_pos,buf,count);
if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {
if (count+file->f_pos > inode->i_size)
count = inode->i_size - file->f_pos;
if (count<=0)
return 0;
return file_read(inode,file,buf,count);
}
if(S_ISPROC(inode->i_mode)) //添加内容。这个 if 语句是为了支持读取 proc 文件
{
return proc_read(inode->i_zone[0], buf, count, &file->f_pos);
}
printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode);
return -EINVAL;
}
最后看一下 proc_read() 的实现代码。为了偷懒,本文将 proc_read() 函数写在了 fs/file_dev.c
文件中:
......
#include <../include/stdarg.h> //添加头文件
......
/*====================================================================*/
extern int vsprintf(char * buf, const char * fmt, va_list args); //函数声明
int sprintf(char *buf, const char *fmt, ...)
{
va_list args; int i;
va_start(args, fmt);
i=vsprintf(buf, fmt, args);
va_end(args);
return i;
}
/**************************************************************
* 函数名:psinfo_read()
* 描述:当读取 psinfo 文件的内容时,可得到系统当前所有进程的状态信息
* 返回值:返回本次读取文件的字节数
***************************************************************/
int psinfo_read(char * buf, int count, off_t *pos)
{
struct task_struct ** p; //指向 task[]
int i = 0; //循环变量
int num = 0; //本次读取文件的字节数
char psinfo[1024]; //psinfo 文件
i += sprintf(psinfo, "pid state father counter start_time\n");//psinfo 文件的列标题
for(p = &LAST_TASK ; (p >= &FIRST_TASK) && (i < 1000); --p)
{ //遍历所有的进程,更新 psinfo 文件内容
//这里是每次调用都会更新文件内容,应该要限制只有在 (*pos) == 0 的时候更新文件内容
if(!(*p)) continue;
//需要获取 pid, state, father, counter, start_time 参数
i += sprintf(psinfo + i, "%d, %d, %d, %d, %d\n", (*p)->pid, (*p)->state, (*p)->father, (*p)->counter, (*p)->start_time);
psinfo[i] = '\0';
}
for(i = (*pos); (i < (*pos) + count) && (psinfo[i] != '\0'); i++)
{ //read并不是一次将文件读完,而是一次都512B(count = 512),
// 若没有完就要接着从上次读到的位置接着读
put_fs_byte(psinfo[i], buf++);
(*pos)++; //
num++;
}
return(num);
}
/**************************************************************
* 函数名:hdinfo_read()
* 描述:当读取 hdinfo 文件的内容时,可得到当前磁盘的使用情况
* 返回值:返回本次读取文件的字节数
***************************************************************/
int hdinfo_read(char * buf, int count, off_t *pos)
{
char hdinfo[1024]; //hdinfo文件
struct super_block *sb = NULL; //超级块指针
struct buffer_head *bh = NULL; //逻辑块位图缓冲
char *data; //指向位图缓冲的数据区(1024个字节)
unsigned short total_blocks = 0; //总块数
unsigned short used_blocks = 0; //已使用的块数
unsigned short free_blocks = 0; //未使用的块数
int i = 0, j = 0, k = 0; //循环变量
int num = 0; //本次读取文件的字节数
sb = get_super(current->root->i_dev);
total_blocks = sb->s_nzones;
for(i = 0; i < sb->s_zmap_blocks; i++)
{ //sb->s_zmap_blocks - 位逻辑块位图所占用的盘块数
bh = sb->s_zmap[i];
data = bh->b_data;
for(j = 0; j < 1024; j++)
{ //一个盘块的大小为 1024B
for(k = 0; k < 8; k++)
{
if( ((*(data + j)) >> k) & 0x0001) used_blocks++;
}
}
}
free_blocks = total_blocks - used_blocks;
i = sprintf(hdinfo, " total_blocks : %d;\n free_blocks : %d;\n used_blocks : %d;\n total_inodes : %d;\n"
, total_blocks, free_blocks, used_blocks, sb->s_ninodes);
hdinfo[i] = '\0';
for(i = (*pos); (i < (*pos) + count) && (hdinfo[i] != '\0'); i++)
{
put_fs_byte(hdinfo[i], buf++);
(*pos)++; //
num++;
}
return num;
}
/**************************************************************
* 函数名:inodeinfo_read()
* 描述:当读取 inodeinfo 文件的内容时,可得到当前系统中 inode 的使用情况
* 返回值:返回本次读取文件的字节数
***************************************************************/
int inodeinfo_read(char * buf, int count, off_t *pos)
{
char inodeinfo[1024]; //inodeinfo 文件
unsigned short total_inodes = 0; //inode 数
unsigned short used_inodes = 0; //已使用的 inode 数
unsigned short free_inodes = 0; //未使用的 inode 数
struct super_block *sb = NULL; //超级块指针
char *data; //指向位图缓冲的数据区(1024个字节)
int i = 0, j = 0, k = 0; //循环变量
int num = 0; //本次读取文件的字节数
sb = get_super(current->root->i_dev);
total_inodes = sb->s_ninodes;
for(i = 0; i < sb->s_imap_blocks; i++)
{ //sb->s_imap_blocks - inode 位图所占用的盘块数
data = sb->s_imap[i]->b_data;
for(j = 0; j < 1024; j++)
{ //一个盘块的大小为 1024B
for(k = 0; k < 8; k++)
{
if( ((*(data + j)) >> k) & 0x0001) used_inodes++;
}
}
}
free_inodes = total_inodes - used_inodes;
i = sprintf(inodeinfo, " total_inodes : %d;\n free_inodes : %d;\n used_inodes : %d;\n"
, total_inodes, free_inodes, used_inodes);
inodeinfo[i] = '\0';
for(i = (*pos); (i < (*pos) + count) && (inodeinfo[i] != '\0'); i++)
{
put_fs_byte(inodeinfo[i], buf++);
(*pos)++; //
num++;
}
return num;
}
/**************************************************************
* 函数名:proc_read()
* 描述:读取 proc 类型文件
* 返回值:返回本次读取文件的字节数
***************************************************************/
int proc_read(int dev, char * buf, int count, off_t *pos)
{
int num = 0;
switch(dev)
{
case 0: num = psinfo_read(buf, count, pos); // psinfo 文件
break;
case 1: num = hdinfo_read(buf, count, pos); // hdinfo 文件
break;
case 2: num = inodeinfo_read(buf, count, pos);// inodeinfo 文件
break;
default:num = -1; break; //设备号错误
}
return num;
}
3 实验结果
实验结果如下:
4 参考
1、《Linux内核完全剖析——基于0.12内核》
2、HIT oslab之实验9 proc文件系统的实现
3、4.7 Proc文件系统的实现