“操作系统实验”实验报告

proc文件系统的实现

实验名称:proc文件系统的实现

实验日期:2021/7/8

班级:物联网194

姓名:杨毅

学号:1930110740

一·实验目的

1.掌握虚拟文件系统的实现原理;

2.实践文件、目录、文件系统等概念。

二·实验环境

硬件环境是 IA-32(x86) 架构的 PC 机(在实验楼的环境中就是右侧的窗口),主要软件环境是 Bochs + gcc + 编辑器 / IDE + 操作系统 + Linux 0.11。

三·实验内容

在 Linux 0.11 上实现 procfs(proc 文件系统)内的 psinfo 结点。当读取此结点的内容时,可得到系统当前所有进程的状态信息。例如,用 cat 命令显示 /proc/psinfo 的内容,可得到:

$ cat /proc/psinfo
pid    state    father    counter    start_time
0    1    -1    0    0
1    1    0    28    1
4    1    1    1    73
3    1    1    27    63
6    0    4    12    817
copy
$ cat /proc/hdinfo
total_blocks:    62000;
free_blocks:    39037;
used_blocks:    22963;
copy

procfs 及其结点要在内核启动时自动创建。

相关功能实现在 fs/proc.c 文件内。

四·实验过程及数据记录

1.配置实验环境

$ tar -zxvf hit-oslab-linux-20110823.tar.gz \
  -C /home/shiyanlou/
copy

图片描述 修改include/sys/stst.h,添加一个定义宏。增加新的类型的方法分两步: (1)定义一个类型宏 S_IFPROC,其值应在 0010000 到 0100000 之间,但后四位八进制数必须是 0(这是 S_IFMT 的限制,分析测试宏可知原因),而且不能和已有的任意一个 S_IFXXX 相同;

(2)定义一个测试宏 S_ISPROC(m),形式仿照其它的 S_ISXXX(m) (注意,C 语言中以 “0” 直接接数字的常数是八进制数。)

#define S_IFPROC 0030000
#define S_ISPROC(m) (((m)&S_IFMT)==S_IFPROC)
copy

图片描述 psinfo 结点要通过 mknod() 系统调用建立,所以要让它支持新的文件类型。 直接修改 fs/namei.c 文件中的 sys_mknod() 函数中的一行代码,如下:

if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISPROC(mode))
     inode->i_zone[0] = dev;
// 文件系统初始化
copy

图片描述 内核初始化的全部工作是在 main() 中完成,而 main() 在最后从内核态切换到用户态,并调用 init()。修改main.c如下:

_syscall2(int,mkdir,const char*,name,mode_t,mode)
_syscall3(int,mknod,const char*,filename,mode_t,mode,dev_t,dev)
```![图片描述](https://dn-simplecloud.shiyanlou.com/courses/uid1682555-20210708-1625748076619)
创建proc目录下的各节点:
copy

mkdir("/proc",0755); mknod("/proc/psinfo",S_IFPROC|0444,0); mknod("/proc/hdinfo",S_IFPROC|0444,1); mknod("/proc/inodeinfo",S_IFPROC|0444,2);

![图片描述](https://dn-simplecloud.shiyanlou.com/courses/uid1682555-20210708-1625748143428)
make all 编译运行:![图片描述](https://dn-simplecloud.shiyanlou.com/courses/uid1682555-20210708-1625748226212)
运行./run:
![图片描述](https://dn-simplecloud.shiyanlou.com/courses/uid1682555-20210708-1625748271238)
.psinfo被正确open()了。接下来需要修改sys_read().首先在/fs/read_write.c中添加声明:
copy

extern int proc_read(int dev,char *but,int count,unsigned long *pos);

在proc_read()添加proc文件
copy

if (S_ISPROC(inode->i_mode)) return proc_read(inode->i_zone[0],buf,count,&file->f_pos);

![图片描述](https://dn-simplecloud.shiyanlou.com/courses/uid1682555-20210708-1625748469947)
make all

创建/fs/proc.c文件实现上述的proc_read()函数,用于读取proc文件内容
copy

include <linux/sched.h>

include <linux/kernel.h>

include <asm/segment.h>

include <stdarg.h>

include <stddef.h>

extern int vsprintf(char * buf, const char * fmt, va_list args);

//Linux0.11没有sprintf(),该函数是用于输出结果到字符串中的,所以就实现一个,这里是通过vsprintf()实现的。 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; }

int proc_read(int dev, char * buf, int count, unsigned long * pos) { struct task_struct ** p; int output_count=0; char * proc_buf=NULL; int file_size=0; int offset=*pos;

struct super_block * sb; 
struct buffer_head * bh;
int total_blocks, total_inodes;
int used_blocks=0, free_blocks=0;
int i,j,k;
char * db=NULL;
unsigned short s_imap_blocks;
unsigned short s_zmap_blocks;

//硬盘总共有多少块(空闲 + 非空闲),有多少inode索引节点等信息都放在super块中。
sb=get_super(current->root->i_dev);
total_blocks = sb->s_nzones;
total_inodes=sb->s_ninodes;
s_imap_blocks = sb->s_imap_blocks;
s_zmap_blocks = sb->s_zmap_blocks;

//psinfo: 对应的就是输出系统此时的全部进程的状态信息
if(dev==0)
{ 
    proc_buf=(char *)malloc(sizeof(char *)*1024);
    file_size=sprintf(proc_buf,"pid\tstate\tfather\tcounter\tstart_time\n");

    //这里借鉴了,进程切换函数schedule()的代码,也就是遍历系统全部的进程。
    for(p = &LAST_TASK ; p >= &FIRST_TASK ; --p)
        if(*p)
            file_size+=sprintf(proc_buf+file_size,"%d\t%d\t%d\t%d\t%d\n",(*p)->pid,(*p)->state,(*p)->father,(*p)->counter,(*p)->start_time);

    *(proc_buf+file_size)='\0';
}

//hdinfo: 打印出硬盘的一些信息,
//s_imap_blocks、ns_zmap_blocks、
//total_blocks、free_blocks、used_blocks、total_inodes
if(dev==1) 
{
    for(i=0;i<sb->s_zmap_blocks;i++)
    {
        bh=sb->s_zmap[i];
        db=(char*)bh->b_data;
        for(j=0;j<1024;j++){
            for(k=1;k<=8;k++){
                    if((used_blocks+free_blocks)>=total_blocks)
                        break;
                    if( *(db+j) & k)
                        used_blocks++;
                    else
                        free_blocks++;
            }
        }
    }

    proc_buf=(char*)malloc(sizeof(char*)*512);
    file_size=sprintf(proc_buf,"s_imap_blocks:%d\ns_zmap_blocks:%d\n",s_imap_blocks,s_zmap_blocks);
    file_size+=sprintf(proc_buf+file_size,"total_blocks:%d\nfree_blcoks:%d\nused_blocks:%d\ntotal_indoes:%d\n",total_blocks,free_blocks,used_blocks,total_inodes);
}

//将proc_buf缓冲区的内容放入文件
while(count>0)  
{
    if(offset>file_size)
        break;
    put_fs_byte(*(proc_buf+offset),buf++);
    offset++;
    output_count++;
    count--;
}

//重置文件的pos位置,也就是指向文件末尾的指针
(*pos)+=output_count; 

free(proc_buf);
return output_count;
copy

}

修改makefile:
copy

OBJS= open.o read_write.o inode.o file_table.o buffer.o super.o
block_dev.o char_dev.o file_dev.o stat.o exec.o pipe.o namei.o
bitmap.o fcntl.o ioctl.o truncate.o proc.o

![图片描述](https://dn-simplecloud.shiyanlou.com/courses/uid1682555-20210708-1625748735804)
copy

proc.o : proc.c ../include/linux/kernel.h ../include/linux/sched.h
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h
../include/linux/mm.h ../include/signal.h ../include/asm/segment.h

编译:![图片描述](https://dn-simplecloud.shiyanlou.com/courses/uid1682555-20210708-1625748867949)
运行,输出cat命令,查看psinfo和hdinfo信息:
copy

cat /proc/psinfo


copy

cat /proc/hdinfo

``` 图片描述

五·实验结果分析 在 Linux 0.11 上实现 procfs(proc 文件系统)内的 psinfo 结点。当读取此结点的内容时,可得到系统当前所有进程的状态信息。

六·实验心得

通过本次实验,我掌握虚拟文件系统的实现原理; 实践文件、目录、文件系统等概念。

七·回答问题

问题1:如果要求你在 psinfo 之外再实现另一个结点,具体内容自选,那么你会实现一个给出什么信息的结点?为什么?

答:内存使用的信息情况,因为内存是整个系统中最重要的资源,系统的运行要靠内存的支持,能及时了解内存的使用情况是有意义的。

问题2:一次 read() 未必能读出所有的数据,需要继续 read(),直到把数据读空为止。而数次 read() 之间,进程的状态可能会发生变化。你认为后几次 read() 传给用户的数据,应该是变化后的,还是变化前的? + 如果是变化后的,那么用户得到的数据衔接部分是否会有混乱?如何防止混乱? + 如果是变化前的,那么该在什么样的情况下更新 psinfo 的内容?

答:后几次read()传递给用户的数据应该是变化前的,因为读完一部分数据之后,之前读取的进程状态信息可能已经发生了改变了,那么读给proc_buf缓冲区的内容还是读之前的,所以可能会导致读到的数据出现混乱。如果要防止混乱发生,可以考虑将取到的进程信息保存在硬盘的一个文件中,读取时只要该文件存在,就从文件中直接读取,而不是每次read都重新生成进程信息,而一次读动作完成后(返回0)就将硬盘上的文件删除。这样下次再时再次生成文件。这样可以保证传递的数据是一致的。

最新评论
暂无评论~