操作系统实验-进程管理
前言
此文用于记录学校实验课进行的一些操作以便后续回顾
安装合适的编辑器辅助编写代码
这里我使用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中:用于进入命令模式,w和q分别为保存与退出
实验操作
创建内核进程
创建文件目录结构如下
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命令进行编译

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

接下来卸载模块
sudo rmmod task1
查看内核日志可以看到

打印输出当前系统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

卸载内核并查看结果

打印输出当前处于运行状态的进程的 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
操作
编写代码并编译

插入内核查看信息

卸载内核模块并查看信息

使用 cgroup实现限制CPU核数
安装libcgroup
libcgroup用于管理和操作linux控制组,限制、记录和隔离进程组的资源使用
dnf install libcgroup -y
挂载 tmpfs 格式的 cgroup 文件夹
在root目录下执行以下命令
mkdir /cgroup
mount -t tmpfs tmpfs /cgroup
cd /cgroup

挂载 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

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

创建死循环文件并验证
//while_long.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
while (1){}
printf("Over");
exit(0);
}

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

可以看到第一条进程是while_long,pid为2197
Ctrl+c退出后输入如下命令,可以看到cpu核数限制成功

使用cgroup实现不允许访问U盘
连接U盘
电脑上插入U盘后,在VM的上方点击虚拟机-可移动设备,将U盘选中添加到虚拟机中

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

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

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


浙公网安备 33010602011771号