【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

    3. $ make 编译内核:

      m1c

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

      1
      $ sudo insmod aeonni_ps.ko && sudo rmmod aeonni_ps && sudo dmesg -c

      m1o

  2. 带参数的模块设计:

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

      m2f

    3. $ make 编译内核

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

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

      1
      $ 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 中定义了有关参数传递的函数,使用如下语句创建传入的参数。

        1
        2
        3
        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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    #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.");
  2. 模块代码 aeonni_pid.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    #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.");

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×