第六节 组件 linked_list - 侵入式链表

链表是十分常用的数据结构,从实现方式上大致可以分成两类:

linked-list
  1. 左侧 - 普通链表:每个节点都占用独立的内存,包含指向数据块的指针 ,例如 Rust 标准库中提供的 LinkedList 类型。
  2. 右侧 - 侵入式链表:节点直接存在于目标数据块中,节点本身不需要单独申请内存,这种链表常用于维护空闲块。

本节我们来实现侵入式链表,它在后面内核的开发中非常重要,比如下一章中,我们就会把它作为实现 buddy 内存分配器的基础。

我们的侵入式链表的实现参考了 Sergio Benitez 的工作成果,见 CS140e

// buddy_allocator/src/linked_list.rs
#![no_std]

use core::marker::PhantomData;
use core::ptr;

#[derive(Copy, Clone)]
pub struct LinkedList {
    head: *mut usize,
}

unsafe impl Send for LinkedList {}

impl LinkedList {
    pub const fn new() -> LinkedList {
        LinkedList {
            head: ptr::null_mut(),
        }
    }
    pub fn is_empty(&self) -> bool {
        self.head.is_null()
    }
}

第 9 行:链表本身只有一个成员 head,它指向第一个节点,各节点之间通过指针关联。这方面仍然是典型链表的实现。

// buddy_allocator/src/linked_list.rs
impl LinkedList {
    pub unsafe fn push(&mut self, item: *mut usize) {
        *item = self.head as usize;
        self.head = item;
    }
    pub fn pop(&mut self) -> Option<*mut usize> {
        match self.is_empty() {
            true => None,
            false => {
                let item = self.head;
                self.head = unsafe { *item as *mut usize };
                Some(item)
            }
        }
    }
}

第 3~6 行:push 操作是把新的节点插入到链表的开头。

第 7~17 行:pop 操作先处理链表空的情况;然后如果链表不空,就把第一个节点摘出并返回。

综合来看,push/pop 都是针对链表头这端,即后入先出,该链表相当于栈。

下面为链表实现只读和可修改的迭代器:

// buddy_allocator/src/linked_list.rs
impl LinkedList {
    pub fn iter(&self) -> Iter {
        Iter {
            curr: self.head,
            list: PhantomData,
        }
    }
    pub fn iter_mut(&mut self) -> IterMut {
        IterMut {
            prev: &mut self.head as *mut *mut usize as *mut usize,
            curr: self.head,
            list: PhantomData,
        }
    }
}

第 3~8 行:Iter 迭代器比较简单,只需要一个指向当前位置的指针。

第 9~15 行:IterMut 的迭代器,需要支持把节点弹出链表的操作,为此,除了当前位置指针,还需要前一个节点的指针 prev - 即指向当前位置指针的指针,这样通过简单的修改 prev 就能从链表中删除掉当前节点。

Iter 迭代器的实现:

// buddy_allocator/src/linked_list.rs
pub struct Iter<'a> {
    curr: *mut usize,
    list: PhantomData<&'a LinkedList>,
}
impl<'a> Iterator for Iter<'a> {
    type Item = *mut usize;

    fn next(&mut self) -> Option<Self::Item> {
        if self.curr.is_null() {
            None
        } else {
            let item = self.curr;
            let next = unsafe { *item as *mut usize };
            self.curr = next;
            Some(item)
        }
    }
}

IterMut 迭代器的实现:

// buddy_allocator/src/linked_list.rs
pub struct IterMut<'a> {
    list: PhantomData<&'a mut LinkedList>,
    prev: *mut usize,
    curr: *mut usize,
}
impl<'a> Iterator for IterMut<'a> {
    type Item = ListNode;

    fn next(&mut self) -> Option<Self::Item> {
        if self.curr.is_null() {
            None
        } else {
            let res = ListNode {
                prev: self.prev,
                curr: self.curr,
            };
            self.prev = self.curr;
            self.curr = unsafe { *self.curr as *mut usize };
            Some(res)
        }
    }
}

pub struct ListNode {
    prev: *mut usize,
    curr: *mut usize,
}
impl ListNode {
    pub fn pop(self) -> *mut usize {
        unsafe {
            *(self.prev) = *(self.curr);
        }
        self.curr
    }
    pub fn value(&self) -> *mut usize {
        self.curr
    }
}

第 25~39 行:IterMut 迭代器需要额外定义一个辅助类型 ListNode。

现在我们可以编写一组测试用例,验证侵入式链表的功能:

#[test]
fn test_linked_list() {
    let mut value1: usize = 0;
    let mut value2: usize = 0;
    let mut value3: usize = 0;
    let mut list = linked_list::LinkedList::new();
    unsafe {
        list.push(&mut value1 as *mut usize);
        list.push(&mut value2 as *mut usize);
        list.push(&mut value3 as *mut usize);
    }

    assert_eq!(value3, &value2 as *const usize as usize);
    assert_eq!(value2, &value1 as *const usize as usize);
    assert_eq!(value1, 0);

    let mut iter = list.iter();
    assert_eq!(iter.next(), Some(&mut value3 as *mut usize));
    assert_eq!(iter.next(), Some(&mut value2 as *mut usize));
    assert_eq!(iter.next(), Some(&mut value1 as *mut usize));
    assert_eq!(iter.next(), None);

    let mut iter_mut = list.iter_mut();
    assert_eq!(iter_mut.next().unwrap().pop(), &mut value3 as *mut usize);
    assert_eq!(list.pop(), Some(&mut value2 as *mut usize));
    assert_eq!(list.pop(), Some(&mut value1 as *mut usize));
    assert_eq!(list.pop(), None);
}

执行 make test,看到测试用例执行结果,验证成功!