---
tags: linux2023
---
# 2023q1 Homework1 (quiz1)
contributed by < [KHLee529](https://github.com/KHLee529) >
> [測驗問題](/@sysprog/linux2023-quiz1)
## 測驗 `1`
### 作答結果
- `AAA`: `struct item`
- `BBB`: `list`
- `CCC`: `list_for_each_entry_safe`
- `DDD`: `list_move_tail`
- `EEE`: `list_move_tail`
- `FFF`: `list_splice_tail`
:::spoiler 程式碼
```c
#include <stdint.h>
#include "list.h"
struct item {
uint16_t i;
struct list_head list;
};
static inline int cmpint(const void *p1, const void *p2)
{
const uint16_t *i1 = (const uint16_t *) p1;
const uint16_t *i2 = (const uint16_t *) p2;
return *i1 - *i2;
}
static void list_sort(struct list_head *head)
{
if (list_empty(head) || list_is_singular(head))
return;
struct list_head list_less, list_greater;
INIT_LIST_HEAD(&list_less);
INIT_LIST_HEAD(&list_greater);
struct item *pivot = list_first_entry(head, struct item, list);
list_del(&pivot->list);
struct item *itm = NULL, *is = NULL;
list_for_each_entry_safe (itm, is, head, list) {
if (cmpint(&itm->i, &pivot->i) < 0)
list_move_tail (&itm->list, &list_less);
else
list_move_tail (&itm->list, &list_greater);
}
list_sort(&list_less);
list_sort(&list_greater);
list_add(&pivot->list, head);
list_splice(&list_less, head);
list_splice_tail(&list_greater, head);
}
```
:::
### 程式運作原理
首先以一個長度為 5 的串列進行演算法的演示如下所示。
```graphviz
digraph {
label="Initial"
rankdir=LR;
node[shape=record];
h[label="head|{<p>prev|<x>next} "];
n1[label="node 1|{<p>prev|<x>next} "];
n2[label="node 2|{<p>prev|<x>next} "];
n3[label="node 3|{<p>prev|<x>next} "];
n4[label="node 4|{<p>prev|<x>next} "];
n5[label="node 5|{<p>prev|<x>next} "];
h:x->n3:w
n3:x->n4:w
n4:x->n1:w
n1:x->n2:w
n2:x->n5:w
n5:p->n2:e
n2:p->n1:e
n1:p->n4:e
n4:p->n3:e
n3:p->h:e
}
```
首先選擇第一個節點作為 pivot,用於將剩餘的節點做大小分類。
```graphviz
digraph {
label="Take first node as pivot"
rankdir=LR;
node[shape=record];
h[label="head|{<p>prev|<x>next}"];
n1[label="node 1|{<p>prev|<x>next}"];
n2[label="node 2|{<p>prev|<x>next}"];
n3[label="node 3|{<p>prev|<x>next}"];
n4[label="node 4|{<p>prev|<x>next}"];
n5[label="node 5|{<p>prev|<x>next}"];
p[label="pivot", shape=plaintext]
p->n3:w
h:x->n4:w
n4:x->n1:w
n1:x->n2:w
n2:x->n5:w
n5:p->n2:e
n2:p->n1:e
n1:p->n4:e
n4:p->h:e
}
```
接著逐一走過每一個節點,按照與 pivot 節點的值大小比較分成 `list_greater` 與 `list_less` 兩條。之後遞迴的對於這兩個串列再進行排序。
**注意**: 由於需要確保 stable sorting 的性質,必須維持原先的順序,故 `DDD` 與 `EEE` 需使用 `list_move_tail` 而非 `list_move`。
```graphviz
digraph {
label="Separate list into two list \n <list_greater> and <list_less> and sort them"
rankdir=LR;
node[shape=record];
h[label="head|{<p>prev|<x>next}"];
n1[label="node 1|{<p>prev|<x>next}"];
n2[label="node 2|{<p>prev|<x>next}"];
n3[label="node 3|{<p>prev|<x>next}"];
n4[label="node 4|{<p>prev|<x>next}"];
n5[label="node 5|{<p>prev|<x>next}"];
p[label="pivot", shape=plaintext]
ll[label="list_less", shape=plaintext]
lg[label="list_greater", shape=plaintext]
h
p->n3:w
lg->n4:w
n4:x->n5:w
ll->n1:w
n1:x->n2:w
n5:p->n4:e
n2:p->n1:e
}
```
最後依序將排序好的 `list_less`、`pivot`、排序好的 `list_greater` 接回原本的串列便完成整個串列的排序。
```graphviz
digraph {
label="Merge them all in the order\n<list_less>-<pivot>-<list_greater>"
rankdir=LR;
node[shape=record];
h[label="head|{<p>prev|<x>next}"];
n1[label="node 1|{<p>prev|<x>next}"];
n2[label="node 2|{<p>prev|<x>next}"];
n3[label="node 3|{<p>prev|<x>next}"];
n4[label="node 4|{<p>prev|<x>next}"];
n5[label="node 5|{<p>prev|<x>next}"];
p[label="pivot", shape=plaintext]
ll[label="list_less", shape=plaintext]
lg[label="list_greater", shape=plaintext]
p->n3:n
ll->n1:n
lg->n4:n
h:x->n1:w
n1:x->n2:w
n2:x->n3:w
n3:x->n4:w
n4:x->n5:w
n5:p->n4:e
n4:p->n3:e
n3:p->n2:e
n2:p->n1:e
n1:p->h:e
}
```
## 測驗 `2`
### 作答結果
- `GGGG`: `list_for_each_entry_safe`
- `HHHH`: `list_add_tail`
- `IIII`: `&stack[++top]`
- `JJJJ`: `&stack[++top]`
- `KKKK`: `&stack[top--]`
:::spoiler 程式碼
```c
#define MAX_DEPTH 512
static void list_sort_nr(struct list_head *head)
{
if (list_empty(head) || list_is_singular(head))
return;
struct list_head stack[MAX_DEPTH];
for (int i = 0; i < MAX_DEPTH; i++)
INIT_LIST_HEAD(&stack[i]);
int top = -1;
/* add the origin list into stack */
list_splice_init(head, &stack[++top]);
struct list_head partition;
INIT_LIST_HEAD(&partition);
/* process while stack are not empty */
while (top >= 0) {
INIT_LIST_HEAD(&partition);
/* take the first list in stack to process */
list_splice_init(&stack[top--], &partition);
if (!list_empty(&partition) && !list_is_singular(&partition)) {
struct list_head list_less, list_greater;
INIT_LIST_HEAD(&list_less);
INIT_LIST_HEAD(&list_greater);
struct item *pivot =
list_first_entry(&partition, struct item, list);
list_del(&pivot->list);
INIT_LIST_HEAD(&pivot->list);
struct item *itm = NULL, *is = NULL;
/* separate list into greater and less comparing to pivot */
list_for_each_entry_safe (itm, is, &partition, list) {
list_del(&itm->list);
if (cmpint(&itm->i, &pivot->i) < 0)
list_move(&itm->list, &list_less);
else
list_move(&itm->list, &list_greater);
}
list_add_tail (&pivot->list, &list_less);
if (!list_empty(&list_greater))
list_splice_tail(&list_greater, &stack[++top]);
if (!list_empty(&list_less))
list_splice_tail(&list_less, &stack[++top]);
} else {
/* if partition is empty or singular */
top++;
list_splice_tail(&partition, &stack[top]);
while (top >= 0 && list_is_singular(&stack[top])) {
struct item *tmp =
list_first_entry(&stack[top], struct item, list);
list_del(&tmp->list);
INIT_LIST_HEAD(&stack[top--]);
list_add_tail(&tmp->list, head);
}
}
}
}
```
:::
### 程式運作原理
以一個六個節點的串列進行演示。
首先將整個串列加入堆疊 (stack) 的頂端,如下圖所示,開始進行迭代式的快速排序。
```graphviz
digraph {
node[shape=record];
rankdir=LR;
h[label="head|{<p>prev|<x>next}"]
stack[shape=plaintext]
s[label="<1>top|<2>|<3>|<4>|<5>|<6>|..."]
n1[label="1|{<p>prev|<x>next}"]
n2[label="2|{<p>prev|<x>next}"]
n3[label="3|{<p>prev|<x>next}"]
n4[label="4|{<p>prev|<x>next}"]
n5[label="5|{<p>prev|<x>next}"]
n6[label="6|{<p>prev|<x>next}"]
stack->s:n
s:1->n3:w
n3:x->n4:w
n4:x->n6:w
n6:x->n5:w
n5:x->n2:w
n2:x->n1:w
n1:p->n2:e
n2:p->n5:e
n5:p->n6:e
n6:p->n4:e
n4:p->n3:e
n3:p->s:1
}
```
取出位在堆疊頂部的串列,並取第一個節點作為 pivot 後,將整個串列根據其值與 pivot 的先後關係分成 `list_greater` 與 `list_less`。
```graphviz
digraph {
node[shape=record];
rankdir=LR;
h[label="head|{<p>prev|<x>next}"]
s[label="<1>|<2>|<3>|<4>|<5>|<6>|..."]
stack[shape=plaintext]
n2[label="2|{<p>prev|<x>next}"]
n1[label="1|{<p>prev|<x>next}"]
n3[label="3|{<p>prev|<x>next}"]
n4[label="4|{<p>prev|<x>next}"]
n5[label="5|{<p>prev|<x>next}"]
n6[label="6|{<p>prev|<x>next}"]
p[label="pivot" shape=plaintext]
lg[label="list_greater" shape=plaintext]
ll[label="list_less" shape=plaintext]
stack->s:n
p->n3:w
lg->n4:w
n4:x->n6:w
n6:x->n5:w
ll->n2:w
n2:x->n1:w
n5:p->n6:e
n6:p->n4:e
n1:p->n2:e
}
```
接著將 pivot 接上 `list_less` 的最後端後將 `list_greater` 與 `list_less` 分別推入堆疊中。
```graphviz
digraph {
node[shape=record];
rankdir=LR;
h[label="head|{<p>prev|<x>next}"]
s[label="<1>|<2>top|<3>|<4>|<5>|<6>|..."]
stack[shape=plaintext]
n4[label="4|{<p>prev|<x>next}"]
n5[label="5|{<p>prev|<x>next}"]
n6[label="6|{<p>prev|<x>next}"]
n2[label="2|{<p>prev|<x>next}"]
n1[label="1|{<p>prev|<x>next}"]
n3[label="3|{<p>prev|<x>next}"]
//p[label="pivot" shape=plaintext]
//lg[label="list_greater" shape=plaintext]
//ll[label="list_less" shape=plaintext]
stack->s:n
s:1->n4:w
n4:x->n6:w
n6:x->n5:w
s:2->n2:w
n2:x->n1:w
n1:x->n3:w
n5:p->n6:e
n6:p->n4:e
n1:p->n2:e
n3:p->n1:e
}
```
接著進入下一次迭代,再將原先在堆疊頂端的串列取出,進行與前述相同的操作後放回堆疊。
```graphviz
digraph {
node[shape=record];
rankdir=LR;
h[label="head|{<p>prev|<x>next}"]
s[label="<1>|<2>|<3>top|<4>|<5>|<6>|..."]
stack[shape=plaintext]
n4[label="4|{<p>prev|<x>next}"]
n5[label="5|{<p>prev|<x>next}"]
n6[label="6|{<p>prev|<x>next}"]
n2[label="2|{<p>prev|<x>next}"]
n1[label="1|{<p>prev|<x>next}"]
n3[label="3|{<p>prev|<x>next}"]
//p[label="pivot" shape=plaintext]
//lg[label="list_greater" shape=plaintext]
//ll[label="list_less" shape=plaintext]
stack->s:n
s:1->n4:w
n4:x->n6:w
n6:x->n5:w
s:3->n1:w
n1:x->n2:w
s:2->n3:w
n5:p->n6:e
n6:p->n4:e
n2:p->n1:e
}
```
再進行一次相同的流程。
```graphviz
digraph {
node[shape=record];
rankdir=LR;
h[label="head|{<p>prev|<x>next}"]
s[label="<1>|<2>|<3>|<4>top|<5>|<6>|..."]
stack[shape=plaintext]
n4[label="4|{<p>prev|<x>next}"]
n5[label="5|{<p>prev|<x>next}"]
n6[label="6|{<p>prev|<x>next}"]
n2[label="2|{<p>prev|<x>next}"]
n1[label="1|{<p>prev|<x>next}"]
n3[label="3|{<p>prev|<x>next}"]
//p[label="pivot" shape=plaintext]
//lg[label="list_greater" shape=plaintext]
//ll[label="list_less" shape=plaintext]
stack->s:n
s:1->n4:w
n4:x->n6:w
n6:x->n5:w
s:3->n2:w
s:4->n1:w
s:2->n3:w
n5:p->n6:e
n6:p->n4:e
}
```
此時由於堆疊頂端的串列僅有一個元素,此時依序將只有一個元素的串列接上 `head`。
```graphviz
digraph {
node[shape=record];
rankdir=LR;
h[label="head|{<p>prev|<x>next}"]
s[label="<1>top|<2>|<3>|<4>|<5>|<6>|..."]
stack[shape=plaintext]
n4[label="4|{<p>prev|<x>next}"]
n5[label="5|{<p>prev|<x>next}"]
n6[label="6|{<p>prev|<x>next}"]
n2[label="2|{<p>prev|<x>next}"]
n1[label="1|{<p>prev|<x>next}"]
n3[label="3|{<p>prev|<x>next}"]
//p[label="pivot" shape=plaintext]
//lg[label="list_greater" shape=plaintext]
//ll[label="list_less" shape=plaintext]
stack->s:n
s:1->n4:w
n4:x->n6:w
n6:x->n5:w
n5:p->n6:e
n6:p->n4:e
h:x->n1:w
n1:x->n2:w
n2:x->n3:w
n3:p->n2:e
n2:p->n1:e
n1:p->h:e
}
```
對剩下來的串列再做一次上述的拆分流程。
```graphviz
digraph {
node[shape=record];
rankdir=LR;
h[label="head|{<p>prev|<x>next}"]
s[label="<1>|<2>|<3>top|<4>|<5>|<6>|..."]
stack[shape=plaintext]
n4[label="4|{<p>prev|<x>next}"]
n5[label="5|{<p>prev|<x>next}"]
n6[label="6|{<p>prev|<x>next}"]
n2[label="2|{<p>prev|<x>next}"]
n1[label="1|{<p>prev|<x>next}"]
n3[label="3|{<p>prev|<x>next}"]
//p[label="pivot" shape=plaintext]
//lg[label="list_greater" shape=plaintext]
//ll[label="list_less" shape=plaintext]
stack->s:n
s:1->n6:w
s:2->n5:w
s:3->n4:w
h:x->n1:w
n1:x->n2:w
n2:x->n3:w
n3:p->n2:e
n2:p->n1:e
n1:p->h:e
}
```
最後當堆疊清空時迭代結束,完成排序。
```graphviz
digraph {
node[shape=record];
rankdir=LR;
h[label="head|{<p>prev|<x>next}"]
s[label="<1>|<2>|<3>|<4>|<5>|<6>|..."]
stack[shape=plaintext]
n4[label="4|{<p>prev|<x>next}"]
n5[label="5|{<p>prev|<x>next}"]
n6[label="6|{<p>prev|<x>next}"]
n2[label="2|{<p>prev|<x>next}"]
n1[label="1|{<p>prev|<x>next}"]
n3[label="3|{<p>prev|<x>next}"]
//p[label="pivot" shape=plaintext]
//lg[label="list_greater" shape=plaintext]
//ll[label="list_less" shape=plaintext]
stack->s:n
h:x->n1:w
n1:x->n2:w
n2:x->n3:w
n3:x->n4:w
n4:x->n5:w
n5:x->n6:w
n6:p->n5:e
n5:p->n4:e
n4:p->n3:e
n3:p->n2:e
n2:p->n1:e
n1:p->h:e
}
```
### 程式實作缺失
1. 在拆分為 `pivot`、`list_greater`、`list_less` 的三個部份後,由於已經確定 `list_less` 的所有節點值都小於 `pivot`,應將 `pivot` 節點單獨推入堆疊中,否則會增加所需要的比較次數以及操作次數,修改如下
```diff
diff --git a/origin.c b/modified.c
index 62b862c..b056693 100644
--- a/origin.c
+++ b/modified.c
@@ -36,9 +36,9 @@ static void list_sort_nr(struct list_head *head) {
list_move(&itm->list, &list_greater);
}
- list_add_tail(&pivot->list, &list_less);
if (!list_empty(&list_greater))
list_splice_tail(&list_greater, &stack[++top]);
+ list_add(&pivot->list, &stack[++top]);
if (!list_empty(&list_less))
list_splice_tail(&list_less, &stack[++top]);
} else {
```
## 測驗 `3`
### 作答結果
- `LLLL`: `(uintptr_t) (a) ^ (uintptr_t) (b)`
- `MMMM`: `node`
- `PPPP`: `real_next->cmp`
:::spoiler 程式碼
```c
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct _xorlist_node {
struct _xorlist_node *cmp;
} xor_node_t;
typedef struct _xor_list_struct {
xor_node_t head, tail;
uint32_t count;
} xor_list_t;
#define XOR_COMP(a, b) ((xor_node_t *) ((uintptr_t) a ^ (uintptr_t) b))
static inline xor_node_t *address_of(xor_node_t *n1, xor_node_t *n2)
{
assert(n1 && n2);
return XOR_COMP(n1, n2);
}
#define container_of(ptr, type, member) \
__extension__({ \
const __typeof__(((type *) 0)->member) *__pmember = (ptr); \
(type *) ((char *) __pmember - offsetof(type, member)); \
})
#define xorlist_for_each(node, rp, rn, list) \
for (rp = &(list)->head, node = rp->cmp; node != &(list)->tail; \
rn = address_of(rp, node->cmp), rp = node, node = rn)
#define xorlist_for_each_prev(node, rp, rn, list) \
for (rp = &(list)->tail, node = rp->cmp; node != &(list)->head; \
rn = address_of(rp, node->cmp), rp = node, node = rn)
#define xorlist_for_each_from(node, pos1, pos2, rp, rn, list) \
for (rp = pos2, node = pos1; node != &(list)->tail; \
rn = address_of(rp, node->cmp), rp = node, node = rn)
#define xorlist_for_each_from_prev(node, pos1, pos2, rp, rn, list) \
for (rp = pos1, node = pos2; node != &(list)->head; \
rn = address_of(rp, node->cmp), rp = node, node = rn)
/* Note that when the delete function success is must return 0. */
#define xorlist_delete_prototype(name, node) \
int _xorlist_delete_##name(xor_node_t *node)
#define xorlist_delete_call(name) _xorlist_delete_##name
static inline xor_node_t *xornode_init(xor_node_t *n)
{
assert(n);
n->cmp = NULL;
return n;
}
#define XORNODE_INIT(n) \
do { \
(n).cmp = NULL; \
} while (0)
#define XORLIST_INIT(h) \
do { \
(h).head.cmp = &(h).tail; \
(h).tail.cmp = &(h).head; \
(h).count = 0; \
} while (0)
int xorlist_add(xor_list_t *list, xor_node_t *n)
{
xor_node_t *real_next;
if (!n)
return ENOMEM;
xor_node_t *real_prev = &list->head;
xor_node_t *node = real_prev->cmp;
if (node == &list->tail)
real_next = node;
else
real_next = node;
real_prev->cmp = n;
n->cmp = XOR_COMP(real_prev, real_next);
real_next->cmp = XOR_COMP(n, XOR_COMP(real_prev, real_next->cmp);
list->count++;
return 0;
}
int xorlist_del(xor_list_t *list,
xor_node_t *n,
xor_node_t *target,
int (*delete_func)(xor_node_t *))
{
assert(list && n && target && delete_func);
assert(&list->head != target && &list->tail != target);
xor_node_t *nn = address_of(target, n->cmp);
xor_node_t *an = address_of(n, target->cmp);
xor_node_t *ana = address_of(target, an->cmp);
n->cmp = XOR_COMP(nn, an);
an->cmp = XOR_COMP(n, ana);
delete_func(target);
list->count--;
return 0;
}
int xorlist_destroy(xor_list_t *list, int (*delete_func)(xor_node_t *))
{
assert(delete_func);
xor_node_t *real_prev = &list->head;
xor_node_t *node = real_prev->cmp;
xor_node_t *real_next = address_of(real_prev, node->cmp);
xor_node_t *tmp = real_prev;
real_prev = node;
node = real_next;
while (node != &list->tail) {
real_next = address_of(real_prev, node->cmp);
tmp = real_prev;
real_prev = node;
node = real_next;
if (delete_func(tmp) != 0)
perror("delete function failed");
}
return 0;
}
struct test_node {
int value;
xor_node_t xornode;
};
xorlist_delete_prototype(test_delete, _node)
{
struct test_node *node = container_of(_node, struct test_node, xornode);
free(node);
return 0;
}
int main(void)
{
xor_list_t list;
xor_node_t *p1, *p2;
XORLIST_INIT(list);
for (int i = 0; i < 1000; i++) {
struct test_node *new = malloc(sizeof(struct test_node));
xornode_init(&new->xornode);
new->value = i;
xorlist_add(&list, &new->xornode);
if (i == 5)
p1 = &new->xornode;
if (i == 6)
p2 = &new->xornode;
}
xor_node_t *real_prev, *real_next, *node;
int i = 0;
printf("xorlist_for_each test\n");
xorlist_for_each(node, real_prev, real_next, &list)
{
printf("node [%d] %d\n", i++,
container_of(node, struct test_node, xornode)->value);
}
i = 0;
printf("xorlist_for_from test\n");
xorlist_for_each_from(node, p1, p2, real_prev, real_next, &list)
{
printf("node %d\n",
container_of(node, struct test_node, xornode)->value);
}
i = 0;
printf("xorlist_for_each_from_prev test\n");
xorlist_for_each_from_prev(node, p1, p2, real_prev, real_next, &list)
{
printf("node [%d] %d\n", i++,
container_of(node, struct test_node, xornode)->value);
}
i = 0;
printf("xorlist_for_each_prev test\n");
xorlist_for_each_prev(node, real_prev, real_next, &list)
{
printf("node [%d] %d\n", i++,
container_of(node, struct test_node, xornode)->value);
}
printf("xorlist_del test\n");
xorlist_del(&list, p2, p1, xorlist_delete_call(test_delete));
i = 0;
xorlist_for_each(node, real_prev, real_next, &list)
{
printf("node [%d] %d\n", i++,
container_of(node, struct test_node, xornode)->value);
}
xorlist_destroy(&list, xorlist_delete_call(test_delete));
return 0;
}
```
:::
### 程式運作原理
以下示意圖表示實作的 Xorlist 在包含 0, 1, 2 個節點時分別的狀況。
當零個節點時,頭尾兩個成員分別在 `cmp` 持有對方的位址。
```graphviz
digraph {
node[shape=record]
rankdir=LR;
subgraph cluster_list{
label="list"
lh[label="head|&tail"]
lt[label="tail|&head"]
cnt[label="count|0"]
}
}
```
當一個節點時,頭尾兩個成員分別紀錄唯一的一個節點的位址。由此透過比對頭尾成員保存的位址是否相同,便可知道是否為長度為 1 的串列。
而此時唯一的成員 `node1` 的 `cmp` 成員紀錄 `head` 與 `tail` 兩個串列成員的位址進行 xor 運算後的結果。
```graphviz
digraph {
node[shape=record]
rankdir=LR;
subgraph cluster_list{
label="list"
lh[label="head|&node1"]
lt[label="tail|&node1"]
cnt[label="count|1"]
}
n1[label="node1|&head ^ &tail"]
lh:e->n1:w
n1:s->lt:e
}
```
當串列包含兩個節點時的狀況如下,除了 `head`、`tail` 成員以外的節點均紀錄其前後節點的地址 xor 運算結果。
```graphviz
digraph {
node[shape=record]
rankdir=LR;
subgraph cluster_list{
label="list"
lh[label="head|&node1"]
lt[label="tail|&node2"]
cnt[label="count|2"]
}
n1[label="node1|&head ^ &node2"]
n2[label="node2|&node1 ^ &tail"]
lh:e->n1:w
n1:e->n2:w
n2:s->lt:e
}
```
### 程式實作缺失
在加入節點的程式碼當中並不需要根據