硬件软件系统方案应用范例智能车大赛技术支持学习园地关于我们恩智浦官网

在云实验室开发板上实验task struct(1)

Linux的进程线程通过task struct结构组织起来,每一个进程线程对应一个task struct对象,这些对象通过task list的链表连接在一起。Task struct包含进程线程很多的属性,是kernel的核心结构体,kernel通过current宏来找到进程线程的task struct结构,本实验通过current宏来展示进程线程的进程号、名字、task struct和栈在内存中的地址等基本信息。

1.      在实验本节代码前,准备环境一个ubuntu物理机或者虚拟机,下载并使能交叉编译工具链,然后编译好内核源码,参考《编译内核镜像并在云实验室开发板运行》

2.      Linux kernel进程信息的示例代码:

在你要写代码的目录下用touch新建两个文件,一个源码文件,一个Makefile。如下

~/linux_driver/ 06_processinfo$ touch 06_process_info.c
~/linux_driver/06_processinfo $ touch Makefile
~/linux_driver/06_processinfo $ vim 06_process_info.c 

将下面的代码复制到06_process_info.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 task struct info::\n"
 76                "module                  : %s\n"
 77                "process name            : %s\n"
 78                "PID                     : %d\n"
 79                "TGID                    : %d\n"
 80                "the task struct address : 0x%pK\n"
 81                "the task stack address  : 0x%pK\n",
 82                VIRTCHARDEV_NAME,
 83                current->comm,
 84                current->pid,
 85                current->tgid,
 86                current,
 87                current->stack
 88                );
 89
 90         return ret;
 91 }
 92
 93 static ssize_t virtdev_write(struct file *filp, const char __user   *buf,
 94                         size_t size, loff_t *ppos)
 95 {
 96         unsigned long p =  *ppos;
 97         unsigned int count = size;
 98         int ret = 0;
 99         void *kbuf = NULL;
100         struct virtchardev *dev = filp->private_data;
101
102         kbuf = kvmalloc(size, GFP_KERNEL);
103         memset(kbuf, 0, count);
104         if (copy_from_user(kbuf, buf, count) != 0){
105                 ret =  - EFAULT;
106         }
107         strscpy(dev->mem, kbuf, (count > BUF_LEN ? BUF_LEN : count));
108         ret = count;
109         printk("write bytes: %zu\n", count);
110         printk("Process task struct info::\n"
111                "module                  : %s\n"
112                "process name            : %s\n"
113                "PID                     : %d\n"
114                "TGID                    : %d\n"
115                "the task struct address : 0x%pK\n"
116                "the task stack address  : 0x%pK\n",
117                VIRTCHARDEV_NAME,
118                current->comm,
119                current->pid,
120                current->tgid,
121                current,
122                current->stack
123                );
124
125         return ret;
126 }
127
128 static const struct file_operations virtdev_fops =
129 {
130         .owner = THIS_MODULE,
131         .read = virtdev_read,
132         .write = virtdev_write,
133         .open = virtdev_open,
134         .release = virtdev_release,
135 };
136
137
138 int virtdev_init(void)
139 {
140         virtdev = kmalloc(sizeof(struct virtchardev), GFP_KERNEL);
141         memset(virtdev, 0, sizeof(struct virtchardev));
142         printk("%s %d\n", __func__, __LINE__);
143         sema_init(&virtdev->sema, 1);
144         alloc_chrdev_region(&virtdev->virtdev_id, 0, 1,
145                                 VIRTCHARDEV_NAME );
146         virtdev->virtdev_major = MAJOR(virtdev->virtdev_id);
147
148         cdev_init(&virtdev->virtdev_cdev, &virtdev_fops);
149
150         virtdev->virtdev_cdev.owner = THIS_MODULE;
151         virtdev->virtdev_cdev.ops = &virtdev_fops;
152
153         cdev_add(&virtdev->virtdev_cdev, virtdev->virtdev_id, 1);
154
155         printk("%s %d\n", __func__, __LINE__);
156         virtdev->class = class_create(/*THIS_MODULE,*/ VIRTCHARDEV_NAME);
157
158         device_create(virtdev->class, NULL, virtdev->virtdev_id,
159                         NULL, VIRTCHARDEV_NAME);
160         printk("Process task struct info :\n"
161                "module                  : %s\n"
162                "process name            : %s\n"
163                "PID                     : %d\n"
164                "TGID                    : %d\n"
165                "the task struct address : 0x%pK\n"
166                "the task stack address  : 0x%pK\n",
167                VIRTCHARDEV_NAME,
168                current->comm,
169                current->pid,
170                current->tgid,
171                current,
172                current->stack
173                );
174
175         return 0;
176 }
177
178 void virtdev_exit(void)
179 {
180         cdev_del(&virtdev->virtdev_cdev);
181         unregister_chrdev_region(virtdev->virtdev_id, 1);
182
183         device_destroy(virtdev->class, virtdev->virtdev_id);
184         class_destroy(virtdev->class);
185
186         kfree(virtdev);
187
188 }
189
190 module_init(virtdev_init);
191 module_exit(virtdev_exit);

        编写Makefile:

PWD    := $(shell pwd)
KERDIR := /home/test/kernel_build/linux-imx 
obj-m += 06_process_info.o
 
all:
        make -C $(KERDIR) M=$(PWD) modules
clean:
        make -C $(KERDIR) M=$(PWD) clean

3.      编译:

~/linux_driver/06_processinfo$ export CROSS_COMPILE=~/toolchain/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
~/linux_driver/06_processinfo$ export ARCH=arm64
~/linux_driver/06_processinfo$ make

编译完成后当前目录会有如下文件

~/linux_driver/06_processinfo$ ls
06_process_info.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应用的内核驱动程序06_process_info.ko,通过一次点击下图中的1,2,3位置,然后切换到本地对应的文件目录上传。

6.      运行字符驱动模块

在执行下面的操作前要先使能串口终端的log打印,否则会看不到串口终端打印信息:

root@imx93evk:~# echo 8 > /proc/sys/kernel/printk

执行insmod加载模块,可以看到log里打印出了进程名字insmod,PID等信息。

当向内核模块写入字符串,可以看到sh进程在工作,并打印了相关信息

然后读取刚才写入的信息时,可以看到cat进程的相关信息,log如下: