实验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 实验结果

实验结果如下:

图3.1 实验结果

图3.1 实验结果

4 参考

1、《Linux内核完全剖析——基于0.12内核》
2、HIT oslab之实验9 proc文件系统的实现
3、4.7 Proc文件系统的实现

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值