0%

Linux-Lab2-Kernel API

Kernel API

  • 熟悉基本的 Linux kernel API
  • 内存分配机制说明
  • locking 机制说明

内核是一个独立的实体,不能使用用户空间中的库(甚至不能使用 libc)

总之,内核编程基于一个全新的独立API,无论我们指的是 POSIX 还是 ANSI-C,它都与用户空间 API 无关

Accessing memory

内核编程中的一个重要区别是如何访问和分配内存,由于内核编程非常接近物理机,因此内存管理有重要的规则

首先,它适用于几种类型的内存:

  • 物理内存
  • 内核地址空间中的虚拟内存
  • 进程地址空间的虚拟内存
  • 驻留内存(那些被映射到进程虚拟内存空间的物理内存)

对于驻留内存而言,进程虚拟地址和内核虚拟地址有不同的情况:

  • 进程的地址空间中的虚拟内存不能被视为驻留:
    • page 可能被交换,或者由于需求分页机制而根本不存在于物理内存中
  • 内核地址空间中的内存可以驻留或不驻留:
    • 模块的数据段和代码段以及进程的内核堆栈都是驻留的
    • 动态内存可能是驻留的,也可能不是驻留的,具体取决于它的分配方式
  • 使用驻留内存时,事情很简单,因为可以随时访问驻留内存
  • 如果使用非驻留内存,则只能从某些上下文中访问它
  • 因此,当操作系统检测到此类访问时,它将采取严厉措施(阻止或重置系统以防止严重损坏)

进程的虚拟内存通常不能直接从内核访问,但在某些情况下,设备驱动程序需要执行此操作:

  • 典型情况是设备驱动程序需要从用户空间访问缓冲区
  • 在这种情况下,设备驱动程序必须使用特殊功能,并且不能直接访问缓冲区
  • 这是防止访问无效内存区域所必需的

相对于用户空间调度,内核的另一个区别是栈:

  • 栈的大小是固定且有限的(在 Linux 中使用 4K 堆栈,在视窗中使用 12K 堆栈)
  • 因此,应避免在堆栈上分配大型结构或使用递归调用

Contexts of execution

关于内核执行,我们区分两个上下文:

  • 进程上下文:
    • 运行代码作为系统调用的结果时
    • 在内核线程的上下文中运行时
  • 中断上下文:
    • 在例程中运行以处理中断时
    • 可延迟操作时

某些内核 API 调用可能会阻止当前进程(例如使用信号量或等待条件变量),在这种情况下,进程将进入等待队列,并且让另一个进程运行

Locking

内核编程最重要的特性之一是并行性,Linux 支持具有多个处理器和内核抢占性的 SMP 系统

这使得内核编程更加困难,因为对全局变量的访问必须与自旋锁基元 spinlock primitives 或阻塞基元 blocking primitives 同步:

  • 在受自旋锁保护的关键区域中运行的代码,不允许挂起当前进程
  • 当进程开始自旋时,其占用的 CPU 资源也不会释放
  • 因此,尽可能少使用自旋锁

Preemptivity

Linux 使用抢占式内核

抢占式多任务处理的概念是指:操作系统在其量程(时间片)到期时,强制中断在用户空间中运行的进程,以便运行另一个进程

  • 由于抢占性,当我们需要从不同进程上下文中运行的两部分代码之间共享资源时,我们需要使用 synchronization primitives 同步基元来保护自己,即使在单个处理器的情况下也是如此

Convention indicating errors

对于 Linux 内核编程,用于调用函数以指示成功的约定与在 UNIX 编程中相同:

  • 0 表示成功,或 0 以外的值表示失败(返回负值)

详尽的错误列表和摘要解释可以在 include/asm-generic/errno-base.hincludes/asm-generic/ernno.h

Exercises

要解决练习,您需要执行以下步骤:

  • 从模板准备 skeletons
  • 构建模块
  • 将模块复制到虚拟机
  • 启动 VM 并在 VM 中测试模块
1
2
3
make clean
LABS=kernel_api make skels
make build
1
2
➜  kernel_api git:(master) ✗ ls
1-mem 2-sched-spin 3-memory 4-list 5-list-full 6-list-sync 7-list-test

1.Memory allocation in Linux kernel:

  • 观察调用 kmalloc() 对内存的分配情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*
* Kernel API lab
*
* mem.c - Memory allocation in Linux
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/ctype.h>

MODULE_DESCRIPTION("Print memory");
MODULE_AUTHOR("SO2");
MODULE_LICENSE("GPL");

static char *mem;

static int mem_init(void)
{
size_t i;

mem = kmalloc(4096 * sizeof(*mem), GFP_KERNEL);
if (mem == NULL)
goto err_mem;

pr_info("chars: ");
for (i = 0; i < 4096; i++) {
if (isalpha(mem[i]))
printk(KERN_CONT "%c ", mem[i]);
}
pr_info("\n");

return 0;

err_mem:
return -1;
}

static void mem_exit(void)
{
kfree(mem);
}

module_init(mem_init);
module_exit(mem_exit);
  • 结果如下:(printk 默认换行,可以使用 KERN_CONT 禁止换行)
1
2
3
4
5
6
7
8
9
10
mem: loading out-of-tree module taints kernel.                                  
chars: Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z
Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z
Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z
Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z
Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z
Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z
Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z
Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z
Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z

2.Sleeping in atomic context:

  • 熟悉自旋锁的使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
* Kernel API lab
*
* sched-spin.c: Sleeping in atomic context
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>

MODULE_DESCRIPTION("Sleep while atomic");
MODULE_AUTHOR("SO2");
MODULE_LICENSE("GPL");

static int sched_spin_init(void)
{
spinlock_t lock;

spin_lock_init(&lock);

/* TODO 0: Use spin_lock to aquire the lock */
spin_lock(&lock);
set_current_state(TASK_INTERRUPTIBLE);
/* Try to sleep for 5 seconds. */
schedule_timeout(5 * HZ);

/* TODO 0: Use spin_unlock to release the lock */
spin_unlock(&lock);
return 0;
}

static void sched_spin_exit(void)
{
}

module_init(sched_spin_init);
module_exit(sched_spin_exit);
  • 加载内核模块时报出了 scheduling while atomic 这个错误,说是会污染内核(当内核受到污染意味着内核处于社区不支持的状态)
1
2
3
4
5
6
7
8
root@qemux86:~/skels/kernel_api/2-sched-spin# insmod sched-spin.ko              
sched_spin: loading out-of-tree module taints kernel.
BUG: scheduling while atomic: insmod/239/0x00000002
1 lock held by insmod/239:
#0: c585bdb8 (&lock){+.+.}-{2:2}, at: sched_spin_init+0x32/0x90 [sched_spin]
Modules linked in: sched_spin(O+)
CPU: 0 PID: 239 Comm: insmod Tainted: G O 5.10.14+ #3
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04
  • 在错误消息中按照包含 BUG 的说明:不能在原子操作中 sleep(自旋锁是用原子操作实现的)

3.Working with kernel memory:

  • 为结构体 struct task_info 分配内存并初始化其字段
  • 为当前进程、父进程、下一进程、下一进程的下一进程分配结构体 struct task_info
  • 显示四个结构
  • 释放结构占用的内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/*
* SO2 lab3 - task 3
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>

MODULE_DESCRIPTION("Memory processing");
MODULE_AUTHOR("SO2");
MODULE_LICENSE("GPL");

struct task_info {
pid_t pid;
unsigned long timestamp;
};

static struct task_info *ti1, *ti2, *ti3, *ti4;

static struct task_info *task_info_alloc(int pid)
{
struct task_info *ti;

/* TODO 1: allocated and initialize a task_info struct */
ti = (struct task_info*)kmalloc(sizeof(struct task_info),NULL);
ti->pid = pid;
ti->timestamp = jiffies;

return ti;
}

static int memory_init(void)
{
struct task_struct *p;
/* TODO 2: call task_info_alloc for current pid */
p = current;
ti1 = task_info_alloc(p->pid);
/* TODO 2: call task_info_alloc for parent PID */
p = current->parent;
ti2 = task_info_alloc(p->pid);
/* TODO 2: call task_info alloc for next process PID */
p = next_task(p);
ti3 = task_info_alloc(p->pid);
/* TODO 2: call task_info_alloc for next process of the next process */
p = next_task(next_task(p));
ti4 = task_info_alloc(p->pid);
return 0;

}

static void memory_exit(void)
{
/* TODO 3: print ti* field values */
printk("%d:%d",ti1->pid,ti1->timestamp);
printk("%d:%d",ti2->pid,ti1->timestamp);
printk("%d:%d",ti3->pid,ti1->timestamp);
printk("%d:%d",ti4->pid,ti1->timestamp);
/* TODO 4: free ti* structures */
kfree(ti1);
kfree(ti2);
kfree(ti3);
kfree(ti4);
}

module_init(memory_init);
module_exit(memory_exit);
  • 结果:
1
2
3
4
5
6
root@qemux86:~/skels/kernel_api/3-memory# insmod memory.ko                      
memory: loading out-of-tree module taints kernel.
root@qemux86:~/skels/kernel_api/3-memory# rmmod memory.ko
237:-48258
213:-48258
214:-48258

4.Working with kernel lists:

  • 熟悉 Linux 链表的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/*
* Kernel API lab
*
* list.c: Working with lists
*
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/sched/signal.h>

MODULE_DESCRIPTION("Use list to process task info");
MODULE_AUTHOR("SO2");
MODULE_LICENSE("GPL");

struct task_info {
pid_t pid;
unsigned long timestamp;
struct list_head list;
};

static struct list_head head;

static struct task_info *task_info_alloc(int pid)
{
struct task_info *ti;

ti = kmalloc(sizeof(*ti), GFP_KERNEL);
if (ti == NULL)
return NULL;
ti->pid = pid;
ti->timestamp = jiffies;

return ti;
}

static void task_info_add_to_list(int pid)
{
struct task_info *ti;

/* TODO 1: Allocate task_info and add it to list */
ti = task_info_alloc(pid);
list_add(&ti->list,&head);
}

static void task_info_add_for_current(void)
{
/* Add current, parent, next and next of next to the list */
task_info_add_to_list(current->pid);
task_info_add_to_list(current->parent->pid);
task_info_add_to_list(next_task(current)->pid);
task_info_add_to_list(next_task(next_task(current))->pid);
}

static void task_info_print_list(const char *msg)
{
struct list_head *p;
struct task_info *ti;

pr_info("%s: [ ", msg);
list_for_each(p, &head) {
ti = list_entry(p, struct task_info, list);
pr_info("(%d, %lu) ", ti->pid, ti->timestamp);
}
pr_info("]\n");
}

static void task_info_purge_list(void)
{
struct list_head *p, *tmp;
struct task_info *ti;

/* TODO 2: Iterate over the list and delete all elements */
list_for_each_safe(p, tmp, &head) {
ti = list_entry(p, struct task_info, list);
list_del(p);
kfree(ti);
}
}

static int list_init(void)
{
INIT_LIST_HEAD(&head);
task_info_add_for_current();
return 0;
}

static void list_exit(void)
{
task_info_print_list("before exiting");
task_info_purge_list();
}

module_init(list_init);
module_exit(list_exit);
  • 结果:
1
2
3
4
5
6
7
8
9
root@qemux86:~/skels/kernel_api/4-list# insmod list.ko                          
root@qemux86:~/skels/kernel_api/4-list# rmmod list.ko
216:-48258
before exiting: [
(1, 4294930828)
(0, 4294930828)
(213, 4294930828)
(239, 4294930828)
]

5.Working with kernel lists for process handling:

  • 熟悉 Linux 链表的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
* Kernel API lab
*
* list-full.c: Working with lists (advanced)
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/sched/signal.h>

MODULE_DESCRIPTION("Full list processing");
MODULE_AUTHOR("SO2");
MODULE_LICENSE("GPL");

struct task_info {
pid_t pid;
unsigned long timestamp;
atomic_t count;
struct list_head list;
};

static struct list_head head;

static struct task_info *task_info_alloc(int pid)
{
struct task_info *ti;

ti = kmalloc(sizeof(*ti), GFP_KERNEL);
if (ti == NULL)
return NULL;
ti->pid = pid;
ti->timestamp = jiffies;
atomic_set(&ti->count, 0);

return ti;
}

static struct task_info *task_info_find_pid(int pid)
{
struct list_head *p;
struct task_info *ti;

/* TODO 1: Look for pid and return task_info or NULL if not found */
list_for_each(p, &head) {
ti = list_entry(p, struct task_info, list);
if(ti->pid == pid){
return ti;
}
}
return NULL;
}

static void task_info_add_to_list(int pid)
{
struct task_info *ti;

ti = task_info_find_pid(pid);
if (ti != NULL) {
ti->timestamp = jiffies;
atomic_inc(&ti->count);
return;
}
ti = task_info_alloc(pid);
list_add(&ti->list, &head);
}

static void task_info_add_for_current(void)
{
task_info_add_to_list(current->pid);
task_info_add_to_list(current->parent->pid);
task_info_add_to_list(next_task(current)->pid);
task_info_add_to_list(next_task(next_task(current))->pid);
}

static void task_info_print_list(const char *msg)
{
struct list_head *p;
struct task_info *ti;

pr_info("%s: [ ", msg);
list_for_each(p, &head) {
ti = list_entry(p, struct task_info, list);
pr_info("(%d, %lu) ", ti->pid, ti->timestamp);
}
pr_info("]\n");
}

static void task_info_remove_expired(void)
{
struct list_head *p, *q;
struct task_info *ti;

list_for_each_safe(p, q, &head) {
ti = list_entry(p, struct task_info, list);
if (jiffies - ti->timestamp > 3 * HZ && atomic_read(&ti->count) < 5) {
list_del(p);
kfree(ti);
}
}
}

static void task_info_purge_list(void)
{
struct list_head *p, *q;
struct task_info *ti;

list_for_each_safe(p, q, &head) {
ti = list_entry(p, struct task_info, list);
list_del(p);
kfree(ti);
}
}

static int list_full_init(void)
{
INIT_LIST_HEAD(&head);

task_info_add_for_current();
task_info_print_list("after first add");

set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(5 * HZ);

return 0;
}

static void list_full_exit(void)
{
struct task_info *ti;

/* TODO 2: Ensure that at least one task is not deleted */
ti = task_info_find_pid(current->parent->pid);
if(ti == NULL){
printk("not find pid: %d",ti->pid);
}
else{
printk("find pid: %d",ti->pid);
}

list_del(&ti->list);
task_info_remove_expired();
list_add(&ti->list, &head);
task_info_print_list("after removing expired");
task_info_purge_list();
task_info_remove_expired();
}

module_init(list_full_init);
module_exit(list_full_exit);
  • 结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
root@qemux86:~/skels/kernel_api/5-list-full# insmod list-full.ko                
list_full: loading out-of-tree module taints kernel.
after first add: [
(1, 4294916763)
(0, 4294916763)
(213, 4294916763)
(238, 4294916763)
]
root@qemux86:~/skels/kernel_api/5-list-full# rmmod list-full.ko
find pid: 213
after removing expired: [
(213, 4294916763)
]

6.Synchronizing list work:

  • 熟悉 Linux 锁的使用
  • 熟悉 Linux 符号的导出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
* Linux API lab
*
* list-sync.c - Synchronize access to a list
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/sched/signal.h>

MODULE_DESCRIPTION("Full list processing with synchronization");
MODULE_AUTHOR("SO2");
MODULE_LICENSE("GPL");

struct task_info {
pid_t pid;
unsigned long timestamp;
atomic_t count;
struct list_head list;
};

static struct list_head head;

/* TODO 1: you can use either a spinlock or rwlock, define it here */
DEFINE_SPINLOCK(lock);

static struct task_info *task_info_alloc(int pid)
{
struct task_info *ti;

ti = kmalloc(sizeof(*ti), GFP_KERNEL);
if (ti == NULL)
return NULL;
ti->pid = pid;
ti->timestamp = jiffies;
atomic_set(&ti->count, 0);

return ti;
}

static struct task_info *task_info_find_pid(int pid)
{
struct list_head *p;
struct task_info *ti;

list_for_each(p, &head) {
ti = list_entry(p, struct task_info, list);
if (ti->pid == pid) {
return ti;
}
}

return NULL;
}

static void task_info_add_to_list(int pid)
{
struct task_info *ti;

/* TODO 1: Protect list, is this read or write access? */
spin_lock(&lock);
ti = task_info_find_pid(pid);
if (ti != NULL) {
ti->timestamp = jiffies;
atomic_inc(&ti->count);
/* TODO: Guess why this comment was added here */
return;
}
spin_unlock(&lock);
/* TODO 1: critical section ends here */

ti = task_info_alloc(pid);
/* TODO 1: protect list access, is this read or write access? */
spin_lock(&lock);
list_add(&ti->list, &head);
spin_unlock(&lock);
/* TODO 1: critical section ends here */
}

void task_info_add_for_current(void)
{
task_info_add_to_list(current->pid);
task_info_add_to_list(current->parent->pid);
task_info_add_to_list(next_task(current)->pid);
task_info_add_to_list(next_task(next_task(current))->pid);
}
EXPORT_SYMBOL(task_info_add_for_current);
/* TODO 2: Export the kernel symbol */

void task_info_print_list(const char *msg)
{
struct list_head *p;
struct task_info *ti;

pr_info("%s: [ ", msg);

/* TODO 1: Protect list, is this read or write access? */
spin_lock(&lock);
list_for_each(p, &head) {
ti = list_entry(p, struct task_info, list);
pr_info("(%d, %lu) ", ti->pid, ti->timestamp);
}
spin_unlock(&lock);
/* TODO 1: Critical section ends here */
pr_info("]\n");
}
EXPORT_SYMBOL(task_info_print_list);
/* TODO 2: Export the kernel symbol */

void task_info_remove_expired(void)
{
struct list_head *p, *q;
struct task_info *ti;

/* TODO 1: Protect list, is this read or write access? */
spin_lock(&lock);
list_for_each_safe(p, q, &head) {
ti = list_entry(p, struct task_info, list);
if (jiffies - ti->timestamp > 3 * HZ && atomic_read(&ti->count) < 5) {
list_del(p);
kfree(ti);
}
}
spin_unlock(&lock);
/* TODO 1: Critical section ends here */
}
EXPORT_SYMBOL(task_info_remove_expired);
/* TODO 2: Export the kernel symbol */

static void task_info_purge_list(void)
{
struct list_head *p, *q;
struct task_info *ti;

/* TODO 1: Protect list, is this read or write access? */
spin_lock(&lock);
list_for_each_safe(p, q, &head) {
ti = list_entry(p, struct task_info, list);
list_del(p);
kfree(ti);
}
spin_unlock(&lock);
/* TODO 1: Critical sections ends here */
}

static int list_sync_init(void)
{
INIT_LIST_HEAD(&head);

task_info_add_for_current();
task_info_print_list("after first add");

set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(5 * HZ);

return 0;
}

static void list_sync_exit(void)
{
struct task_info *ti;

ti = list_entry(head.prev, struct task_info, list);
atomic_set(&ti->count, 10);

task_info_remove_expired();
task_info_print_list("after removing expired");
task_info_purge_list();
}

module_init(list_sync_init);
module_exit(list_sync_exit);
  • 结果:
1
2
3
4
5
6
7
8
9
10
11
12
root@qemux86:~/skels/kernel_api/6-list-sync# insmod list-sync.ko                
list_sync: loading out-of-tree module taints kernel.
after first add: [
(1, 4294905468)
(0, 4294905468)
(213, 4294905468)
(237, 4294905468)
]
root@qemux86:~/skels/kernel_api/6-list-sync# rmmod list-sync.ko
after removing expired: [
(237, 4294905468)
]
  • 一般有循环的地方都要加锁(这里加的是自旋锁)

7.Test module calling in our list module:

  • 从位于目录 6-list-sync/ 的模块中导出各个符号
  • 删除 6-list-sync/ 的模块中所有的锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*
* SO2 lab3 - task 7
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>

MODULE_DESCRIPTION("Test list processing");
MODULE_AUTHOR("SO2");
MODULE_LICENSE("GPL");

extern void task_info_add_for_current(void);
extern void task_info_remove_expired(void);
extern void task_info_print_list(const char *msg);

static int list_test_init(void)
{
/* TODO 1: Uncomment after exporting the symbols in 6-list-sync. */
task_info_add_for_current();
task_info_print_list("after new addition");

return 0;
}

static void list_test_exit(void)
{
/* TODO 1: Uncomment after exporting the symbols in 6-list-sync. */
task_info_remove_expired();
task_info_print_list("after removing expired");
}

module_init(list_test_init);
module_exit(list_test_exit);
  • 结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root@qemux86:~# insmod ./skels/kernel_api/6-list-sync/list-sync.ko              
list_sync: loading out-of-tree module taints kernel.
after first add: [
(1, 4294904706)
(0, 4294904706)
(213, 4294904706)
(237, 4294904706)
]
root@qemux86:~# insmod ./skels/kernel_api/7-list-test/list-test.ko
after new addition: [
(238, 4294906931)
(1, 4294906931)
(0, 4294906931)
(213, 4294906931)
(237, 4294904706)
]
  • 在加载模块的时候出现了死锁的问题,最后才发现需要把 Synchronizing list work 中的锁删掉
1
2
3
4
5
insmod/238 is trying to acquire lock:                                           
d0847110 (lockA){+.+.}-{2:2}, at: task_info_add_to_list+0x11/0xb0 [list_sync]

but task is already holding lock:
d0847110 (lockA){+.+.}-{2:2}, at: task_info_add_to_list+0x11/0xb0 [list_sync]