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

proc文件系统的实现

1 实验目的与要求

• 掌握虚拟文件系统的实现原理;
• 实践文件、目录、文件系统等概念。


2 实验环境(软、硬件平台)

  • 蓝桥平台lanqiao.cn

3 实验内容及步骤

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

  1. 增加新文件类型
  2. 让 mknod() 支持新的文件类型
  3. 建立 / proc目录和目录下的结点
  4. 让 proc 文件可读
  5. 在fs目录下创建proc.c文件
  6. 编译内核并运行进行测试

4 实验过程记录

  1. 在 include/sys/stat.h 文件中增加新文件类型和测试宏,增加的内容如下:
    image.png

2.使创建结点的mknod() 函数支持新的文件类型,修改 fs/namei.c 文件中的 sys_mknod() 函数
image.png

3.建立 / proc目录和目录下的结点 建立目录和结点分别需要调用mkdir()和mknod()系统调用。因为初始化时已经在用户态,所以不能直接调用sys_mkdir()和sys_mknod()。必须在初始化代码所在文件中实现这两个系统调用的用户态接口 image.png
接下来即可在init()中创建目录和节点,增加如下代码
image.png

4.修改sys_read()系统调用,使之能够读取proc类型文件。打开文件fs/read_write.c ,在其中的sys_read函数中,增添如下语句
image.png

在文件开头添加全局声明
image.png

在fs目录下创建proc.c文件,实现了vsprintf和proc_read代码如下

#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);
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;

    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: 打印出硬盘信息,
    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

由于添加了一个新文件,所以需要修改fs/Makefile中的编译规则
image.png

然后在dependencies中增加如下代码
image.png

6.编译内核,运行,测试 在linux-0.11的目录下make all然后../run 在模拟器界面输入
cat /proc/psinfo
cat /proc/hdinfo
查看信息:
image.png


5 实验数据分析及小结

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

答:如果再实现另一个结点,我想会选内存使用的信息情况,因为它是外存与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大,能及时了解内存的使用情况是有意义的。

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

答:(1)是变化后,可能会导致信息混乱。(2)如果要防止混乱发生,可以考虑将取到的进程信息保存在硬盘的一个文件中,读取时只要该文件存在,就从文件中直接读取,而不是每次read都重新生成进程信息,而一次读动作完成后(返回0)就将硬盘上的文件删除。这样下次再时再次生成文件。这样可以保证传递的数据是一致的。

最新评论
暂无评论~