Windows 应用linux内核链表
一、从网上现在linux kernel代码
linux内核版本有2种: 稳定版(次版本为偶数),开发版(次版本为奇数)
版本号: 主版本.次版本.释出版本-修改版本
内核下载连接网站:https://www.kernel.org/
下载内核版本:4.19.144
压缩文件大小:98.64MB
内核代码查看使用软件:Source Insight 4.0
Winddow软件平台:VS2019
二、关键文件
要进行移植的文件路径:
/linux-4.19.144/include/list.h
list.h文件里面定义了几个头文件,虽然这些定义的文件于list.h什么关系还不清楚,但可以一个一个清除跟他们之间的关系。换句话说,要从这些定义头文件里面复制相关代码出来,并加以修改。
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/poison.h>
#include <linux/const.h>
#include <linux/kernel.h>
三、修改list.h头文件
1、/include/linux/types.h文件中找到有关链表的相关定义,代码拷贝下来
struct list_head {
struct list_head* next, * prev;
};
struct hlist_head {
struct hlist_node* first;
};
struct hlist_node {
struct hlist_node* next, ** pprev;
};
2、list.h中不需要调试链表,所以删除了预编译定义 #ifdef CONFIG_DEBUG_LIST与 #else 里面相关代码,只保留了 #else 与 #endif 里面代码。
3、如果用C++语言编写,new会成为关键字,修改成newStruct
4、list.h中的宏定义WRITE_ONCE它的定义路径在
/tool/virtio/linux/compiler.h
5、进行第一次编译。。。发生了未定义NULL,在此出引用Windows.h头文件,用这个头文件的原因是我在Windows10平台下写的代码,应当遵循这个平台下的文件代码(有兼容作用)。
它里面是这样定义,当然也可以自己手动这样定义NULL,也可以添加头文件stdio.h这样不用声明这个NULL
#define NULL 0
6、第二次编译还是出现了大量的报错问题,一共三种报错,如下。
| 严重性| 代码|说明| 项目| 文件|行|禁止显示状态
| 错误 | C3861 |“typeof”: 找不到标识符 | list | E:\VS\list\list.h | 53 |
| 警告 | C4197| “volatile int”: 忽略强制转换中的顶级 volatile 变量 | list |E:\VS\list\list.h| 53|
| 错误(活动) | E0260 | 缺少显式类型(假定“int”) | list |E:\VS\list\list.h| 53|
主要还是复制过来的代码出现问题,慢慢分解这个代码修改前:
#define WRITE_ONCE(var, val) \
(*((volatile typeof(val) *)(&(var))) = (val))
#define READ_ONCE(var) (*((volatile typeof(var) *)(&(var))))
C++不支持typeof() 这个东西,没办法获取获取变量类型,比如typeof(val),如果传入是无符号字节,那么它就会等效成unsigned char,为了解决不兼容这个问题,我们使用C11标准的 delctype() 来获取就可以了。
代码修改后:
#define WRITE_ONCE(var, val) \
(*((volatile decltype(val) *)(&(var))) = (val))
#define READ_ONCE(var) (*((volatile decltype(var) *)(&(var))))
7、C++编译器不支持 ({ }) 形式 ,这个要修改 , 然后为防止重复offsetof重复用宏定义,offsetof改为offsetof2;
代码修改后:
#define offsetof2(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) \
(type *)( (char *)(ptr) - offsetof2(type,member))
四、移植完成后应用测试代码验证
#include "list.h" //移植后头文件名称
#include <iostream>
using namespace std;
struct student
{
char name[60]; //学生名字
int id; //学生id号
struct list_head list;//链表
};
int main(void)
{
struct student *q;
struct student *p;
struct student A = { "Jack" ,1, LIST_HEAD_INIT(A.list) };//初始化变量
struct student B = { "Pink" ,2, LIST_HEAD_INIT(B.list) };//初始化变量
list_add(&B.list, &A.list);//添加到链表中
//通过子变量获取父变量的地址
q = container_of(&A.list, struct student , list);
//查看q指向的下一个成员变量获取父变量的地址
p = container_of(q->list.next, struct student , list);
cout<<"name: "<<p->name<<endl;
cout <<"id :"<< p->id << endl;
//查看q指向的下一个的下一个成员变量获取父变量的地址
p = container_of(q->list.next->next, struct student, list);
cout << "name: " << p->name << endl;
cout << "id :" << p->id << endl;
//查看q指向的下一个的下一个的下一个成员变量获取父变量的地址
p = container_of(q->list.next->next->next, struct student, list);
cout << "name: " << p->name << endl;
cout << "id :" << p->id << endl;
return 0;
}
上面代码运行效果如下图所示。
Linux平台上应用linux内核链表
一、代码示例
下面代码也是在内核中参考应用得的
#include "list.hpp" //移植后头文件名称
#include <stdlib.h>
#include <stdio.h>
struct student
{
int id; //学生id号
struct list_head list;//链表
};
int main(void)
{
struct student *p,*t;
struct student list_test;
list_test.id = 333;
t = &list_test;
//初始化首个数据
INIT_LIST_HEAD(&t->list);
//列表是否为空?
printf("队列是否为空?:%u\n",list_empty(&t->list));
//生成10个学生ID
for (int i = 0; i < 10; i++)
{
p = (struct student *)malloc(sizeof(struct student));
INIT_LIST_HEAD(&p->list);
//赋值一个ID
p->id = i;
//将数据添加到列表后面
list_add_tail(&p->list, &list_test.list);
}
//列表是否为空?
printf("队列是否为空?:%u\n",list_empty(&t->list));
//遍历所有数据
printf("list_for_each_entry :\n");
list_for_each_entry(p, &t->list, list) {
printf("%d\n",p->id);
}
//倒序遍历所有数据
printf("list_for_each_entry_reverse :\n");
list_for_each_entry_reverse(p, &t->list, list)
{
printf("%d\n",p->id);
}
struct student *a,*n;
//删除所有数据,并释放内存空间
list_for_each_entry_safe(a, n, &t->list, list) {
printf("a:%d\n",a->id);
printf("n:%d\n",n->id);
list_del(&a->list);
free(a);
}
return 0;
}
运行效果如下: