HDU操作系统课程设计实验二

这篇博客介绍了如何通过Linux内核模块编程获取系统内核线程信息以及构建进程家族树。实验内容包括设计一个模块列出所有内核线程的程序名、PID、状态、优先级和父PID,并实现类似pstree的功能,展示指定PID进程的家族信息。实验中涉及遍历进程链表、父子进程关系和进程状态的输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


这是一个比较简单、容易的实验,前提条件是对Linux内核的父子进程链表遍历、查找、访问等有一定的了解。注意:实验时可能因为模块代码有问题导致模块无法正常加载,被阻塞在hello_init()函数中,以至于模块无法被rmmod命令卸载。重启虚拟机,模块自动卸载。

一、设计目的

Linux提供的模块机制能动态扩充Linux功能而无需重新编译内核,已经广泛应用在Linux内核的许多功能的实现中。在本实验中将学习模块的基本概念、原理及实现技术,然后利用内核模块编程访问进程的基本信息,加深对进程概念的理解,掌握基本的模块编程技术。

二、内容要求

(1)设计一个模块,要求列出系统中所有内核线程的程序名、PID、进程状态、进程优先级、父进程的PID,输出按列对齐。
(2)设计一个带参数的模块,其参数为某个进程的PID号,模块的功能是列出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID号及进程状态,同时实现类似pstree的输出。
(3)请根据自身情况,进一步阅读分析程序中用到的相关内核函数的源码实现。

三、实验内容

实验思路

要求一思路图(图片出处不详)
要求二思路图(图片出处不详)

实验过程

  1. 编写模块代码和Makefile文件。
  2. 使用make命令编译模块代码。
  3. 使用insmodmodprobe命令将需要载入的模块以目标代码的形式加载到内核中,将自动调用init_module宏。
  4. 使用lsmod命令查看已载入系统的模块信息。
  5. 使用modinfo命令查看指定的模块信息。
  6. 使用rmmod命令删除指定的模块。

输出按列对齐输出系统中所有内核线程的程序名、PID、进程状态、进程优先级、父进程的PID

Linux系统中的每个进程都有一个父进程(init进程除外);每个进程还有0个或多个子进程。在进程描述符中parent指针指向其父进程,还有一个名为children的子进程链表(父进程task_struct中的children相当于链表的表头)。
通过for_each_process(task_struct)函数遍历内核进程对齐输出即可。
参考代码(不完整):

static int myallkt_init(void)
{
	struct task_struct *p;
	p = NULL;
	printk(KERN_ALERT"myallkt begin\n%*s程序名%*s进程号%*s进程状态%*s进程优先级%*s父进程进程号\n",14," ",0," ",0," ",0," ",0," ");
	for_each_process(p)
	{
		if(p->mm == NULL)
		{
			printk(KERN_ALERT"%20s %6d %8ld %10d %12d\n",p->comm,p->pid,p->state,p->prio,p->parent->pid);
		}
	}
	printk(KERN_ALERT"\n");
	return 0;
}

参数为某个进程的PID号,类似pstree的输出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID号及进程状态

通过list_for_each(list_head*,task_struct*)函数遍历,task_struct中的children指针指向其某个子进程的进程描述符task_struct中children的地址而非直接指向某个子进程的地址,也就是说子进程链表中存放的仅仅是各个task_struct成员children的地址。
我们可以用list_entry(ptr,type,member)这个宏来获取task_struct成员的地址,ptr是指向该数据中list_head成员的指针,type是节点的类型,member是节点类型中list_head成员的变量名。
然后按照树状打印即可。
安装模块时传入参数:

module_param(pid, int ,0644); // module_param()宏来修饰要传入的参数变量
insmod 模块名.ko 变量名=值 // 加载模块时出入参数的命令

参考代码(不完整):

void show_it_children(struct task_struct *p,char fout1[100],int fl,int nps)
{
    struct task_struct *pchildren[500];
    struct list_head *L;
    int i = 0,npc = 0,ml = 0;
    char out[100];
    char fout2[100];

    list_for_each(L,&p->children){
        pchildren[npc++]=list_entry(L,struct task_struct,sibling);
    }
    
    //输出当前进程信息
    sprintf(out,"─%s(pid:%d,state:%ld)",p->comm,p->pid,p->state);
    ml = strlen(out) - 1;
    if(npc)
    {
        if(npc != 1)
            sprintf(fout2,"%s%s─┬─",fout1,out);
        else 
            sprintf(fout2,"%s%s───",fout1,out);
    }
    else
    {
        printk("%s%s\n",fout1,out);
        return ;
    }    
    //输出子进程信息
    if(nps - 1 > 0)
        sprintf(fout1,"%*s│%*s",fl,"",ml,"");
    else
	sprintf(fout1,"%*s",fl + ml + 2,"");
    for(i = 0;i < npc;i++)
    {
        sprintf(out,"%s(pid:%d,state:%ld)",pchildren[i]->comm,pchildren[i]->pid,pchildren[i]->state);
        if(i)
        {
            if(i != npc - 1)
                printk("%s├─%s\n",fout1,out);
            else
                printk("%s└─%s\n",fout1,out);
        }
        else
        {
            printk("%s%s\n",fout2,out);
        }
    }
}

static int mypetree_init(void)
{
    struct task_struct *p;
    struct task_struct *psibling[100];
    struct list_head *L;
    int i = 0,nps = 0,fl = 0,tps = 0;
    char out[100];
    char fout1[100];

    p=pid_task(find_vpid(pid),PIDTYPE_PID);

    list_for_each(L,&p->parent->children){
        psibling[nps++]=list_entry(L,struct task_struct,sibling);
    }

    //输出父进程信息
    if(p->parent==NULL)
        sprintf(out,"无父进程─");
    else
        sprintf(out,"%s(pid:%d,state:%ld)─",p->parent->comm,p->parent->pid,p->parent->state);
    fl = strlen(out) - 2;
    if(nps)
        sprintf(fout1,"%s┬",out);
    else
        sprintf(fout1,"%s─",out);

    show_it_children(p,fout1,fl,nps);

    tps = nps - 1;
    //输出兄弟进程信息
    for(i = 0;i < nps;i++)
    {
        if(psibling[i] != p)
        {
            --tps;
            if(tps)
                sprintf(out,"%*s├─",fl,"");
            else
                sprintf(out,"%*s└─",fl,"");
            show_it_children(psibling[i],out,fl,tps);
        }
    }
    return 0;
}

四、实验核心代码

allkt.c:按列对齐输出系统中所有内核线程的程序名、PID、进程状态、进程优先级、父进程的PID。
Makefile1:allkt.c的Makefile文件。
pstree.c:设计一个带参数的模块,其参数为某个进程的PID号,模块的功能是类似pstree的输出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID号及进程状态。
Makefile2:pstree.c的Makefile文件。
完整实验代码详见:HDU-operation-system-course-design-code/实验二/

### 杭州电子科技大学操作系统课程设计资源 #### 关于操作系统课程设计的目标与要求 操作系统课程设计旨在让学生深入理解操作系统理及其实际应用。学生需掌握进程管理、内存分配、文件系统以及设备驱动等方面的知识,并能够独立完成一个小规模的操作系统模块开发或优化现有系统性能的任务。 #### 可供参考的学习材料和教程链接 对于希望获取更多关于杭州电子科技大学操作系统课程设计的信息,建议访问学校官网的教学资源页面或是联系授课教师获得官方发布的指南文档。此外,在线平台上也有许多优质的开源项目案例可供学习借鉴: - **GitHub**: 许多高校会将其OS实验项目的源码托管于此处,可以找到不同版本内核实现细节; - **Stack Overflow & 优快云论坛**: 当遇到具体技术难题时可在此类社区提问交流寻求解决方案; #### 示例代码片段:简易调度器模拟 下面给出一段简单的Python程序来演示如何创建一个多道批处理环境下的轮转法(Round Robin)CPU调度仿真工具[^1]: ```python import queue class Process: def __init__(self, pid, burst_time): self.pid = pid self.burst_time = burst_time def round_robin(processes, time_slice): ready_queue = queue.Queue() for p in processes: ready_queue.put(p) while not ready_queue.empty(): current_process = ready_queue.get() if current_process.burst_time > 0 : print(f"Executing process {current_process.pid}") if current_process.burst_time >= time_slice: current_process.burst_time -= time_slice if current_process.burst_time > 0: ready_queue.put(current_process) else: remaining_time = min(time_slice,current_process.burst_time) current_process.burst_time=0 if __name__ == '__main__': proc_list=[Process(1,8),Process(2,4),Process(3,9)] slice_duration=2 round_robin(proc_list,slice_duration) ``` #### 报告撰写模板结构框架 一份完整的课程设计报告通常应包含以下几个部分: - 封面页:题目名称、作者姓名学号班级等基本信息。 - 摘要摘要:简述研究背景意义目的方法结论等内容不超过一页纸长度。 - 正文主体章节划分如下: - 绪论Introduction:介绍选题缘由现状分析理论依据等前置知识铺垫工作。 - 方案设计Design Scheme:描述整体架构思路关键技术难点解决措施等具体内容阐述。 - 实验测试Experimentation and Testing:记录调试过程参数设置运行结果对比评价指标体系构建等情况说明。 - 总结Conclusion:总结研究成果不足之处未来改进方向展望等收尾性质的文字表达。 最后附上必要的参考资料列表以便读者查阅始出处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值