操作系统实验-进程管理

前言

此文用于记录学校实验课进行的一些操作以便后续回顾

安装合适的编辑器辅助编写代码

这里我使用vim进行代码编写,以下是安装命令:

yum -y install vim

安装完成后可以修改一些配置文件方便后续代码编写
输入以下命令打开配置文件

vi /etc/vimrc

I进入编写模式,在下方键入以下内容

 set nu          " 设置显示行号
 set showmode    " 设置在命令行界面最下面显示当前模式等
 set ruler       " 在右下角显示光标所在的行数等信息
 set autoindent  " 设置每次单击Enter键后,光标移动到下一行时与上一行的起始字符对齐
 syntax on       " 即设置语法检测,当编辑C或者Shell脚本时,关键字会用特殊颜色显示 

按下Esc推出插入模式,输入:wq进行保存并退出,在vim中:用于进入命令模式,wq分别为保存与退出

实验操作

创建内核进程

创建文件目录结构如下

task1/
├── task1.c         // 内核模块源码
└── Makefile        // 编译规则

创建目录使用mkdir命令

cd进入task1目录后进行如下操作

使用vim命令创建文件(没有vim可以使用nano,个人感觉不太好用)

vim task1.c

I进入插入模式,编写代码如下

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kthread.h>     // kthread_run, kthread_stop
#include <linux/delay.h>       // msleep

static struct task_struct *my_thread = NULL;

// 线程函数
static int my_thread_func(void *data)
{
    while (!kthread_should_stop()) {
        printk(KERN_INFO "Hello from kernel thread!\n");
        msleep(2000);  // 每隔 2 秒打印一次
    }
    return 0;
}

// 模块初始化函数
static int __init task1_init(void)
{
    printk(KERN_INFO "Loading module: task1\n");

    // 创建并运行内核线程
    my_thread = kthread_run(my_thread_func, NULL, "my_kthread");
    if (IS_ERR(my_thread)) {
        printk(KERN_ERR "Failed to create kernel thread\n");
        return PTR_ERR(my_thread);
    }

    return 0;
}

// 模块退出函数
static void __exit task1_exit(void)
{
    printk(KERN_INFO "Unloading module: task1\n");

    if (my_thread) {
        kthread_stop(my_thread);  // 停止线程
        printk(KERN_INFO "Kernel thread stopped.\n");
    }
}

module_init(task1_init);
module_exit(task1_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("YourName");
MODULE_DESCRIPTION("A simple kernel module with a kernel thread");
MODULE_VERSION("1.0");

保存方式与上文编辑配置文件部分相同

同样的,编写Makefile文件如下

obj-m += task1.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

接下来进行编译,在task1目录下键入make命令进行编译

alt text

此时目录下会生成一系列文件,其中task1.ko是你的可加载内核模块,加载并查看结果

加载模块

sudo insmod task1.ko

查看内核日志

dmesg | tail

alt text

接下来卸载模块

sudo rmmod task1

查看内核日志可以看到
alt text

打印输出当前系统CPU负载情况

文件结构如下,创建目录前可以用 cd .. 切换到上一级目录

task2/
├── task2.c         // 内核模块源码
└── Makefile        // 编译规则

task2

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/sched.h>              // for avenrun[]
#include <linux/sched/loadavg.h>      // for LOAD_INT / LOAD_FRAC

extern unsigned long avenrun[]; // 导出的平均负载数组

#define PROC_NAME "cpu_load"
#define BUFFER_SIZE 256

static struct proc_dir_entry *my_proc_file;

// 读取 proc 文件的函数
static ssize_t my_proc_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
    char *buffer;
    ssize_t ret;

    buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
    if (!buffer) {
        return -ENOMEM;
    }

    // 直接格式化负载信息
    snprintf(buffer, BUFFER_SIZE,
             "System Load:\n"
             "1 min: %lu.%02lu   5 min: %lu.%02lu   15 min: %lu.%02lu\n",
             LOAD_INT(avenrun[0]), LOAD_FRAC(avenrun[0]),
             LOAD_INT(avenrun[1]), LOAD_FRAC(avenrun[1]),
             LOAD_INT(avenrun[2]), LOAD_FRAC(avenrun[2]));

    ret = simple_read_from_buffer(buf, count, pos, buffer, strlen(buffer));
    kfree(buffer);
    return ret;
}

// 使用 struct proc_ops 替代 struct file_operations(适用于内核 >= 5.6)
static const struct proc_ops my_proc_fops = {
    .proc_read = my_proc_read,
};

// 模块初始化函数
static int __init task2_init(void)
{
    printk(KERN_INFO "Loading module: cpu_load\n");

    // 打印 1 分钟的 CPU 负载到内核日志
    printk(KERN_INFO "The CPU loading in one minute is %lu.%02lu\n",
           LOAD_INT(avenrun[0]), LOAD_FRAC(avenrun[0]));

    // 创建 proc 文件
    my_proc_file = proc_create(PROC_NAME, 0444, NULL, &my_proc_fops);
    if (my_proc_file == NULL) {
        printk(KERN_ERR "Failed to create /proc/%s\n", PROC_NAME);
        return -ENOMEM;
    }

    printk(KERN_INFO "/proc/%s created successfully\n", PROC_NAME);
    return 0;
}

// 模块退出函数
static void __exit task2_exit(void)
{
    printk(KERN_INFO "Unloading module: cpu_load\n");

    remove_proc_entry(PROC_NAME, NULL);
    printk(KERN_INFO "/proc/%s removed\n", PROC_NAME);
}

module_init(task2_init);
module_exit(task2_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("YourName");
MODULE_DESCRIPTION("A simple kernel module that prints the current CPU load to a /proc file");
MODULE_VERSION("1.0");

Makefile

obj-m += task2.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

创建源码文件并编译

插入内核查看负载信息

sudo insmod task2.ko
dmesg | tail

alt text

卸载内核并查看结果
alt text

打印输出当前处于运行状态的进程的 PID 和名字

task3.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>

static int __init task3_init(void)
{
    struct task_struct *task;

    printk(KERN_INFO "Loading module: task3\n");
    printk(KERN_INFO "Currently running processes:\n");

    // 遍历所有进程
    for_each_process(task) {
        if (task->state == 0) { // 0 表示进程处于运行状态
            printk(KERN_INFO "1) Name: %s  2) PID: %ld  3) State: %ld\n",
                   task->comm, (long)task->pid, (long)task->state);
        }
    }

    return 0;
}

static void __exit task3_exit(void)
{
    printk(KERN_INFO "Unloading module: task3\n");
}

module_init(task3_init);
module_exit(task3_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("YourName");
MODULE_DESCRIPTION("A kernel module to print running processes' PID and names");
MODULE_VERSION("1.0");

Makefile

obj-m += task3.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

操作

编写代码并编译

插入内核查看信息
alt text

卸载内核模块并查看信息
alt text

使用 cgroup实现限制CPU核数

安装libcgroup

libcgroup用于管理和操作linux控制组,限制、记录和隔离进程组的资源使用

dnf install libcgroup -y

挂载 tmpfs 格式的 cgroup 文件夹

在root目录下执行以下命令

mkdir /cgroup
mount -t tmpfs tmpfs /cgroup
cd /cgroup

alt text
挂载 tmpfs 文件类型:tmpfs 是直接建立在 VM 之上的,用一个简单的 mount 命令就可以创建tmpfs 文件系统了。速度快,可以动态分配文件系统大小

挂载 cpuset 管理子系统

挂载某一个 cgroups 子系统到挂载点之后,就可以通过在挂载点下面建立文件夹或者使用cgcreate 命令的方法创建 cgroups 层级结构中的节点/控制组;对应的删除则使用 rmdir 删除文件夹,或使用 cgdelete 命令删除。

mkdir cpuset
mount -t cgroup -o cpuset cpuset /cgroup/cpuset //挂载cpu子系统
cd cpuset
mkdir mycpuset //创建控制组
cd mycpuset

alt text

设置cpu核数(不能超过vm虚拟机中设置)

alt text

创建死循环文件并验证

//while_long.c
#include <stdio.h> 
#include <stdlib.h> 
int main(int argc, char *argv[]) 
{
while (1){} 
printf("Over"); 
exit(0); 
} 

alt text

在vm中打开中断执行以下命令:

top

alt text
可以看到第一条进程是while_long,pid为2197

Ctrl+c退出后输入如下命令,可以看到cpu核数限制成功
alt text

使用cgroup实现不允许访问U盘

连接U盘

电脑上插入U盘后,在VM的上方点击虚拟机-可移动设备,将U盘选中添加到虚拟机中

alt text

输入以下命令fdisk -l以查看操作系统分区情况,大致如图:

alt text

U盘的路径在/dev/sdb,
使用ls -l得到U盘设备号如下

alt text

挂载U盘,挂载devices子系统,并设置U盘拒绝访问
alt text

posted @ 2025-06-02 13:32  Capache  阅读(16)  评论(0)    收藏  举报