List
下面是截取内核源码,有部分修改方便自己测试使用
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list){
list->next = list;
list->prev = list;
}
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
static inline void list_add(struct list_head *new, struct list_head *head){
__list_add(new, head, head->next);
}
static inline void list_add_tail(struct list_head *new, struct list_head *head){
__list_add(new, head->prev, head);
}
static inline void __list_del(struct list_head * prev, struct list_head * next){
next->prev = prev;
prev->next = next;
}
static inline void __list_del_clearprev(struct list_head *entry){
__list_del(entry->prev, entry->next);
entry->prev = NULL;
}
static inline void __list_del_entry(struct list_head *entry){
__list_del(entry->prev, entry->next);
}
static inline void list_del(struct list_head *entry){
__list_del_entry(entry);
entry->next = NULL;
entry->prev = NULL;
}
static inline void list_replace(struct list_head *old,struct list_head *new){
new->next = old->next;
new->next->prev = new;
new->prev = old->prev;
new->prev->next = new;
}
static inline void list_replace_init(struct list_head *old, struct list_head *new){
list_replace(old, new);
INIT_LIST_HEAD(old);
}
static inline void list_swap(struct list_head *entry1, struct list_head *entry2){
struct list_head *pos = entry2->prev;
list_del(entry2);
list_replace(entry1, entry2);
if (pos == entry1)
pos = entry2;
list_add(entry1, pos);
}
static inline void list_del_init(struct list_head *entry){
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}
static inline void list_move(struct list_head *list, struct list_head *head){
__list_del_entry(list);
list_add(list, head);
}
static inline void list_move_tail(struct list_head *list, struct list_head *head){
__list_del_entry(list);
list_add_tail(list, head);
}
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define list_entry(ptr, type, member) container_of(ptr, type, member)
#define list_first_entry(ptr, type, member) list_entry((ptr)->next, type, member)
#define list_last_entry(ptr, type, member) list_entry((ptr)->prev, type, member)
#define list_first_entry_or_null(ptr, type, member) ({ \
struct list_head *head__ = (ptr); \
struct list_head *pos__ = READ_ONCE(head__->next); \
pos__ != head__ ? list_entry(pos__, type, member) : NULL; \
})
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, typeof(*(pos)), member)
#define list_prev_entry(pos, member) \
list_entry((pos)->member.prev, typeof(*(pos)), member)
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_continue(pos, head) \
for (pos = pos->next; pos != (head); pos = pos->next)
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
#define list_for_each_prev_safe(pos, n, head) \
for (pos = (head)->prev, n = pos->prev; \
pos != (head); \
pos = n, n = pos->prev)
#define list_entry_is_head(pos, head, member) \
(&pos->member == (head))
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \
!list_entry_is_head(pos, head, member); \
pos = list_next_entry(pos, member))
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member); \
!list_entry_is_head(pos, head, member); \
pos = list_prev_entry(pos, member))
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
#endif
用户态测试
形成的数据结果如图
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
struct type1{
int value;
struct list_head list;
};
struct type2 {
int id;
struct type1 item;
struct list_head list;
};
int id_count = 0;
struct type2 src;
int find_by_id (int id,struct type2 **tmp) {
struct list_head *pos;
list_for_each(pos, &(src.list)){
*tmp = list_entry(pos, struct type2, list);
if ((*tmp)->id == id) {
return 0;
}
}
return -1;
}
int add_type2(int * id) {
struct type2 *tmp;
tmp = malloc(sizeof(struct type2));
INIT_LIST_HEAD(&(tmp->item.list));
id_count ++;
tmp->id = id_count;
*id = id_count;
list_add(&(tmp->list),&(src.list));
return 0;
}
int del_type2(int id){
struct type2 *tmp;
struct type1 *prev, *next;
int ret = find_by_id(id,&tmp);
if (ret)
return ret;
list_for_each_entry_safe(prev, next, &(tmp->item.list), list) {
if(prev) {
list_del(&(prev->list));
free(prev);
}
}
list_del(&(tmp->list));
free(tmp);
return 0;
}
int add_type1(int id,int value) {
struct type2 *tmp;
struct type1 *item_tmp;
int ret = find_by_id(id,&tmp);
if (ret)
return ret;
item_tmp = malloc(sizeof(struct type1));
item_tmp->value = value;
list_add(&(item_tmp->list),&(tmp->item.list));
return 0;
}
int del_type1(int id,int value) {
struct type2 *tmp;
struct list_head *type1_ptr;
struct type1 *item_tmp;
int ret = find_by_id(id,&tmp);
if (ret)
return ret;
list_for_each(type1_ptr, &(tmp->item.list)){
item_tmp = list_entry(type1_ptr, struct type1, list);
if (item_tmp->value == value) {
list_del(type1_ptr);
free(item_tmp);
return 0;
}
}
return -1;
}
int del() {
struct type2 *tmp_prev,*tmp_next;
struct type1 *type2_prev,*type2_next;
list_for_each_entry_safe(tmp_prev,tmp_next,&(src.list),list) {
if (!tmp_prev) {
printf("error\n");
continue;
}
list_for_each_entry_safe(type2_prev,type2_next,&(tmp_prev->item.list),list) {
if (type2_prev) {
list_del(&(type2_prev->list));
free(type2_prev);
}
}
list_del(&(tmp_prev->list));
free(tmp_prev);
}
return 0;
}
int print_list() {
struct type2 *tmp;
struct type1 *type1_tmp;
printf("----------------------------\n");
list_for_each_entry(tmp,&(src.list),list) {
printf("id=%d:",tmp->id);
list_for_each_entry(type1_tmp,&(tmp->item.list),list) {
printf("%d, ",type1_tmp->value);
}
printf("\n");
}
return 0;
}
int main() {
int opt;
int type1_id;
int value;
INIT_LIST_HEAD(&(src.list));
while(~scanf("%d",&opt)) {
switch (opt) {
case 1: {
add_type2(&type1_id);
break;
}
case 2: {
scanf("%d",&value);
del_type2(value);
break;
}
case 3: {
scanf("%d",&value);
add_type1(type1_id,value);
break;
}
case 4: {
scanf("%d",&value);
del_type1(type1_id,value);
break;
}
}
print_list();
}
del();
print_list();
return 0;
}
输入测试
1
3 1
3 2
1
3 3
3 4
4 4
2 1
内核模块测试
参考编写Linux内核模块/驱动程序,并提供ioctl接口
内核模块
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#define ERR(fmt, args...) printk(KERN_ERR "%s()-%d: " fmt "\n", __func__, __LINE__, ##args)
#define DBG(fmt, args...) printk(KERN_DEBUG "%s()-%d: " fmt "\n", __func__, __LINE__, ##args)
struct type1{
int value;
struct list_head list;
};
struct type2 {
int id;
struct type1 item;
struct list_head list;
};
struct param{
int value;
int id;
};
int majorNumber;
struct class* list_test_module_class = NULL;
struct device* list_test_module_device = NULL;
#define DEVICE_NAME "list_test"
#define CLASS_NAME "list_test_module"
int id_count = 0;
struct type2 src;
#define PLR_SYNC_MAGIC 'l'
#define ADD_TYPE2 _IOR(PLR_SYNC_MAGIC, 1,int)
#define DEL_TYPE2 _IOW(PLR_SYNC_MAGIC, 2,int)
#define ADD_TYPE1 _IOW(PLR_SYNC_MAGIC, 3,struct param)
#define DEL_TYPE1 _IOW(PLR_SYNC_MAGIC, 4,struct param)
#define PRINT _IO(PLR_SYNC_MAGIC,5)
static long list_test_module_ioctl(struct file *, unsigned int, unsigned long);
struct file_operations ply_sync_ops = {
.owner = THIS_MODULE,
.unlocked_ioctl = list_test_module_ioctl
};
static int __init list_test_init(void);
static void __exit list_test_exit(void);
int add_type2(int *id);
int del_type2(int id);
int add_type1(int id,int value);
int del_type1(int id,int value);
int del(void);
int print_list(void);
static int __init list_test_init(void) {
DBG("list_test_init enter");
majorNumber = register_chrdev(0, DEVICE_NAME, &ply_sync_ops);
if(majorNumber < 0){
ERR("[TestModule:] Failed to register a major number.");
return majorNumber;
}
DBG("[TestModule:] Successful to register a major number %d.", majorNumber);
list_test_module_class = class_create(THIS_MODULE, CLASS_NAME);
if(IS_ERR(list_test_module_class)){
unregister_chrdev(majorNumber, DEVICE_NAME);
ERR("[TestModule:] Class device register failed!");
return PTR_ERR(list_test_module_class);
}
DBG("[TestModule:] Class device register success!");
list_test_module_device = device_create(list_test_module_class, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
if (IS_ERR(list_test_module_device)){
class_destroy(list_test_module_class);
unregister_chrdev(majorNumber, DEVICE_NAME);
ERR("Failed to create the device");
return PTR_ERR(list_test_module_device);
}
INIT_LIST_HEAD(&(src.list));
DBG("list_test_init exit");
return 0;
}
static void __exit list_test_exit(void)
{
DBG("list_test_exit enter");
device_destroy(list_test_module_class, MKDEV(majorNumber, 0));
class_destroy(list_test_module_class);
unregister_chrdev(majorNumber, DEVICE_NAME);
del();
DBG("list_test_init exot");
}
static long list_test_module_ioctl(struct file *file,
unsigned int cmd,
unsigned long param)
{
int ret = -EINVAL;
struct param info;
switch(cmd){
case ADD_TYPE2:{
ret = add_type2((int *)param);
break;
}
case DEL_TYPE2: {
ret = del_type2(param);
break;
}
case ADD_TYPE1: {
ret = copy_from_user(&info, (struct param __user *)param, sizeof(info));
if (ret)
return -EFAULT;
ret = add_type1(info.id,info.value);
break;
}
case DEL_TYPE1: {
ret = copy_from_user(&info, (struct param __user *)param, sizeof(info));
if (ret)
return -EFAULT;
ret = del_type1(info.id,info.value);
break;
}
case PRINT: {
print_list();
break;
}
default:
ERR("[TestModule:] Unknown ioctl cmd!");
}
return ret;
}
int find_by_id (int id,struct type2 **tmp) {
struct list_head *pos;
list_for_each(pos, &(src.list)){
*tmp = list_entry(pos, struct type2, list);
if ((*tmp)->id == id) {
return 0;
}
}
return -1;
}
int add_type2(int *id) {
struct type2 *tmp;
tmp = kmalloc(sizeof(struct type2),GFP_KERNEL);
INIT_LIST_HEAD(&(tmp->item.list));
id_count ++;
tmp->id = id_count;
*id = id_count;
list_add(&(tmp->list),&(src.list));
return 0;
}
int del_type2(int id){
struct type2 *tmp;
struct type1 *prev, *next;
int ret = find_by_id(id,&tmp);
if (ret)
return ret;
list_for_each_entry_safe(prev, next, &(tmp->item.list), list) {
if(prev) {
list_del(&(prev->list));
kfree(prev);
}
}
list_del(&(tmp->list));
kfree(tmp);
return 0;
}
int add_type1(int id,int value) {
struct type2 *tmp;
struct type1 *item_tmp;
int ret = find_by_id(id,&tmp);
if (ret)
return ret;
item_tmp = kmalloc(sizeof(struct type1),GFP_KERNEL);
item_tmp->value = value;
list_add(&(item_tmp->list),&(tmp->item.list));
return 0;
}
int del_type1(int id,int value) {
struct type2 *tmp;
struct list_head *type1_ptr;
struct type1 *item_tmp;
int ret = find_by_id(id,&tmp);
if (ret)
return ret;
list_for_each(type1_ptr, &(tmp->item.list)){
item_tmp = list_entry(type1_ptr, struct type1, list);
if (item_tmp->value == value) {
list_del(type1_ptr);
kfree(item_tmp);
return 0;
}
}
return -1;
}
int del() {
struct type2 *tmp_prev,*tmp_next;
struct type1 *type2_prev,*type2_next;
list_for_each_entry_safe(tmp_prev,tmp_next,&(src.list),list) {
if (!tmp_prev) {
printk("error\n");
continue;
}
list_for_each_entry_safe(type2_prev,type2_next,&(tmp_prev->item.list),list) {
if (type2_prev) {
list_del(&(type2_prev->list));
kfree(type2_prev);
}
}
list_del(&(tmp_prev->list));
kfree(tmp_prev);
}
return 0;
}
int print_list() {
struct type2 *tmp;
struct type1 *type1_tmp;
printk("----------------------------\n");
list_for_each_entry(tmp,&(src.list),list) {
printk("id=%d:",tmp->id);
list_for_each_entry(type1_tmp,&(tmp->item.list),list) {
printk("%d, ",type1_tmp->value);
}
printk("\n");
}
return 0;
}
module_init(list_test_init);
module_exit(list_test_exit);
MODULE_AUTHOR("antrain");
MODULE_DESCRIPTION("kernel list test");
MODULE_LICENSE("GPL");
makefile
MODULE_NAME := test_list
#MODCFLAGS:=-O2 -Wall -DMODULE -D__KERNEL__ -DLINUX -std=c99
#EXTRA_CFLAGS += $(MODULE_FLAGS) $(CFG_INC) $(CFG_INC)
EXTRA_CFLAGS += -g -std=gnu99 -Wfatal-errors
ifneq ($(KERNELRELEASE),) # kernelspace
obj-m := $(MODULE_NAME).o
else # userspace
CURRENT_PATH ?= $(shell pwd)
LINUX_KERNEL ?= $(shell uname -r)
LINUX_KERNEL_PATH ?= /lib/modules/$(LINUX_KERNEL)/build
CURRENT_PATH := $(shell pwd)
modules:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
modules_install:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules_install
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
rm -f modules.order Module.symvers Module.markers
.PHNOY:
modules modules_install clean
endif
用户态测试程序
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#define PLR_SYNC_MAGIC 'l'
#define ADD_TYPE2 _IOR(PLR_SYNC_MAGIC, 1,int)
#define DEL_TYPE2 _IOW(PLR_SYNC_MAGIC, 2,int)
#define ADD_TYPE1 _IOW(PLR_SYNC_MAGIC, 3,struct param)
#define DEL_TYPE1 _IOW(PLR_SYNC_MAGIC, 4,struct param)
#define PRINT _IO(PLR_SYNC_MAGIC,5)
struct param{
int value;
int id;
};
int main(){
int opt;
int type1_id;
int value;
int ret;
int fd = open("/dev/list_test",O_RDWR);
if (fd<0) {
perror("Failed to open the device...");
return -1;
}
struct param p;
while(~scanf("%d",&opt)) {
switch (opt) {
case 1: {
ioctl(fd, ADD_TYPE2,&type1_id);
break;
}
case 2: {
scanf("%d",&value);
ioctl(fd, DEL_TYPE2,value);
break;
}
case 3: {
scanf("%d",&value);
p.id = type1_id;
p.value = value;
ioctl(fd, ADD_TYPE1,&p);
break;
}
case 4: {
scanf("%d",&value);
p.id = type1_id;
p.value = value;
ioctl(fd, DEL_TYPE1,&p);
break;
}
case 5: {
ioctl(fd,PRINT);
}
}
}
close(fd);
return 0;
}
测试
在虚拟机进行测试
sudo dmesg -C
make
sudo insmod test_list.ko
gcc -o test test.c
1
3 1
3 2
1
3 3
3 4
5
4 4
5
2 1
5
sudo ./test < test.log
sudo rmmod test_list
dmesg
|