sharelist.c文件分析

来源:互联网 发布:excel怎么找出相同数据 编辑:程序博客网 时间:2024/05/01 16:39

描述
假设存在这样一个内核共享资源-链表。另外构造一个多个内核任务访问链表的场景:内核线程向链表加入新节点;内核定时器定时删除节点;系统调用销毁链表。这三种内核任务并发执行时,有可能会破坏链表数据的完整性,所以我们必须对链表进行同步访问保护,以保证数据的一致性。
实现机制
主要的共享资源是链表(mine),操作它的内核任务有三个:200个内核线程(sharelist),他们负责从表头将新节点(struct my_strucct)插入链表;
定时器任务(qt_task),它负责每个时钟节拍从链表头删除一个节点;
系统调用share_exit,它负责销毁链表并卸载模块。

源代码见本文最后。

 

Linux大作业源代码分析
一.源程序中的数据结构及所用函数分析
1.struct my_struct
{
struct list_head list;
int id;
int pid;
};
struct my_struct结构体是一个表示链表结点的结构体,它有三个数据成员,分别是
struct list_head list、int id、int pid。其中,list是结点的指针域,list有两个成员,分别是struct list_head *prev和struct list_head *next,prev指向当前节点的前驱结点,next指向当前结点的后继结点;id是链表结点的自编号;pid是创建该结点的内核线程编号。
2.static struct work_struct queue; /*定义工作队列*/ 工作队列(work queue)是Linux kernel中将工作推后执行的一种机制。这种工作队列机制是把推后的工作交由一个内核线程去执行,因此工作队列的优势就在于它允许重新调度甚至睡眠。
work_struct结构体定义如下:
struct work_struct{
unsigned long pending;/*这个工作是否正在等待处理*/
struct list_head entry;/*将任务挂载到queue的挂载点, 链接所有工作的链表,形成工作队列*/
void (*func)(void *);/*处理函数*/
void *data;/*传递给处理函数的参数*/
void *wq_data;/*内部使用数据*/
struct timer_list timer;/*延迟的工作队列所用到的定时器*/
};
而一些这种结构体的实体链接成的链表就是所谓的工作队列。工作者线程会在被唤醒时执行链表上的所有工作,当一个工作被执行完毕后,相应的work_struct结构体也会被删除。当这个工作链表上没有工作时,工作线程就会休眠。
通过如下宏可以创建一个要推后的完成的工作:
DECLARE_WORK(name,void(*func)(void*),void *data);
通过下述宏动态创建一个工作:
INIT_WORK(struct work_struct *work,void(*func)(void*),void *data);
与tasklet类似,每个工作都有具体的工作队列处理函数,原型如下:
void work_handler(void *data);
将工作队列机制对应到具体的中断程序中,即那些被推后的工作将会在func所指向的那个工作队列处理函数中被执行。
实现了工作队列处理函数后,就需要schedule_work函数对这个工作进行调度,就像这样:schedule_work(&work);这样work会马上就被调度,一旦工作线程被唤醒,这个工作就会被执行(因为其所在工作队列会被执行)。
3.static struct timer_list mytimer; /*用于定时器队列*/
内核定时器,也称为动态定时器,是管理内核所耗费时间的基础,它是一种用来推迟执行程序的工具。首先看看内核是如何描述定时器的:
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;//定时器链表入口
unsigned long expires;//以jiffies为单位的定时值
struct tvec_base *base;

void (*function)(unsigned long); //定时器处理函数
unsigned long data; //传给定时器处理函数的参数

int slack;
#ifdef CONFIG_TIMER_STATS
void *start_site;
char start_comm[16];
int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
接着介绍一下内核中几个关于时间的概念:
节拍率:系统定时器以某种频率触发时钟中断,这个频率就称为节拍率(tick rate)。节拍率是通过静态预处理定义的,被定义为HZ。对于x86体系结构来说,它的HZ为100。
节拍: 两次时钟中断的间隔就称为节拍(tick)。可以看到,节拍等于节拍率分之一。
jiffies:全局变量jiffies用来记录系统自启动以来产生的节拍总数。通过jiffies/HZ就可获取系统自启动以来的秒数。
内核定时器使用一般有以下几个步骤:
1)定义一个timer_list;
static struct timer_list mytimer;
2)初始化定时器,即对timer_list结构中的相关字段进行赋值;
init_timer(&my_timer);
具体是这样的:
my_timer.expires = jiffies + dalay; /*定时器到期节拍数*/
如果jiffies计数值等于或者大于my_timer.expires时,由my_timer.function指向的处理函数就会开始运行。
my_timer.data = 0;/*给定时器处理函数传入0值*/
my_timer.function = my_function;/*给定时器到时时调用的函数*/
3)定义你自己的定时器处理函数;
void my_timer_function(unsigned long data);
4)激活定时器;
add_timer(&my_timer);
5) 修改定时器,如果有必要的话;
6) 删除定时器;
del_timer(&my_timer);
如果需要在定时器到期前停止定时器,使用此函数,被激活的或者未被激活的定时器都可以使用该函数删除,不需要为已经到期的定时器调用该函数,因为他们会被自动删除。
7)执行定时器;
内核在时钟中断发生后执行定时器,定时器在下班部中被run_time_list()执行。内核将定时器按照它们的到期时间分为5组,当定时器到期时间临近时,定时器将移到数组的下一个元素,这样可以提高搜索效率。
4.static LIST_HEAD(mine); /*sharelist头*/
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
INIT_LIST_HEAD函数将链表初始化为一个空链表,当前链表只含有头结点,头结点的prev和next域均指向头结点,其值等于头指针list。
#define LIST_HEAD_INIT(name) {&(name), &name}
#define LIST_HEAD(name) /
struct list_head name = LIST_HEAD_INIT(name)
第二个宏是一个嵌套定义的宏,它在定义一个新的宏的时候调用了已定义的第一个宏。宏#define LIST_HEAD(name)生成了一个头结点,宏#define LIST_HEAD_INIT(name)是对头结点name所表示的链表初始化,即将name的地址直接分别赋给那name.prev和name.next,形成一个空链表。因此,宏LIST_HEAD(name)的作用就是定义并初始化一个空的双向循环链表。
更多关于内核链表的分析,详见我对"list.h的分析"。
5.static DECLARE_MUTEX(sem); /*内核线程进行同步的信号量*/
信号量的定义如下:
Struct semaphore {
atomic_t count;
int sleepers;
wait_queue_head_t wait;
};
各个域的含义如下:
count:存放一个院子类型的值,如果该值大于0,那么资源就是空闲的,及资源可以使用;但如果count值 为0,那么信号量就是忙的,单位有进程等待这个被保护的资源;如果count为负数,则资源被占用,并且至少有一个进程在等待资源。
sleepers:存放一个标志,表示是否有一些进程在信号量上睡眠。
wait:存放等待队列链表的地址,当前等待该资源的所有睡眠进程都存放在这个链表中。
信号量的使用:
1)声明并初始化互斥信号量;
static DECLARE_MUTEX(sem);除了这个方法,还有以下方法:
sem_init(&sem,val); 其中val为信号量的初始值。
init_MUTEX(&sem); 适用于互斥信号量。
2)获获取指定的信号量;
down_interruptible(&sem);
获取失败,进程将以TASK_INTERRUPTIBLE状态睡眠,这种状态意味着任务可以被信号唤醒;若果该函数正常结束并得到了需要的资源,则返回0;
还有
down(&sem);
进程使用该函数时,如果信号量值此时为0,则该进车会进入睡眠状态,因此该函数不能用于中断上下文中。
3)释放指定的信号量;
up(&sem);如果此时等待队列中有进程,则唤醒一个
4)static spinlock_t my_lock = SPIN_LOCK_UNLOCKED; /*保护对链表的操作*/
自旋锁是专为防止多处理器并行而引入的一种锁,如果一个内核任务试图请求一个已被持有的自旋锁,那这个任务就会一直执行忙循环也就是自旋。自旋锁的作用是
在短时间内进行轻量级的锁定;忙等待,可用于中断上下文;持有锁后不可睡眠。
自旋锁的使用:
定义一个自旋锁:spinlock_t lock;
初始化自旋锁:spin_lock_init(lock);
获取自旋锁:spin_lock(lock);
释放自旋锁:spin_unlock(lock);
试图获取自旋锁:spin_trylock(lock);
注意问题:自旋锁实际上是忙等待,当锁不可用时,CPU一直循环执行"测试并设置"该锁直到可用而取得该锁。因此只有在占用锁的时间极短的情况下,使用自旋锁才是合理的,当临界区很大或共享设备的时候,需要较长时间占用锁,使用自旋锁会降低系统的性能。
6.static atomic_t my_count = ATOMIC_INIT(0); /*以原子方式进行追加*/
原子操作是在执行过程中不会被别的代码所中断的操作。原子整数操作最常见的用途就是实现计数器。使用复杂的锁机制来保护一个单纯的计数器是很低效的,所以开发者最好使用原子操作来操作计数器。
原子操作的有以下使用:
1)整形原子操作
(1)设置原子变量的值
void atomic_set(atomic_t *v, int i) //设置原子变量的值为i
atomic_t v = ATOMIC_INIT(0);//定义并初始化原子变量为
(2)获取原子变量的值
atomic_read(atomic_t *v); //读取原子变量的值
(3)原子变量的加/减
void atomic_add(int i, atomic_t *v);//原子变量加i
void atomic_sub(int i, atomic_t *v);//原子变量减i
(4)原子变量自加/自减
void atomic_inc(atomic_t *v);//原子变量加1
void atomic_dec(atomic_t *v);//原子变量减1
(5)操作并测试
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i,atomic_t *v);
(6)操作并返回(返回原子变量的值)
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
atomic_dec_return(atomic_t *v);
2)位原子操作
unsigned int nr;
volatile unsigned long *addr;
void set_bit(nr, addr);
void clear_bit(nr, addr);
void change_bit(nr, addr);
void test_bit(nr, addr);
int test_and_set_bit(nr, addr);
int test_and_clear_bit(nr, addr);
int test_and_change_bit(nr, addr);
7.函数static int share_init(void);
是模块注册函数,并且通过它初始化运行队列、启动定时器任务和内核线程。它首先初始化初始化工作队列queue,然后设置定时器mytimer并添加到定时器任务队列,接着一次启动200个内核线程。至此开始对链表进行操作。
8.函数static void share_exit(void);
是模块卸载函数,负责删除定时器并销毁链表。由于销毁时内核线程与定时器任务都在运行,所以应进行同步保护,即利用自旋锁锁着链表,利用自旋锁是因为自旋锁保证了任务执行的串行化,此刻其他任务都没有机会运行了,当重新打开自旋锁时,其他任务就可以运行。其中释放链表结点时,使用函数kfree()释放链表结点。
9.static int sharelist(void *data);
该函数是内核线程的执行函数,作用是向链表中加入新节点。为防止定时器任务队列抢占执行时间是造成链表数据不一致,需要在操作链表期间进行同步保护,即通过spin_lock(&my_lock)禁止或通过_unck(&my_lock)开启定时器任务执行权限。
10.static void start_kthread(void)
该函数负责调度工作队列,运行内核线程。
11.void kthread_launcher(struct work_struct *q);
该函数的作用仅仅是通过kernel_thread函数启动内核线程sharelist。为了能依次建立且启动内核线程,start_kernel函数会在任务加入调度队列后利用量进行自我阻塞down(&sem),直到内核线程执行后才解除阻塞即up(&sem),这种信号量同步机制保证了串行地创建内核线程。
12.void qt_task(unsigned long data);
该函数是定时器任务的任务函数,负责每个时钟节拍从链表头删除一个结点。当每次发生时钟中断时,定时器任务队列中的mytimer定时器的任务被执行,执行完删除链表结点操作后要修改定时器时间。
二.详细的调试过程
1.补充源程序sharelist.c。
2.编写Makefile文件。
3.插入模块insmod sharelist.ko
4.查看信息dmesg
5.卸载模块rmmod sharelist
运行结果:dmesg显示内容
[ 119.394859] share list enter
[ 119.413297]
[ 119.413307] THREAD ADD:0 THREAD ADD:1 THREAD ADD:2 THREAD ADD:3
[ 119.413391] THREAD ADD:4 THREAD ADD:5 THREAD ADD:6 THREAD ADD:7
[ 119.413447] THREAD ADD:8 THREAD ADD:9 THREAD ADD:10 THREAD ADD:11
[ 119.413505] THREAD ADD:12 THREAD ADD:13 THREAD ADD:14 THREAD ADD:15
[ 119.413562] THREAD ADD:16 THREAD ADD:17 THREAD ADD:18 THREAD ADD:19
[ 119.413619] THREAD ADD:20 THREAD ADD:21 THREAD ADD:22 THREAD ADD:23
[ 119.413675] THREAD ADD:24 THREAD ADD:25 THREAD ADD:26 THREAD ADD:27
[ 119.413746] THREAD ADD:28 THREAD ADD:29 THREAD ADD:30 THREAD ADD:31
[ 119.420254] THREAD ADD:32 THREAD ADD:33 THREAD ADD:34 THREAD ADD:35
[ 119.420309] THREAD ADD:36 THREAD ADD:37 THREAD ADD:38 THREAD ADD:39
[ 119.420365] THREAD ADD:40 THREAD ADD:41 THREAD ADD:42 THREAD ADD:43
[ 119.420420] THREAD ADD:44 THREAD ADD:45 THREAD ADD:46 THREAD ADD:47
[ 119.420474] THREAD ADD:48 THREAD ADD:49 THREAD ADD:50 THREAD ADD:51
[ 119.420528] THREAD ADD:52 THREAD ADD:53 THREAD ADD:54 THREAD ADD:55
[ 119.420579] THREAD ADD:56 THREAD ADD:57 THREAD ADD:58 THREAD ADD:59
[ 119.420628] THREAD ADD:60 THREAD ADD:61 THREAD ADD:62 THREAD ADD:63
[ 119.420679] THREAD ADD:64 THREAD ADD:65 THREAD ADD:66 THREAD ADD:67
[ 119.420732] THREAD ADD:68 THREAD ADD:69 THREAD ADD:70 THREAD ADD:71
[ 119.421876] THREAD ADD:72 THREAD ADD:73 THREAD ADD:74 THREAD ADD:75
[ 119.427536] THREAD ADD:76 THREAD ADD:77 THREAD ADD:78 THREAD ADD:79
[ 119.427604] THREAD ADD:80 THREAD ADD:81 THREAD ADD:82 THREAD ADD:83
[ 119.430958] THREAD ADD:84 THREAD ADD:85 THREAD ADD:86 THREAD ADD:87
[ 119.432028] THREAD ADD:88 THREAD ADD:89 THREAD ADD:90 THREAD ADD:91
[ 119.432860] THREAD ADD:92 THREAD ADD:93 THREAD ADD:94 THREAD ADD:95
[ 119.432910] THREAD ADD:96 THREAD ADD:97 THREAD ADD:98 THREAD ADD:99
[ 119.432961] THREAD DEL:0 THREAD ADD:100 THREAD DEL:1 THREAD ADD:101
[ 119.433010] THREAD DEL:2 THREAD ADD:102 THREAD DEL:3 THREAD ADD:103
[ 119.433107] THREAD DEL:4 THREAD ADD:104 THREAD DEL:5 THREAD ADD:105
[ 119.433294] THREAD DEL:6 THREAD ADD:106 THREAD DEL:7 THREAD ADD:107
[ 119.433492] THREAD DEL:8 THREAD ADD:108 THREAD DEL:9 THREAD ADD:109
[ 119.434080] THREAD DEL:10 THREAD ADD:110 THREAD DEL:11 THREAD ADD:111
[ 119.434186] THREAD DEL:12 THREAD ADD:112 THREAD DEL:13 THREAD ADD:113
[ 119.434374] THREAD DEL:14 THREAD ADD:114 THREAD DEL:15 THREAD ADD:115
[ 119.441300] THREAD DEL:16 THREAD ADD:116 THREAD DEL:17 THREAD ADD:117
[ 119.441369] THREAD DEL:18 THREAD ADD:118 THREAD DEL:19 THREAD ADD:119
[ 119.441429] THREAD DEL:20 THREAD ADD:120 THREAD DEL:21 THREAD ADD:121
[ 119.441488] THREAD DEL:22 THREAD ADD:122 THREAD DEL:23 THREAD ADD:123
[ 119.441607] THREAD DEL:24 THREAD ADD:124 THREAD DEL:25 THREAD ADD:125
[ 119.441670] THREAD DEL:26 THREAD ADD:126 THREAD DEL:27 THREAD ADD:127
[ 119.444181] THREAD DEL:28 THREAD ADD:128 THREAD DEL:29 THREAD ADD:129
[ 119.444270] THREAD DEL:30 THREAD ADD:130 THREAD DEL:31 THREAD ADD:131
[ 119.444323] THREAD DEL:32 THREAD ADD:132 THREAD DEL:33 THREAD ADD:133
[ 119.444370] THREAD DEL:34 THREAD ADD:134 THREAD DEL:35 THREAD ADD:135
[ 119.444419] THREAD DEL:36 THREAD ADD:136 THREAD DEL:37 THREAD ADD:137
[ 119.444597] THREAD DEL:38 THREAD ADD:138 THREAD DEL:39 THREAD ADD:139
[ 119.444645] THREAD DEL:40 THREAD ADD:140 THREAD DEL:41 THREAD ADD:141
[ 119.445247] THREAD DEL:42 THREAD ADD:142 THREAD DEL:43 THREAD ADD:143
[ 119.445340] THREAD DEL:44 THREAD ADD:144 THREAD DEL:45 THREAD ADD:145
[ 119.447681] THREAD DEL:46 THREAD ADD:146 THREAD DEL:47 THREAD ADD:147
[ 119.447737] THREAD DEL:48 THREAD ADD:148 THREAD DEL:49 THREAD ADD:149
[ 123.496862] share list exit
[ 123.496869]
[ 123.496873] SYSCALL DEL: 149SYSCALL DEL: 148SYSCALL DEL: 147SYSCALL DEL: 146
[ 123.496886] SYSCALL DEL: 145SYSCALL DEL: 144SYSCALL DEL: 143SYSCALL DEL: 142
[ 123.496898] SYSCALL DEL: 141SYSCALL DEL: 140SYSCALL DEL: 139SYSCALL DEL: 138
[ 123.496909] SYSCALL DEL: 137SYSCALL DEL: 136SYSCALL DEL: 135SYSCALL DEL: 134
[ 123.496920] SYSCALL DEL: 133SYSCALL DEL: 132SYSCALL DEL: 131SYSCALL DEL: 130
[ 123.496931] SYSCALL DEL: 129SYSCALL DEL: 128SYSCALL DEL: 127SYSCALL DEL: 126
[ 123.496942] SYSCALL DEL: 125SYSCALL DEL: 124SYSCALL DEL: 123SYSCALL DEL: 122
[ 123.496953] SYSCALL DEL: 121SYSCALL DEL: 120SYSCALL DEL: 119SYSCALL DEL: 118
[ 123.496964] SYSCALL DEL: 117SYSCALL DEL: 116SYSCALL DEL: 115SYSCALL DEL: 114
[ 123.496975] SYSCALL DEL: 113SYSCALL DEL: 112SYSCALL DEL: 111SYSCALL DEL: 110
[ 123.496985] SYSCALL DEL: 109SYSCALL DEL: 108SYSCALL DEL: 107SYSCALL DEL: 106
[ 123.496996] SYSCALL DEL: 105SYSCALL DEL: 104SYSCALL DEL: 103SYSCALL DEL: 102
[ 123.497008] SYSCALL DEL: 101SYSCALL DEL: 100SYSCALL DEL: 99SYSCALL DEL: 98
[ 123.497019] SYSCALL DEL: 97SYSCALL DEL: 96SYSCALL DEL: 95SYSCALL DEL: 94
[ 123.497029] SYSCALL DEL: 93SYSCALL DEL: 92SYSCALL DEL: 91SYSCALL DEL: 90
[ 123.497040] SYSCALL DEL: 89SYSCALL DEL: 88SYSCALL DEL: 87SYSCALL DEL: 86
[ 123.497051] SYSCALL DEL: 85SYSCALL DEL: 84SYSCALL DEL: 83SYSCALL DEL: 82
[ 123.497062] SYSCALL DEL: 81SYSCALL DEL: 80SYSCALL DEL: 79SYSCALL DEL: 78
[ 123.497072] SYSCALL DEL: 77SYSCALL DEL: 76SYSCALL DEL: 75SYSCALL DEL: 74
[ 123.497083] SYSCALL DEL: 73SYSCALL DEL: 72SYSCALL DEL: 71SYSCALL DEL: 70
[ 123.497094] SYSCALL DEL: 69SYSCALL DEL: 68SYSCALL DEL: 67SYSCALL DEL: 66
[ 123.497105] SYSCALL DEL: 65SYSCALL DEL: 64SYSCALL DEL: 63SYSCALL DEL: 62
[ 123.497116] SYSCALL DEL: 61SYSCALL DEL: 60SYSCALL DEL: 59SYSCALL DEL: 58
[ 123.497126] SYSCALL DEL: 57SYSCALL DEL: 56SYSCALL DEL: 55SYSCALL DEL: 54
[ 123.497137] SYSCALL DEL: 53SYSCALL DEL: 52SYSCALL DEL: 51SYSCALL DEL: 50
[ 123.497147] Over
源程序:

Makefile文件:

原创粉丝点击