给Linux增加系统调用,并编写对应的linux应用程序

前言

这是研一上学期的Linux内核分析作业。这个课程还是很有用的。

题目要求:

(1)能够返回指定进程(通过指定PID)的任务描述符;

(2)能够返回指定进程(通过指定PID)的进程地址空间的布局和统计信息(代码段、数据段、BSS段、堆、栈等区域的位置和大小、包含多少个虚拟内存区VMA、每个VMA的属性、该进程页表的地址、已映射的物理内存大小等。)(该题目需要研究Linux 进程描述符和内存描述符mm_struct。)

相关知识

系统调用是内核为用户进程提供服务的一种方式。通过系统调用,内核能够提供给用户模式下的进程和硬件设备的接口,保护对内核所管理的资源的访问,提高系统安全,提高程序的可移植性。



根据题目,本质就是要求在内核中设计实现一个新的函数,通过指定的进程ID可以返回该进程有关信息。

一个进程ID号,其实没有那么简单。因为Linux系统中有命名空间这种设定,目前实现的有六种不同的命名空间,分别为mount命名空间、UTS命名空间、IPC命名空间、用户命名空间、PID命名空间、网络命名空间。对于PID命名空间,有不同的层次,像图2这样。


在上图有四个命名空间,一个父命名空间衍生了两个子命名空间,其中的一个子命名空间又衍生了一个子命名空间。以PID命名空间为例,由于各个命名空间彼此隔离,所以每个命名空间都可以有 PID 号为 1 的进程;但又由于命名空间的层次性,父命名空间是知道子命名空间的存在,因此子命名空间要映射到父命名空间中去,因此上图中 level 1 中两个子命名空间的六个进程分别映射到其父命名空间的PID 号5~10。

命名空间增大了 PID 管理的复杂性,对于某些进程可能有多个PID——在其自身命名空间的PID以及其父命名空间的PID,凡能看到该进程的命名空间都会为其分配一个PID。因此就有:

全局ID:在内核本身和初始命名空间中唯一的ID,在系统启动期间开始的 init 进程即属于该初始命名空间。系统中每个进程都对应了该命名空间的一个PID,叫全局ID,保证在整个系统中唯一。

局部ID:对于属于某个特定的命名空间,它在其命名空间内分配的ID为局部ID,该ID也可以出现在其他的命名空间中。

所以根据题目,可以假设我们需要提供的是全局PID。

那么如何根据PID的数值找到task_struct结构体呢:

1.通过 PID 计算 pid 挂接到哈希表 pid_hash[] 的表项;

2. 遍历该表项,找到 pid 结构体中 nr 值与 PID 值相同的那个 pid

3.通过该 pid 结构体的 tasks 指针找到 node

4. 最后根据内核的 container_of 机制就能找到 task_struct 结构体


task_struct结构体包含了一个进程所需的所有信息。它定义在include/linux/sched.h文件中。

struct task_struct{

       … …

       struct mm_struct        *mm;

       pid_t           

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核中增加一个系统调用可以实现对进程的监控与管理,对于系统管理员来说非常实用。本文将介绍如何增加一个可以遍历当前所有进程任务描述符的系统调用,并编写对应Linux应用程序。 首先,我们需要打开Linux内核的源代码,并找到和系统调用相关的文件,其中最关键的是`syscall_64.tbl`和`unistd.h`。在`syscall_64.tbl`文件中添加系统调用号和函数名,例如: 336 common my_syscall __x64_sys_my_syscall 然后在`unistd.h`文件中添加函数原型: long my_syscall(unsigned long arg); 接下来,我们需要编写系统调用实现的代码,将其放在内核中。 SYSCALL_DEFINE1(my_syscall, unsigned long, arg) { struct task_struct *task; printk(KERN_INFO "PID\tSTATE\tPPID\tNAME\n"); for_each_process(task) { printk("%d\t%ld\t%d\t%s\n", task->pid, task->state, task->parent->pid, task->comm); } return 0; } 在这段代码中,我们使用了`for_each_process()`函数遍历了当前系统的所有进程,然后输出了进程的PID、状态、父进程PID和进程名等信息。 最后,我们需要编写对应Linux应用程序来使用该系统调用。我们可以在终端中运行以下命令: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/syscall.h> #define __NR_my_syscall 336 int main(int argc, char **argv) { syscall(__NR_my_syscall); return 0; } 在这段代码中,我们使用`syscall()`函数调用刚刚增加的`my_syscall()`系统调用。运行该应用程序会输出所有进程的PID、状态、父进程PID和进程名等信息,方便系统管理员进行进程监控和管理。 总之,在Linux内核中增加一个系统调用编写对应Linux应用程序并不是一件容易的事情。需要掌握一定的Linux内核编程知识,同时对系统调用有深入理解,才能够轻松完成这个过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值