本用例来展示Linux进程task struct结构里的进程状态、优先级、调度策略等参数。在调试问题的过程中,进程状态参数可以快速确定进程是处于运行态,可中断的睡眠状态、不可中断的睡眠状态还是已停止等,例如如果发现进程处于不可中断睡眠状态,可能是由于等待某些硬件资源或I/O操作,这有助于定位系统瓶颈或潜在的死锁问题。优先级参数在调试中可以快速识别出关键进程,从而重点关注其运行情况和资源占用。调度策略参数可以用来区分进程类型,从而调整调度策略,分析进程的调度行为是否符合预期。也可以调度策略和优先级同时使用来优化系统的实时性。
1. 在实验本节代码前,准备环境一个ubuntu物理机或者虚拟机,下载并使能交叉编译工具链,然后编译好内核源码,参考《编译内核镜像并在云实验室开发板运行》
2. Linux kernel进程信息的示例代码:
在你要写代码的目录下用touch新建两个文件,一个源码文件,一个Makefile。如下
~/linux_driver/07_process_state$ touch 07_process_state.c
~/linux_driver/07_process_state$ touch Makefile
~/linux_driver/07_process_state$ vim 07_process_state.c
将下面的代码复制到07_process_state.c, 如下:
/*
* Copyright 2025 NXP
* SPDX-License-Identifier: GPL-2.0+
*/
1 #include <linux/module.h>
2 #include <linux/types.h>
3 #include <linux/init.h>
4 #include <linux/cdev.h>
5
6 MODULE_LICENSE("GPL");
7 MODULE_AUTHOR("Cloud Lab");
8
9 #define BUF_LEN 0x1000
10 #define VIRTCHARDEV_NAME "virtdev"
11
12 struct virtchardev
13 {
14 dev_t virtdev_id;
15 struct cdev virtdev_cdev;
16 struct class *class;
17 struct device *device;
18 int virtdev_major;
19 int virtdev_minor;
20 struct semaphore sema;
21
22 struct cdev cdev;
23 unsigned char mem[BUF_LEN];
24
25 };
26
27 struct virtchardev *virtdev;
28
29
30 int virtdev_open(struct inode *inode, struct file *filp)
31 {
32 filp->private_data = virtdev;
33
34 down_interruptible(&virtdev->sema);
35 return 0;
36 }
37
38 int virtdev_release(struct inode *inode, struct file *filp)
39 {
40 struct virtchardev *dev = filp->private_data;
41
42 up(&dev->sema);
43 return 0;
44 }
45
46 static ssize_t virtdev_read(struct file *filp, char __user *buf,
47 size_t size, loff_t *ppos)
48 {
49 unsigned long p = *ppos;
50 unsigned int count = size;
51 int ret = 0;
52 struct virtchardev *dev = filp->private_data;
53
54 if (p >= BUF_LEN)
55 {
56 printk("*ppos = %d\n", p);
57 return count ? -ENXIO : 0;
58 }
59
60 if (count > BUF_LEN - p)
61 {
62 count = BUF_LEN - p;
63 }
64 if (copy_to_user(buf, (void*)(dev->mem + p), count))
65 {
66 ret = - EFAULT;
67 }
68 else
69 {
70 *ppos += count;
71 ret = count;
72
73 printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);
74 }
75 printk("Process state,priority,schedule policy info::\n"
76 "module : %s\n"
77 "process name : %s\n"
78 "state, 0 Run, 1 Sleep, 2 D, 4 T Stopped : %d\n"
79 "priority : %d\n"
80 "static priority : %d\n"
81 "normal priority : %d\n"
82 "scheduler priority: 0 Normal, 5 Idle : %d\n"
83 "Run on CPU : %d\n",
84 VIRTCHARDEV_NAME,
85 current->comm,
86 current->__state,
87 current->prio,
88 current->static_prio,
89 current->normal_prio,
90 current->policy,
91 raw_smp_processor_id()
92 );
93
94 return ret;
95 }
96
97 static ssize_t virtdev_write(struct file *filp, const char __user *buf,
98 size_t size, loff_t *ppos)
99 {
100 unsigned long p = *ppos;
101 unsigned int count = size;
102 int ret = 0;
103 void *kbuf = NULL;
104 struct virtchardev *dev = filp->private_data;
105
106 kbuf = kvmalloc(size, GFP_KERNEL);
107 memset(kbuf, 0, count);
108 if (copy_from_user(kbuf, buf, count) != 0){
109 ret = - EFAULT;
110 }
111 strscpy(dev->mem, kbuf, (count > BUF_LEN ? BUF_LEN : count));
112 ret = count;
113 printk("write bytes: %zu\n", count);
114 printk("Process state,priority,schedule policy info::\n"
115 "module : %s\n"
116 "process name : %s\n"
117 "state, 0 Run, 1 Sleep, 2 D, 4 T Stopped : %d\n"
118 "priority : %d\n"
119 "static priority : %d\n"
120 "normal priority : %d\n"
121 "scheduler priority: 0 Normal, 5 Idle : %d\n"
122 "Run on CPU : %d\n",
123 VIRTCHARDEV_NAME,
124 current->comm,
125 current->__state,
126 current->prio,
127 current->static_prio,
128 current->normal_prio,
129 current->policy,
130 raw_smp_processor_id()
131 );
132
133 return ret;
134 }
135
136 static const struct file_operations virtdev_fops =
137 {
138 .owner = THIS_MODULE,
139 .read = virtdev_read,
140 .write = virtdev_write,
141 .open = virtdev_open,
142 .release = virtdev_release,
143 };
144
145
146 int virtdev_init(void)
147 {
148 virtdev = kmalloc(sizeof(struct virtchardev), GFP_KERNEL);
149 memset(virtdev, 0, sizeof(struct virtchardev));
150 printk("%s %d\n", __func__, __LINE__);
151 sema_init(&virtdev->sema, 1);
152 alloc_chrdev_region(&virtdev->virtdev_id, 0, 1,
153 VIRTCHARDEV_NAME );
154 virtdev->virtdev_major = MAJOR(virtdev->virtdev_id);
155
156 cdev_init(&virtdev->virtdev_cdev, &virtdev_fops);
157
158 virtdev->virtdev_cdev.owner = THIS_MODULE;
159 virtdev->virtdev_cdev.ops = &virtdev_fops;
160
161 cdev_add(&virtdev->virtdev_cdev, virtdev->virtdev_id, 1);
162
163 printk("%s %d\n", __func__, __LINE__);
164 virtdev->class = class_create(/*THIS_MODULE,*/ VIRTCHARDEV_NAME);
165
166 device_create(virtdev->class, NULL, virtdev->virtdev_id,
167 NULL, VIRTCHARDEV_NAME);
168 printk("Process state,priority,schedule policy info::\n"
169 "module : %s\n"
170 "process name : %s\n"
171 "state, 0 Run, 1 Sleep, 2 D, 4 T Stopped : %d\n"
172 "priority : %d\n"
173 "static priority : %d\n"
174 "normal priority : %d\n"
175 "scheduler priority: 0 Normal, 5 Idle : %d\n"
176 "Run on CPU : %d\n",
177 VIRTCHARDEV_NAME,
178 current->comm,
179 current->__state,
180 current->prio,
181 current->static_prio,
182 current->normal_prio,
183 current->policy,
184 raw_smp_processor_id()
185 );
186
187 return 0;
188 }
189
190 void virtdev_exit(void)
191 {
192 cdev_del(&virtdev->virtdev_cdev);
193 unregister_chrdev_region(virtdev->virtdev_id, 1);
194
195 device_destroy(virtdev->class, virtdev->virtdev_id);
196 class_destroy(virtdev->class);
197
198 kfree(virtdev);
199
200 }
201
202 module_init(virtdev_init);
203 module_exit(virtdev_exit);
编写Makefile:
PWD := $(shell pwd)
KERDIR := /home/test/kernel_build/linux-imx
obj-m += 07_process_state.o
all:
make -C $(KERDIR) M=$(PWD) modules
clean:
make -C $(KERDIR) M=$(PWD) clean
3. 编译:
~/linux_driver/07_process_state$ export CROSS_COMPILE=~/toolchain/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
~/linux_driver/07_process_state$ export ARCH=arm64
~/linux_driver/07_process_state$ make
编译完成后当前目录会有如下文件
~/linux_driver/07_process_state$ ls
07_process_state.ko
4. 替换云实验室开发板镜像并重新启动
根据《编译内核镜像并在云实验室开发板运行》替换dtb和image,这里仍然需要这一步,因为内核模块在加载时需要和内核编译版本相匹配
上传并替换云实验室板子的dtb和Image,依次点击用户网页端下面1,2,3的位置,在点击位置2时,选择TFTP,点击3后选择文件~/kernel_build/linux-imx/arch/arm64/boot/dts/freescale/imx8mp-evk.dtb和~/kernel_build/linux-imx/arch/arm64/boot/Image
上传成功后,点击下方PowerReset EVK按钮,重启开发板。
至此,开发板开始运行自己修改编译的linux镜像。
5. 上传自己编译的字符驱动模块
在4中编译出了关于mutex应用的内核驱动程序07_process_state.ko,通过一次点击下图中的1,2,3位置,然后切换到本地对应的文件目录上传。
6. 运行字符驱动模块
在执行下面的操作前要先使能串口终端的log打印,否则会看不到串口终端打印信息:
root@imx93evk:~# echo 8 > /proc/sys/kernel/printk
执行insmod命令加载模块,可以看到log里打印出了进程状态、优先级、调度策略等信息:
insmod 07_process_state.ko
当向内核模块写入字符串,可以看到sh进程在工作,并打印了相关信息:
echo hello_world > /dev/virtdev
然后读取刚才写入的信息时,可以看到cat进程的相关信息,log如下:
cat /dev/virtdev