HDU 操作系统实验二 -设计一个系统调用,返回指定进程的相关时间信息

这篇具有很好参考价值的文章主要介绍了HDU 操作系统实验二 -设计一个系统调用,返回指定进程的相关时间信息。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、实验思路

二、准备工作——Linux内核编译步骤

1.实验环境配置

2.下载内核源码

3.解压缩内核源码文件

4.清除残留的.config和.o文件

5.配置内核

6.编译内核,生成启动映像文件

7.编译模块

8.安装内核

9.配置grub引导程序

10.重启系统

三、设计系统调用,返回指定进程的相关时间信息

1.分配系统调用号,修改系统调用表

2.申请系统调用服务例程原型

3.实现系统调用服务例程

4.重新编译内核

5.编写用户态程序测试新系统调用

四、函数详解

(1)find_get_pid(pid)

(2)pid_task()

(3)copy_to_user()

(4)list_for_each_entry(pos, head, member)

五、关于验收


一、实验思路

题目:返回指定进程的相关时间信息,如进程创建时间、进程在用户态及内核态的运行时间、进程的所有子孙进程在用户态的运行时间及在内核态的运行时间等。

本实验要求设计系统调用,而系统调用的实质是调用内核函数,于内核态中运行。 所以要先编译好内核,再来进行系统调用:

杭电操作系统实验,linux,运维,服务器,ubuntu

图2.1 实验流程

编译内核的步骤:

杭电操作系统实验,linux,运维,服务器,ubuntu

                                                                 图2.2 编译内核步骤

二、准备工作——Linux内核编译步骤

1.实验环境配置

2.下载内核源码

点击进入Linux官方网站下载

3.解压缩内核源码文件

1.将下载的新内核压缩文件复制到/home中

杭电操作系统实验,linux,运维,服务器,ubuntu

2.进入压缩文件所在子目录

cd /home

 3.分两步解压缩:

xz -d linux-6.0.8.tar.xz

(别急,等着它运行完)

tar -xvf linux-6.0.8.tar

 运行完啦:

杭电操作系统实验,linux,运维,服务器,ubuntu

 文章来源地址https://www.toymoban.com/news/detail-770800.html

 

4.清除残留的.config和.o文件

 注:一定要先进入linux-6.0.8子目录哟

杭电操作系统实验,linux,运维,服务器,ubuntu

 

 不然会报错:

杭电操作系统实验,linux,运维,服务器,ubuntu

1.安装ncurses包命令(Ubuntu中为:libncurses5-dev):

apt-get install libncurses5-dev

2.在开始完全重新编译之前,执行如下命令来清除残留的.config和.o文件:

make mrproper

 

5.配置内核

  运行命令:

make menuconfig

杭电操作系统实验,linux,运维,服务器,ubuntu

 解决方法——依次执行如下命令:

apt-get install flex


apt-get install bison

杭电操作系统实验,linux,运维,服务器,ubuntu

  Do you want to continue?  y

杭电操作系统实验,linux,运维,服务器,ubuntu

 然后再执行:make menuconfig

杭电操作系统实验,linux,运维,服务器,ubuntu

 Save-Enter-OK-Enter-Enter,最后选择Exit,按下Enter键。

6.编译内核,生成启动映像文件

 执行命令:

make -j32

 来,让我们看看报了什么错:

杭电操作系统实验,linux,运维,服务器,ubuntu

  不慌,执行下面两条命令安装缺少的包:

apt-get install libelf-dev

apt-get install libssl–dev

缺少bison:
 apt-get install bison

缺少flex:
apt-get install flex

  还是报错:

scripts/sign-file.c:25:10: fatal error: openssl/opensslv.h: No such file or directory    25 | #include <openssl/opensslv.h>       |          ^~~~~~~~~~~~~~~~~~~~ compilation terminated.

make[1]: *** [scripts/Makefile.host:95: scripts/sign-file]

Error 1 make: *** [Makefile:1189: scripts]

Error 2 make: *** Waiting for unfinished jobs....

root@xxx:/home/linux-6.0.8# apt-get install libssl–dev Reading package lists... Done Building dependency tree        

Reading state information... Done

E: Unable to locate package libssl–dev

 解决方法:,先执行apt-get update,再执行apt-get install libssl-dev

libssl-dev安装步骤
Update the package index:

apt-get update

Install libssl-dev deb package:

apt-get install libssl-dev

 我用的是make -j16,想快点就make -j32,结果:

杭电操作系统实验,linux,运维,服务器,ubuntu

 

7.编译模块

make modules

 好哇,又报错:

make[1]**No rule to nake target ‘debian/canonical-certs.pew’, needed by ‘certs/x509_certificate_list’.Stop.

 解决方法:

编辑.config文件,输入命令:
vim .config

 打开文件后输入“/”查找其所在的命令行(找到之后删除/xxxx,然后按I(Insert)键就不用我说了吧),如下图:

杭电操作系统实验,linux,运维,服务器,ubuntu

杭电操作系统实验,linux,运维,服务器,ubuntu

 

 将文件中的CONFIG_SYSTEM_TRUSTED_KEYS置空,如果CONFIG_SYSTEM_REVOCATION_KEYS的值不为空的话,也将其赋空值。

之后再make -j32

杭电操作系统实验,linux,运维,服务器,ubuntu

CONFIG_SYSTEM_TRUSTED_KEYS=""
CONFIG_SYSTEM_REVOCATION_KEYS=""

 然后再,make -j16,嗯,又报错了,我真是把错都遇到了个遍,挺好:

杭电操作系统实验,linux,运维,服务器,ubuntu

解决方法——输入以下指令:,再 make -j16

apt-get install dwarves

 成功啦!😀

杭电操作系统实验,linux,运维,服务器,ubuntu

8.安装内核

(1)安装模块:

make modules_install

(2)安装内核:

make install

 真碰巧,又报错了,都快做完了,结果/boot空间不够,我。。。

杭电操作系统实验,linux,运维,服务器,ubuntu

  尝试了删除旧的内核版本:apt purge linux-image-5.4.0-26-generic,得到更大的错误:

杭电操作系统实验,linux,运维,服务器,ubuntu

 怎么解决呢,只搜到了这一个答案,喜提再来一遍。。。不哭不哭,继续加油,再来一遍~

杭电操作系统实验,linux,运维,服务器,ubuntu

再来一遍后成功了(第二遍顺利多了):

杭电操作系统实验,linux,运维,服务器,ubuntu

 杭电操作系统实验,linux,运维,服务器,ubuntu

9.配置grub引导程序

执行命令:

update-grub2

10.重启系统

执行命令:

reboot

 可以使用命令"uname -a"查看内核版本

杭电操作系统实验,linux,运维,服务器,ubuntu

看到别的博主都把root@主机名马赛克啦,我也意识到这个隐私问题,觉得打马赛克太麻烦啦,于是用这个命令改了1下主机名,姑且叫test吧:

hostname test

三、设计系统调用,返回指定进程的相关时间信息

1.分配系统调用号,修改系统调用表

root@test:~# cd /root
root@test:~# cd linux-6.0.8
root@test:~/linux-6.0.8# vim arch/x86/entry/syscalls/syscall_64.tbl

 为新调用添加一条目录:

杭电操作系统实验,linux,运维,服务器,ubuntu

2.申请系统调用服务例程原型

root@test:~/linux-6.0.8# vim include/linux/syscalls.h

在文件末尾添加以下内容:

asmlinkage long sys_jzc(pid_t pid,u64 __user *start_time,u64 __user *utime,u64 __user *stime,int __user *num);

杭电操作系统实验,linux,运维,服务器,ubuntu

3.实现系统调用服务例程

root@test:~/linux-6.0.8# vim kernel/sys.c

 在sys.c文件中添加下列服务例程:

//该系统调用需要五个参数,mygettime是系统调用名称
SYSCALL_DEFINE5(mygettime,pid_t,pid,u64 __user *,start_time,u64 __user *,utime,u64 __user *,stime,int __user *,num)
{
    u64 c_start_time,c_utime,c_stime;
    int c_num=0;
    struct pid *ppid;
    struct task_struct *p;
    struct task_struct *pp;

    ppid = find_get_pid(pid);
    p = pid_task(ppid,PIDTYPE_PID);
    c_start_time = p -> start_time;
    c_utime = p -> utime;
    c_stime = p -> stime;
    copy_to_user(start_time,&c_start_time,sizeof(c_start_time));
    copy_to_user(utime,&c_utime,sizeof(c_utime));
    copy_to_user(stime,&c_stime,sizeof(c_stime));

    list_for_each_entry(pp,&(p -> children),sibling)
     {
         c_num ++;
         c_start_time = pp -> start_time;
         c_utime = pp -> utime;
         c_stime = pp -> stime;

         copy_to_user(start_time,&c_start_time,sizeof(c_start_time));
         copy_to_user(utime+1,&c_utime,sizeof(c_utime));
         copy_to_user(stime+1,&c_stime,sizeof(c_stime));
     }
    copy_to_user(num,&c_num,sizeof(c_num));
    return 0;
}

4.重新编译内核

在第一大部分linux内核编译中,从  4.清除残留的.config和.o文件 再来一遍

make menuconfig      //配置内核
make -j16            //编译内核
make modules         //编译模块
make modules_install //安装模块
make install         // 安装内核
reboot               // 立即重启

5.编写用户态程序测试新系统调用

mkdir work2
vim work2.c

 work2.c文件:

//可以通过终端命令行pstree -p来查看进程号并选择想选择的pid
#define _GNU_SOURCE
#include<stdio.h>
#include<unistd.h>
#include<sys/syscall.h>

int main()
{
    unsigned long long start_time[50],utime[50],stime[50];
    int num;
    syscall(323,1309,start_time,utime,stime,&num);
    printf("num:%d\n",num);
    int i;
    for(i = 0;i<=num;i++)
     {
          printf("i:%d,start_time:%llu,utime:%llu,stime:%llu\n",i,start_time[i],utime[i],stime[i]);
     }
    return 0;
}
root@test:~# cd ..
root@test:/# cd root
root@test:~# gcc work2.c
root@test:~# gcc work2.c -o a
root@test:~# ./a

结果:杭电操作系统实验,linux,运维,服务器,ubuntu

四、函数详解

(1)find_get_pid(pid)

位置:kernel/pid.c

源码:

杭电操作系统实验,linux,运维,服务器,ubuntu

 

图4.1.1 find_get_pid()源码

功能 :此函数根据提供的进程号nr获取对应的进程描述符,并且将该进程描述符中的字段count加1.即此进程的用户数目加1(在新进程创建之初,进程描述符字段count的值为1,而函数find_get_pid()执行后,进程描述符字段count变为2)

rcu_read_lock()与rcu_read_ulock():

rcu_read_lock()和rcu_read_ulock()是 RCU “随意读” 的关键,它们的效果是声明了一个读端的临界区,用来保持一个读者的RCU临界区.在该临界区内不允许发生上下文切换。

进程上下文指进程切换时需要保持的进程状态,包括寄存器值、用户和核心栈状态。

get_pid:使count字段加1

find_vpid(nr) :通过它来找到进程描述符,在/kernel/pid.c中定义

杭电操作系统实验,linux,运维,服务器,ubuntu

                                                       图4.1.2 find_vpid()源码

杭电操作系统实验,linux,运维,服务器,ubuntu

图4.1.2 find_vpid()源码解析

杭电操作系统实验,linux,运维,服务器,ubuntu

图4.1.3 task_active_pid_ns ()源码

task_active_pid_ns ():

这个函数分为两步,第一步通过task_pid 拿到当前task的pid,这里的pid是全局的。

在以pid 为形参通过ns_of_pid 得到pid 对应的namespace

(2)pid_task()

杭电操作系统实验,linux,运维,服务器,ubuntu

图4.1.2 pid_task()源码

功能:通过pid和pid的类型来获取它的task_struct结构体,也就是PCB,PCB里有我们所需要的进程的相关时间信息。

(3)copy_to_user()

copy_to_user()

杭电操作系统实验,linux,运维,服务器,ubuntu

图4.1.2  copy_to_user()源码

完成数据从内核空间到用户空间的复制,to 目标地址,这个地址是用户空间的地址;from 源地址,这个地址是内核空间的地址; n将要拷贝的数据的字节数。

如果数据拷贝成功,则返回零;否则,返回没有拷贝成功的数据字节数。

access_ok():作用是检查用户空间指针是否可用。可用则返回True

__copy_to_user():复制功能的具体实现,是用汇编语言实现的

感兴趣的看参考博客13

(4)list_for_each_entry(pos, head, member)

它实际上是一个 for 循环,利用传入的 pos 作为循环变量,从表头 head 开始,逐项向后(next 方向)移动 pos,直至又回head。

作用:第一个member代表head,list_for_each_entry的作用就是循环遍历每一个pos中的member子项,简单来说就是,遍历链表。

但问题的关键在于如何通过pos获得(节点)结构体的起始地址,以便于引用节点的其他域?

这里我们用到了container_of(ptr, type, member)。

list_for_each_entry()里面涉及到的小函数很多,我们先分解一下它: 杭电操作系统实验,linux,运维,服务器,ubuntu

图4.1.3 list_for_each_entry()源码

杭电操作系统实验,linux,运维,服务器,ubuntu

图4.1.4 list_first_entry()源码

杭电操作系统实验,linux,运维,服务器,ubuntu

图4.1.5 list_entry()源码

其中:

杭电操作系统实验,linux,运维,服务器,ubuntu

 图4.2.6 list_entry()宏

而整个宏定义的关键就在于 container_of上面。而这个宏定义如下:

#define container_of(ptr, type, member) ({                  \
        const typeof( ((type *)0)->member ) *__mptr = (ptr); \
        (type *)( (char *)__mptr - offsetof(type,member) );})

container_of(ptr, type, member)的做用是:根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。

可分为两步来看它:

第一步,首先定义一个临时的数据类型(通过typeof(((type *)0)->member)获得)与ptr相同的指针变量__mptr,然后用它来保存ptr的值。

第二步,用(char *)__mptr减去member在结构体中的偏移量,得到的值就是整个结构体变量的首地址(整个宏的返回值就是这个首地址)。

偏移量又是怎么计算的呢?

offsetof(type,member):用来计算一个struct结构体中某个成员相对于结构体首地址的偏移量。

 

五、关于验收

我是周旭老师班级的,周旭老师讲课超好哦,下面是大家最关心的验收问题:

        好像是这样的:我一去验收,老师就说,讲讲吧,你用到了哪些函数啊,然后我就开始讲上面的第四部分的函数详解,期间问了我几个问题,我很肯定地回答了他,后来问我

1.task_struct 结构体在哪定义的,我知道他上课说过,看linux代码要从task_struct结构体看起,但我找了别的就没找这个,哎栽了:task_struct 结构体在 /include/linux/sched.h中定义 ;

2.好像还问了我copy_to_user()的作用
         把数据从内核空间复制到用户空间。
3.ptr减去偏移量就是结构体type类型所对应的地址具体是怎么做的,懂不懂?
          当即开始胡言乱语了,还自己问自己:这个偏移量是怎么求的,真是自己给自己找事,(offsetof(type,member) 是求偏移量的),具体看上面讲的:list_for_each_entry(pos, head, member)

大抵就是这些了,若有错麻烦大家多多指正、多多包涵,谢谢!

 

参考博客:

1.编译Linux内核kernel时遇到的问题与解决方案

2.内核错误:BTF: .tmp_vmlinux.btf: pahole (pahole) is not available

3.内核错误: No rule to make target ‘debian/canonical-certs.pem‘, needed by ‘certs/x509_certificate_list‘

4.libssl-dev安装步骤

5.杭电(杭州电子科技大学)操作系统实验一:Linux内核编译及添加系统调用(返回指定进程的相关时间信息)

6.Linux源码

7.find_get_pid 解析

8.进程管理API之find_get_pid

9.进程管理API之task_active_pid_zhong

10.Linux 内核学习知识:浅析 offsetof 宏以及内核开发学习的所思所想(内核开发人员必读)

11.Linux内核中的container_of函数简要介绍

12.list_for_each_entry(pos, head, member)的内幕

13.初步解析内核函数copy_to_user和copy_from_user

 

 

到了这里,关于HDU 操作系统实验二 -设计一个系统调用,返回指定进程的相关时间信息的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 操作系统的运行机制、中断和异常、系统调用

    🐌个人主页: 🐌 叶落闲庭 💨我的专栏:💨 c语言 数据结构 javaweb 石可破也,而不可夺坚;丹可磨也,而不可夺赤。 程序是如何运行的? 一条高级语言的代码翻译过来可能会对应多条机器指令 程序运行的过程就是CPU一条一条的机器指令的执行过程 “指令”就是处理器(

    2024年02月14日
    浏览(41)
  • 探索操作系统:内核、启动和系统调用的奥秘

    首先,对于有科班背景的读者,可以跳过本系列文章。这些文章的主要目的是通过简单易懂的汇总,帮助非科班出身的读者理解底层知识,进一步了解为什么在面试中会涉及这些底层问题。否则,某些概念将始终无法理解。这些计算机基础文章将为你打通知识的任督二脉,祝

    2024年02月11日
    浏览(55)
  • 计算机操作系统实验:进程调度实验

    前言 二、实验目的 三、实验要求 四、实验原理 五、实验过程 六、代码详解 总结 计算机操作系统是管理计算机硬件和软件资源的核心软件,它负责为用户提供一个友好、高效、安全的使用环境。进程调度是操作系统的一个重要功能,它决定了进程在处理器上的执行顺序和时

    2024年02月07日
    浏览(52)
  • 操作系统实验(进程调度)

      1.1理解有关进程控制块、进程队列的概念。   1.2掌握进程优先权调度算法和时间片轮转调度算法的处理逻辑。   2.1设计进程控制块PCB的结构,分别适用于优先权调度算法和时间片轮转调度算法。   2.2建立进程就绪队列。   2.3编制两种进程调度算法:优先权调度

    2024年02月06日
    浏览(46)
  • 操作系统期末实验:多用户二级文件系统

    期末实验不是python写的,所以很可能是当时在github上找了一个,然后改了改hhh 如果后续找到了链接就放过来 设计一个多用户的二级文件系统,能够实现简单的文件操作。具体包括如下几条命令: (1)Dir 列文件目录; (2)Create 创建文件 (3)Delete 删除文件 (4)Deldir 删除

    2024年01月18日
    浏览(41)
  • 操作系统上机随笔《实验一》

    OK,今天来写一下这个实验一 深刻理解线程和进程的概念,掌握线程与进程在组成成分上的差别,以及与其相适应的通讯方式和应用目标。 以Linux系统进程和线程机制为背景,掌握fork()和clone()系统调用的形式和功能,以及与其相适应的高级通讯方式。由fork派生的子进程之间

    2024年02月06日
    浏览(35)
  • 操作系统上机随笔《实验三》

    一天一个实验,今天是第三天,接着开赶进度 理解动态异长存储分区资源管理,掌握所需数据结构和管理程序,了解各种存储分配算法的优点和缺点。 分析UNIX最先适应(FF)存储分配算法,即map数据结构、存储分配函数malloc()和存储释放函数mfree(),找出与算法有关的成分。 修

    2024年02月10日
    浏览(42)
  • 操作系统实验:进程同步控制

    前言 一、开发语言及实验平台或实验环境 二、实验目的 三、实验要求 四、实验原理 五、实验过程 六、代码详解 七、diy一下 总结 计算机操作系统是一门研究计算机系统的基本原理和设计方法的课程,它涉及到计算机系统的结构、功能、性能和管理等方面。操作系统实验是

    2024年02月05日
    浏览(41)
  • 操作系统-进程调度实验报告

    1.实现四种不同及进程调度算法: 先来先服务、时间片轮转调、优先级调度以及短作业优先调度算法。 2.通过实验理解有关进程控制块,进程队列等的概念。 1.运行素材中的代码,观察其执行结果是否正确?各个调度算法的功能是否完善?如果没有,则完善。 2. 按照下表

    2024年02月06日
    浏览(39)
  • 操作系统实验之文件管理

    目录 一、实验目的 二、实验内容 三、实验思路 四、主要数据结构 五、实验流程图 六、实现代码 七、运行结果 通过这次实验,掌握文件系统的用户管理,掌握普通文件、目录文件管理的基本原理。 1、通过初始化操作建立一个模拟外存空间的虚拟磁盘文件,在该文件中保存

    2024年02月05日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包