【OS】操作系统-实验二:模块的编写

操作环境:

  • 主机:MacOS 10.13.3
  • 虚拟机软件:Parallels Desktop
  • Linux:Ubuntu 16.04 Kernal 4.13.0-36-generic

实验要求:

  1. 设计一个模块,要求列出系统中所有内核线程的程序名、PID、进程状态、进程优先级、父进程的 PID。

  2. 设计一个带参数的模块,其参数为某个进程的 PID 号,模块的功能是列出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID 号、进程状态。

  3. 请根据自身情况,进一步阅读分析程序中用到的相关内核函数的源码实现。

操作说明:

  1. 列出系统中所有内核线程模块设计:

    1. 编写模块,命名为 aeonni_ps.c 代码见附录
    2. 编写 Makefile 文件,内容及文件如下:

    m1f

    1. $ make 编译内核:

    m1c

    1. 测试模块:(加载,卸载,查看,清除一条龙)
    $ sudo insmod aeonni_ps.ko && sudo rmmod aeonni_ps && sudo dmesg -c
    

    m1o

  2. 带参数的模块设计:

    1. 编写模块,命名为 aeonni_pid.c 代码见附录
    2. 编写 Makefile 文件,内容及文件如下:

    m2f

    1. $ make 编译内核

    2. $ ps -ef 命令或直接在上个模块的输出中选择一个顺眼的 PID,这里选择 2

    3. 测试模块:(加载,卸载,查看,清除一条龙)

    $ sudo insmod aeonni_pid.ko pid=2 && sudo rmmod aeonni_pid && sudo dmesg -c
    
    • 得到结果

    m2o

源码分析:

  1. 列出系统中所有内核线程模块设计:

    • 要列出所有进程数据,只需要使用定义在/include/linux/sched.h中的for_each_process() 传入一个指向task_struct的指针,直接通过printk打印即可。
    • task_struct中的有关成员为
      • task->comm --- 进程名称 - char[]
      • task->pid ---- PID - pid_t
      • task->state -- 状态 - volatile long
      • task->prio --- 优先级 - int
      • task->parent - 指向父进程的指针 - task_struct *
  2. 带参数的模块设计:

    • 参数的传入:

      • linux/moduleparam.h 中定义了有关参数传递的函数,使用如下语句创建传入的参数。
      static int pid;
      module_param(pid, int, 0644);
      MODULE_PARM_DESC(pid, "An int variable");
      
      • 最终可以直接在 insmod 命令后加上 pid=x 传入参数值
    • task_struct中的有关成员为

      • task->sibling - list_head
      • task->children - list_head

      sibling.next 指向进程的下一个兄弟进程的进程描述符 sibling 成员,若其后没有其他兄弟进程,则指向父进程;而 sibling.prev 指向进程的上一个兄弟进程,若其之前没有兄弟进程,则指向父进程。

      children.next 指向父进程的第一个子进程的 sibling 成员(而不是children成员!),而 children.prev 却指向父进程的最后一个子进程的 sibling 成员。

      • 特别注意 children.next 指向的是 sibling 成员,因此在使用 list_entry() 获得 task_struct 指针时,参数要用 sibling 而不是 children。
    • 使用 find_get_pid() 函数,根据传入的 pid 值获取到对应进程的指针 task,使用list_for_each(pos, head) 遍历链表,并使用 list_entry() 获取 task_struct 指针,最后重复上一个模块的输出操作。

附录:

  1. 模块代码 aeonni_ps.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>

static int aeonni_ps_init(void)
{
	struct task_struct * task;
	
	for_each_process(task)
	{
		//程序名、PID、进程状态、进程优先级、父进程的 PID
        if(task->mm == NULL) {
            printk("%s { \n\
                \tPID: %d, \n\
                \tState: %d, \n\
                \tPriority: %d, \n\
                \tP_PID: %d \n\
                }",task->comm,task->pid, task->state, task->prio, task->parent->pid);
        }
	}
	return 0;
}

static void aeonni_ps_exit(void)
{
	printk(KERN_ALERT"goodbye\n");
	
}

module_init(aeonni_ps_init);
module_exit(aeonni_ps_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Genius.AEONNI.COM");
MODULE_DESCRIPTION("This module is for the first demand of Exercise3.");
  1. 模块代码 aeonni_pid.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/moduleparam.h>

static int pid;

module_param(pid, int, 0644);
MODULE_PARM_DESC(pid, "An int variable");

static int aeonni_pid_init(void)
{
    struct pid * kpid;
    struct task_struct * task, * sib;
    struct list_head * list;
    kpid = find_get_pid(pid);/* 返回pid */
    task = pid_task(kpid, PIDTYPE_PID);/* 返回task_struct */

	//包括父进程、兄弟进程和子进程的程序名、PID 号、进程状态。
	printk(
"\n%s: { \n\
    PID: %d, \n\
    State: %d, \n\
    Priority: %d, \n\
    Parent: %s { \n\
        PID: %d, \n\
        State: %d, \n\
    } \n\
    Siblings: [\n",task->comm,task->pid, task->state, task->prio, task->parent->comm, task->parent->pid, task->parent->state);

    // 列出兄弟进程
    list_for_each(list, &(task->sibling)){
        sib = list_entry(list, struct task_struct, sibling);
        printk(
"        %s: { \n\
            PID: %d, \n\
            State: %d, \n\
        }\n",sib->comm,sib->pid, sib->state);
    }

    // 列出子进程
	printk("    ], \n\
    Children: [\n");

    list_for_each(list, &(task->children)){
        sib = list_entry(list, struct task_struct, sibling);
        printk(
"        %s: { \n\
            PID: %d, \n\
            State: %d, \n\
        }\n",sib->comm,sib->pid, sib->state);
    }

    printk("    ]\n}");

	return 0;
}

static void aeonni_pid_exit(void)
{
	printk(KERN_ALERT"Goodbye_(┐「ε:)_\n");
	
}

module_init(aeonni_pid_init);
module_exit(aeonni_pid_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Genius.AEONNI.COM");
MODULE_DESCRIPTION("This module is for the second demand of Exercise3.");