【哈工大_操作系统实验】Lab9 proc文件系统的实现

news2024/12/22 12:33:05

本节将更新哈工大《操作系统》课程第九个 Lab 实验 proc文件系统的实现。按照实验书要求,介绍了非常详细的实验操作流程,并提供了超级无敌详细的代码注释

实验目的:

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

实验任务:

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

  • cat:显示文件内容、将文件内容合并输出到终端或其他文件。
  • procfs,它是一个虚拟文件系统,通常被 mount(挂载) 到 /proc 目录上,通过虚拟文件和虚拟目录的方式提供访问系统参数的机会;
  • 这些虚拟的文件和目录并没有真实地存在在磁盘上,而是内核中各种数据的一种直观表示,是完全存在于内存中的。虽然是虚拟的,但它们都可以通过标准的系统调用(open()、read() 等)访问。

实现思路:
Linux 0.11 使用的是 Minix 的文件系统,这是一个典型的基于 inode 的文件系统,《注释》一书对它有详细描述。它的每个文件都要对应至少一个 inode,而 inode 中记录着文件的各种属性,包括文件类型。文件类型有普通文件、目录、字符设备文件和 9b 块设备文件等。在内核中,每种类型的文件都有不同的处理函数与之对应。我们可以增加一种新的文件类型——proc 文件,并在相应的处理函数内实现 procfs 要实现的功能。

一、增加 proc 文件

1、在 include/sys/stat.h 文件定义中新增 proc 文件的 宏定义 和 测试宏:

//proc文件的宏定义/宏函数 
#define S_IFPROC 0030000 
#define S_ISPROC(m) (((m) & S_IFMT) == S_IFPROC) //测试m是否是proc文件

2、修改 fs/namei.c 文件,让mknod() 支持新的文件类型

proc 目录下创建psinfohdinfoinodeinfo等文件,需要调用mknod 函数 -> sys_mknod 函数,则需要让它支持新的文件类型,成功创建inode

int sys_mknod(const char * filename, int mode, int dev)
{
//  ....
	inode->i_mode = mode;
    if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISPROC(mode))
        inode->i_zone[0] = dev;
    inode->i_mtime = inode->i_atime = CURRENT_TIME;

3、修改 init/main.c 文件,创建进程proc文件
procfs 的初始化工作应该在根文件系统挂载之后开始,包括两个步骤:

  1. 建立 /proc 目录,通过用户态调用mkdir() -> sys_mkdir()
  2. 建立 /proc 目录下的各个结点,通过用户态调用mknod() -> sys_mknod()

/* 新增用户态接口 mkdir 和 mknode 系统调用*/
_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);
    (void) open("/dev/tty0",O_RDWR,0);
    (void) dup(0);
    (void) dup(0);

    /* 创建proc目录 和 文件 */
    mkdir("/proc",0755);
    mknod("/proc/psinfo",S_IFPROC|0444,0);
    mknod("/proc/hdinfo",S_IFPROC|0444,1);
    mknod("/proc/inodeinfo",S_IFPROC|0444,2);
//  ....
  • 参数 0755(对应 rwxr-xr-x),表示只允许 root 用户改写此目录
  • 参数 S_IFPROC|0444 做为 mode 值,表示这是一个 proc 文件,权限为 0444(r–r–r–),对所有用户只读。
  • mknod() 的第三个参数 dev 用来说明结点所代表的设备编号。

此时若调用 cat /proc/psinfo 会报错,因为内核在对 psinfo 进行读操作时不能正确处理,所以还要创建 proc_read 函数,并在 sys_read() 中打补丁。

二、实现 proc 文件可读

1、创建 fs/proc.c 文件,实现根据设备编号,把不同的内容写入到用户空间的 buf,使得 cat 指令可以正确获取 proc 文件内容。

#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/segment.h>
#include <linux/fs.h>
#include <stdarg.h>
#include <unistd.h>

// 内联汇编定义一个宏,用于在位图中测试特定位是否被设置
#define set_bit(bitnr,addr) ({ \
register int __res ; \
__asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \
__res; })

// 定义一个缓冲区,用于存储进程、硬盘或 inode 信息,大小为 4096 字节
char proc_buf[4096] ={'\0'};

// 声明一个外部函数 vsprintf,用于将格式化输出写入字符串
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;
}

// 获取进程信息函数,并将其格式化写入 proc_buf
int get_psinfo()
{
    int read = 0;
    read += sprintf(proc_buf+read,"%s","pid\tstate\tfather\tcounter\tstart_time\n");
    struct task_struct **p;
    // 遍历所有进程
    for(p = &FIRST_TASK ; p <= &LAST_TASK ; ++p)
     if (*p != NULL)
     {
	     // 进程ID 进程状态 父进程ID 时间片 启动时间
         read += sprintf(proc_buf+read,"%d\t",(*p)->pid);
         read += sprintf(proc_buf+read,"%d\t",(*p)->state);
         read += sprintf(proc_buf+read,"%d\t",(*p)->father);
         read += sprintf(proc_buf+read,"%d\t",(*p)->counter);
         read += sprintf(proc_buf+read,"%d\n",(*p)->start_time);
     }
     return read;
}

// 获取硬盘信息(参考fs/super.c mount_root()函数)
int get_hdinfo()
{
    int read = 0;
    int i,used;
    struct super_block * sb;
    sb=get_super(0x301);  /*磁盘设备号 3*256+1*/
    /*Blocks信息:总块数、已用块数、空闲块数*/ 
    read += sprintf(proc_buf+read,"Total blocks:%d\n",sb->s_nzones);
    used = 0;
    i=sb->s_nzones;
    while(--i >= 0)
    {
        if(set_bit(i&8191,sb->s_zmap[i>>13]->b_data))
            used++;
    }
    read += sprintf(proc_buf+read,"Used blocks:%d\n",used);
    read += sprintf(proc_buf+read,"Free blocks:%d\n",sb->s_nzones-used);
    /*Inodes 信息:inode数量、已用/空闲inode数量*/
    read += sprintf(proc_buf+read,"Total inodes:%d\n",sb->s_ninodes);
    used = 0;
    i=sb->s_ninodes+1;
    while(--i >= 0)
    {
        if(set_bit(i&8191,sb->s_imap[i>>13]->b_data))
            used++;
    }
    read += sprintf(proc_buf+read,"Used inodes:%d\n",used);
    read += sprintf(proc_buf+read,"Free inodes:%d\n",sb->s_ninodes-used);
     return read;
}

// 获取inode信息,inode 号和第一个数据区块号
int get_inodeinfo()
{
    int read = 0;
    int i;
    struct super_block * sb;
    struct m_inode *mi;
    sb=get_super(0x301);  /*磁盘设备号 3*256+1*/
    i=sb->s_ninodes+1;
    i=0;
    while(++i < sb->s_ninodes+1)
    {
        if(set_bit(i&8191,sb->s_imap[i>>13]->b_data))
        {
            mi = iget(0x301,i);
            read += sprintf(proc_buf+read,"inr:%d;zone[0]:%d\n",mi->i_num,mi->i_zone[0]);
            iput(mi);
        }
        if(read >= 4000) 
        {
            break;
        }
    }
     return read;
}

// proc文件读取函数,根据设备号决定读取信息,并将读取的信息复制到用户空间的缓冲区
int proc_read(int dev, unsigned long * pos, char * buf, int count)
{
    
     int i;
    if(*pos % 1024 == 0)
    {
        if(dev == 0)
            get_psinfo();
        if(dev == 1)
            get_hdinfo();
        if(dev == 2)
            get_inodeinfo();
    }
     for(i=0;i<count;i++)
     {
         if(proc_buf[i+ *pos ] == '\0')  
          break; 
         put_fs_byte(proc_buf[i+ *pos],buf + i+ *pos);
     }
     *pos += i;
     return i;
}

2、修改 fs/Makefile 文件中的编译规则:

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
	
//......
### Dependencies:
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

3、修改 fs/read_write.c 文件,在 sys_read 中添加对 proc 文件的处理函数补丁,这样cat指令才能成功读到内容。

/*新增proc_read函数外部调用*/
extern int proc_read(int dev,char* buf,int count,unsigned long *pos);

int sys_read(unsigned int fd,char * buf,int count)
{
//  ....
    if (inode->i_pipe)
        return (file->f_mode&1)?read_pipe(inode,buf,count):-EIO;

    /*新增proc_read调用*/
    if (S_ISPROC(inode->i_mode))
        return proc_read(inode->i_zone[0],&file->f_pos,buf,count);
        
//  ....
}
  • inode->i_zone[0],这就是 mknod() 时指定的 dev :设备编号
  • &file->f_pos,f_pos 是上一次读文件结束时“文件位置指针”的指向
  • buf,指向用户空间,就是 read() 的第二个参数,用来接收数据
  • count,就是 read() 的第三个参数,说明 buf 指向的缓冲区大小

三、编译并运行

1、编译并运行

cd oslab_Lab8/linux-0.11
make all
../run

2、测试结果

在 Bochs 中进行测试:

cat /proc/psinfo
cat /proc/hdinfo
cat /proc/inodeinfo

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2224558.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++开篇】

首先初阶的数据结构相信大家已经学习的差不多了&#xff0c;关于初阶数据结构排序的相关内容的总结随后我也会给大家分享出来。C语言和C有许多相同的地方&#xff0c;但也有许多不相同的地方。接下来的C部分&#xff0c;我们主要是针对C与C语言不同的地方来与大家进行分享。其中…

量子变分算法 (python qiskit)

背景 变分量子算法是用于观察嘈杂的近期设备上的量子计算效用的有前途的候选混合算法。变分算法的特点是使用经典优化算法迭代更新参数化试验解决方案或“拟设”。这些方法中最重要的是变分量子特征求解器 (VQE)&#xff0c;它旨在求解给定汉密尔顿量的基态&#xff0c;该汉密尔…

这是一篇vue3 的详细教程

Vue 3 详细教程 一、Vue 3 简介 Vue.js 是一款流行的 JavaScript 前端框架&#xff0c;用于构建用户界面。Vue 3 是其最新版本&#xff0c;带来了许多新特性和性能优化&#xff0c;使开发更加高效和灵活。 二、环境搭建 安装 Node.js 前往Node.js 官方网站下载并安装适合你…

WPF+MVVM案例实战(六)- 自定义分页控件实现

文章目录 1、项目准备2、功能实现1、分页控件 DataPager 实现2、分页控件数据模型与查询行为3、数据界面实现 3、运行效果4、源代码获取 1、项目准备 打开项目 Wpf_Examples&#xff0c;新建 PageBarWindow.xaml 界面、PageBarViewModel.cs ,在用户控件库 UserControlLib中创建…

WASM 使用说明23事(RUST实现)

文章目录 1. wasm是什么1.1 chatgpt定义如下:1.2 wasm关键特性&#xff1a; 2. wasm demo2.1 cargo 创建项目2.2 编写code2.3 安装wasm-pack2.4 编译 3.1 html页面引用wasm代码&#xff08;js引用&#xff09;3.2 访问页面4 导入js function4.1 编写lib.rs文件&#xff0c;内容…

UML 总结(基于《标准建模语言UML教程》)

定义 UML 又称为统一建模语言或标准建模语言&#xff0c;是一种标准的图形化建模语言&#xff0c;它是面向对象分析与设计的一种标准表示。尽管UML 本身没有对过程有任何定义&#xff0c;但UML 对任何使用它的方法&#xff08;或过程&#xff09;提出的要求是&#xff1a;支持用…

【含开题报告+文档+PPT+源码】基于vue框架的东升餐饮点餐管理平台的设计与实现

开题报告 在当前信息化社会背景下&#xff0c;餐饮行业正经历着由传统线下服务模式向线上线下深度融合的转变。随着移动互联网技术及大数据应用的飞速发展&#xff0c;用户对于餐饮服务平台的需求也日益多元化和个性化。他们期望能在一个集便捷、高效、个性化于一体的平台上完…

自动化测试工具Ranorex Studio(十六)-添加新Action

在Action表中&#xff0c;有两种手动添加action的方式。 一种方法是指定Action本身&#xff08;’添加新Action’&#xff09;&#xff0c;然后分配对应的对象库条目&#xff08;在多数情况下&#xff09;。 第二种方法是直接把对象库条目拖放到Action表内&#xff0c;然后生成…

力扣 中等 2466.统计构造好字符串的方案数

文章目录 题目介绍题解 题目介绍 题解 题意&#xff1a;每次可以爬 zero 或 one 个台阶&#xff0c;返回爬 low 到 high 个台阶的方案数。 和上题337.组合总和 &#xff08;链接&#xff09;的思路一样&#xff0c;只不过是将可以爬的台阶数从数组换成了两个数&#xff08;ze…

视频美颜平台的搭建指南:基于直播美颜SDK的完整解决方案

众所周知&#xff0c;直播美颜SDK是实现视频美颜功能的核心。本文将详细解析如何基于直播美颜SDK搭建一个完整的视频美颜平台。 一、视频美颜SDK的核心功能 直播美颜SDK作为平台的技术核心&#xff0c;能够提供丰富的美颜效果和稳定的视频处理能力。通常&#xff0c;SDK具备以…

iOS AVAudioSession 详解【音乐播放器的配置】

前言 在 iOS 音频开发中&#xff0c;AVAudioSession 是至关重要的工具&#xff0c;它控制着应用的音频行为&#xff0c;包括播放、录音、后台支持和音频中断处理等。对于音乐播放器等音频需求强烈的应用&#xff0c;设计一个合理的 AVAudioSession 管理体系不仅能保证音频播放…

RabbitMQ是一个开源的消息代理和队列服务器

RabbitMQ是一个开源的消息代理和队列服务器&#xff0c;它基于AMQP&#xff08;Advanced Message Queuing Protocol&#xff0c;高级消息队列协议&#xff09;协议实现&#xff0c;同时也支持其他消息协议如STOMP、MQTT等。作为一个可靠的消息传递服务&#xff0c;RabbitMQ在分…

青少年CTF平台的基础题writeup

青少年CTF平台 1、文章管理系统 首先他这里有一个问号id&#xff0c;就想着使用mysql跑一下&#xff0c;但是windows的sqlmap很不给力&#xff0c;都不动&#xff0c;所以我后面换成了kali机来跑 跑一下就跑出了好多的数据库 第一次我使用ctftraining不出货&#xff0c;跑的一…

Coppelia Sim (v-REP)仿真 机器人3D相机手眼标定与实时视觉追踪 (一)

coppelia sim[V-REP]仿真实现 机器人于3D相机手眼标定与实时视觉追踪 一 标定板的制作生成标定的PDF文件PDF转为图像格式图像加载到仿真中 二 仿真场景设置加载机器人加载的控制dummy ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b48549d355d8441d8dfc20bc7ba7196…

【K8S系列】Kubernetes Pod节点CrashLoopBackOff 状态及解决方案详解【已解决】

在 Kubernetes 中&#xff0c;Pod 的状态为 CrashLoopBackOff 表示某个容器在启动后崩溃&#xff0c;Kubernetes 尝试重启该容器&#xff0c;但由于持续崩溃&#xff0c;重启的间隔时间逐渐增加。下面将详细介绍 CrashLoopBackOff 状态的原因、解决方案及相关命令的输出解释。 …

.NET Core WebApi第1讲(概念):Web基础、AJAX、JQuery

动态页面&#xff1a;数据流动 / Web服务器 / Ajax / 前后端分离 / restful风格源栈课堂一起帮https://17bang.ren/Code/261 一、Web基础 二、AJAX诞生 三、JQuery

ctfshow(262,264)--反序列化漏洞--字符串逃逸

Web262 源代码&#xff1a; index.php: error_reporting(0); class message{public $from;public $msg;public $to;public $tokenuser;public function __construct($f,$m,$t){$this->from $f;$this->msg $m;$this->to $t;} }$f $_GET[f]; $m $_GET[m]; $t $_…

虚拟现实新纪元:VR/AR技术将如何改变娱乐与教育

内容概要 在当今科技飞速发展的时代&#xff0c;虚拟现实&#xff08;VR&#xff09;和增强现实&#xff08;AR&#xff09;技术不仅让我们的娱乐体验如虎添翼&#xff0c;更为教育变革注入了新活力。这些技术的飞跃进展&#xff0c;将原本平淡无奇的场景转变为令人沉醉的沉浸…

HICP--2

在area 0的路由器只生成 area 0 的数据库&#xff0c;只在area 1 的一样。但是既在又在的生成两个 area的 LSDB 一、区域间三类LSA 在OSPF&#xff08;Open Shortest Path First&#xff09;协议中&#xff0c;区域间三类LSA&#xff08;Link-State Advertisement&#xff09…

【UE5】将2D切片图渲染为体积纹理,最终实现使用RT实时绘制体积纹理【第七篇-体积纹理绘制】

我们前几篇已经完成了渲染部分&#xff0c;现在终于开始做动态绘制功能了 之前使用的是这样一个体积雾的切片图&#xff0c;那么现在要做的就是动态编辑它 首先&#xff0c;让我们简单了解一下它是如何运作的&#xff1a; 开始绘制画布以渲染目标&#xff0c;并将材质绘制到画…