本文爲實現雙向鏈表,爲以後的 就緒隊列、全部任務隊列準備數據結構。
一共包含兩個文件,放在 /lib/kernel 目錄下。
list.h
#ifndef __LIB_KERNEL_LIST_H
#define __LIB_KERNEL_LIST_H
#define NULL ((void*)0)
#define bool int
#define true 1
#define false 0
#define offset(struct_type,member) (int)(&((struct_type*)0)->member)
#define elem2entry(struct_type, struct_member_name, elem_ptr) \
(struct_type*)((int)elem_ptr - offset(struct_type, struct_member_name))
/********** 定義鏈表結點成員結構 ***********
*結點中不需要數據成元,只要求前驅和後繼結點指針*/
struct list_elem {
struct list_elem* prev; // 前軀結點
struct list_elem* next; // 後繼結點
};
/* 鏈表結構,用來實現隊列 */
struct list {
/* head是隊首,是固定不變的,不是第1個元素,第1個元素爲head.next */
struct list_elem head;
/* tail是隊尾,同樣是固定不變的 */
struct list_elem tail;
};
/* 自定義函數類型function,用於在list_traversal中做回調函數 */
typedef bool (function)(struct list_elem*, int arg);
void list_init (struct list*);
void list_insert_before(struct list_elem* before, struct list_elem* elem);
void list_push(struct list* plist, struct list_elem* elem);
void list_iterate(struct list* plist);
void list_append(struct list* plist, struct list_elem* elem);
void list_remove(struct list_elem* pelem);
struct list_elem* list_pop(struct list* plist);
bool list_empty(struct list* plist);
uint32_t list_len(struct list* plist);
struct list_elem* list_traversal(struct list* plist, function func, int arg);
bool elem_find(struct list* plist, struct list_elem* obj_elem);
#endif
(1)先是一些宏定義。
(2)list_elem 是鏈表的結點。看着是不是有點怪異,這個結點沒有數據項,只有兩個指針,指向 前驅 和 後繼。
(3)list 是鏈表結構,裏面有固定的隊首和隊尾。隊首的後繼纔是隊列的第一個元素,隊尾的前驅纔是隊列的最後一個元素。
(4)其次就是函數的聲明。
接下來是 list.c
list.c
#include "../std_int.h"
#include "list.h"
#include "../../kernel/my_interrupt.h"
/* 初始化雙向鏈表list */
void list_init (struct list* list) {
list->head.prev = NULL;
list->head.next = &list->tail;
list->tail.prev = &list->head;
list->tail.next = NULL;
}
/* 把鏈表元素elem插入在元素before之前 */
void list_insert_before(struct list_elem* before, struct list_elem* elem) {
enum intr_status old_status = intr_disable();
/* 將before前驅元素的後繼元素更新爲elem, 暫時使before脫離鏈表*/
before->prev->next = elem;
/* 更新elem自己的前驅結點爲before的前驅,
* 更新elem自己的後繼結點爲before, 於是before又回到鏈表 */
elem->prev = before->prev;
elem->next = before;
/* 更新before的前驅結點爲elem */
before->prev = elem;
intr_set_status(old_status);
}
/* 添加元素到列表隊首,類似棧push操作 */
void list_push(struct list* plist, struct list_elem* elem) {
list_insert_before(plist->head.next, elem); // 在隊頭插入elem
}
/* 追加元素到鏈表隊尾,類似隊列的先進先出操作 */
void list_append(struct list* plist, struct list_elem* elem) {
list_insert_before(&plist->tail, elem); // 在隊尾的前面插入
}
/* 使元素pelem脫離鏈表 */
void list_remove(struct list_elem* pelem) {
enum intr_status old_status = intr_disable();
pelem->prev->next = pelem->next;
pelem->next->prev = pelem->prev;
intr_set_status(old_status);
}
/* 將鏈表第一個元素彈出並返回,類似棧的pop操作 */
struct list_elem* list_pop(struct list* plist) {
struct list_elem* elem = plist->head.next;
list_remove(elem);
return elem;
}
/* 從鏈表中查找元素obj_elem,成功時返回true,失敗時返回false */
bool elem_find(struct list* plist, struct list_elem* obj_elem) {
struct list_elem* elem = plist->head.next;
while (elem != &plist->tail) {
if (elem == obj_elem) {
return true;
}
elem = elem->next;
}
return false;
}
/* 把列表plist中的每個元素elem和arg傳給回調函數func,
* arg給func用來判斷elem是否符合條件.
* 本函數的功能是遍歷列表內所有元素,逐個判斷是否有符合條件的元素。
* 找到符合條件的元素返回元素指針,否則返回NULL. */
struct list_elem* list_traversal(struct list* plist, function func, int arg) {
struct list_elem* elem = plist->head.next;
/* 如果隊列爲空,就必然沒有符合條件的結點,故直接返回NULL */
if (list_empty(plist)) {
return NULL;
}
while (elem != &plist->tail) {
if (func(elem, arg)) { // func返回ture則認爲該元素在回調函數中符合條件,命中,故停止繼續遍歷
return elem;
} // 若回調函數func返回true,則繼續遍歷
elem = elem->next;
}
return NULL;
}
/* 返回鏈表長度 */
uint32_t list_len(struct list* plist) {
struct list_elem* elem = plist->head.next;
uint32_t length = 0;
while (elem != &plist->tail) {
length++;
elem = elem->next;
}
return length;
}
/* 判斷鏈表是否爲空,空時返回true,否則返回false */
bool list_empty(struct list* plist) { // 判斷隊列是否爲空
return (plist->head.next == &plist->tail ? true : false);
}
都是數據結構中鏈表的基本操作。不再贅述。