2022q1 Homework1 (lab0)
contributed by < hsuedw
>
Resolve issue when I setup my development environment.
issue 1: Instead of password, GitHub accespts Personal Access Tokens (PAT) when I push code to my repository.
issue 2: When installing the latest cppcheck, I had problems.
- This my solution for installing cppcheck 2.7 on Ubuntu 20.04
- Uninstall the old cppcheck (1.90)
$ sudo apt remove cppcheck
- To avoid bulid fail by some missing header files, install this package
$ sudo apt-get install libpcre3-dev
- Download the latest cppcheck from Unbuntu.
$ wget https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/cppcheck/2.7-1/cppcheck_2.7.orig.tar.gz
- Unpack the tarball.
$ tar -vxf cppcheck_2.7.orig.tar.gz
- After reading the Makefile and read.md of cppcheck, I realized the following command is the command for building it from source code.
$ sudo make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function" install
- Then, I have cppcheck 2.7 installed on my computer.
$ cppcheck --version Cppcheck 2.7
- After cppcheck 2.7 is installed, don't run
apt install
to install cppcheck again. Otherwise, the old version (1.90) will come back.
sudo apt install cppcheck
(Do not run this!)
Implement a queue by using linked list
TODO list
q_size()
commit: bdacefa
- Time complexity: O(n)
- where n is the number of nodes in the linked list.
- Space complexity: O(1)
- list_for_each(pos, head)
- For iterating over a linked list, list_for_each(pos, head) is implemented in lab0-c/list.h Its usage is identical with it counterpart in Linux kernel API.
q_new()
commit: a06dd09
- Time complexity: O(1)
- Space complexity: O(1)
- Based upon the man page of malloc(), I check whether it returns NULL. If it returns NULL, no memory space can be allocated and q_new() returns NULL.
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
@jserv
Sir! I have a couple of questions for WRITE_ONCE(). I have no idea why kernel developer use this, although looking into its implementation.
Also, what is the reason that the developer just apply WRITE_ONCE() on the next pointer?
Why not apply it on the prev pointer as well?
You should read the documentation in advance.
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
jserv
@jserv
Sir! I think that's my mistake. The above implementation of INIT_LIST_HEAD() is from LXR. However, I am doing homework1 (lab0) and developing a user space application. In this homework, one of the goals is to use kenenl APIs to manipulate doubly linked list. The function signatures of those kernel APIs are already in lab0-c/list.h. Instead of LXR, I should read lab0-c/list.h. Is this the documentation in your last reply?
@jserv
Sorry! My mistake again! The documentation you mentioned in your first message should be 你所不知道的 C 語言: linked list 和非連續記憶體, Linux-核心原始程式碼
q_insert_head()
commit: be50027
q_new_element() and q_init_element()
- Because I have to implement q_insert_tail() latter, I write two helper functions to new element_t objects.
- Time and space complexity: O(n)
- where n is the length of s.
- Because a c-style string is terminated by an extra '\0' character, I add one to the return value of strlen(). Then, the variable, len, holds the valid value for malloc() and memcpy().
- If q_new_element() and q_init_element() work successfully, the newly allocated element_t object looks like this.
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- Time and space complexity: O(n)
- where n is the length of s.
- Time and space complexity: O(n)
- where n is the length of s.
- Use list_add(), which is implemneted in lab0-c/list.h, to insert the newly created element_t object at queue head.
q_insert_tail()
commit: be50027
- Time and space complexity: O(n)
- where n is the length of s.
- Reuse the APIs implemented in
commit be50027
to create a new element_t object.
- Use list_add_tail(), which is implemented in lab0-c/list.h, to insert the newly created elemnet_t object at queue tail.
q_reverse()
commit: f87171f
- Time complexity: O(n)
- where n is the number of nodes in queue.
- Space complexity: O(1)
- Reuse the API implemented in lab0-c/list.h, list_empty(), to detect whether queue is empty.
q_free()
commit: 978dec0 (original implementation)
commit: debbd35 (Fix the issue that the head node is not freed if queue is empty.)
- Time complexity: O(n)
- where n is the number of nodes in queue.
- Space complexity: O(1)
q_remove_head()
commit: debbd35
- Time and space complexity: O(bufsize)
q_remove_tail()
commit: 3ebdf77
- Time and space complexity: O(bufsize)
q_delete_mid()
Implementation by using pionter
commit: 8e72fbc
- Time complexity: O(n)
- Space complexity: O(1)
- I use two pointers,
fast
and slow
, to find the middle node in queue. Because in each iteration, slow
moves one hop and fast
move two hops, slow
references the middle node while fast
traverses the whole queue.
Implementation by using pointer of pointer
@jserv
Thanks sir! I am impressed and learned. However, I need more time and practice to accept this idea.
commit: 5c98453
q_swap()
commit: f41ead9
- In this implementation, I use two Linux kernel APIs which are not included in lab0-c/list.h. Therefore, I copy and paste them in a newly created header file, mylist.h, and fix indentations to meet our coding style.
-
Time complexity: O(n)
-
Space complexity: O(1)
-
If queue is NULL, empty or has only one node, return immediately.
-
it
is the iterator to traverse over queue. It starts from the first node, which is head->next. While both it != head
and it != head->next
are true, there are two adjacent nodes to swap.
-
Because github rejects mylist.h, I deleted it and use the other two APIs in list.h to implement q_swap().
commit: cbeabf2
q_release_element()
- q_release_element() is implemented in the original code.
q_sort()
commit: ebead52
q_delete_dup()
commit: c338565
- Time complexity: O(n)
- Space complexity: O(1)
-
Although I have implemented all the required queue APIs, my code cannot pass trace-17-complexity.cmd. I need to think about my implementation of q_insert_head(), q_insert_tail(), q_remove_head() and q_remove_tail().
-
This is what trace-17-complexity.cmd
do.
- I modify
driver.py
and it will only run trace-17-complexity.cmd
. Then, try to figure out what is going on.
- After reviewing my code and doing some experiments, I cannot draw any conclution. I have no idea for resolving this issue.
- I went to 2022q1 Homework1 (作業區) and cloned following github repositories and run 'make test' on my computer. Because they have 👍 behind their names and some of them claim they pass all the test cases, I guess they probably pass trace-17-complexity and can learn something from them. Surprisingly! None of them can pass the time complexity test!
- blueskyson
- laneser
- risheng1128
- 2020leon
- kdnvt
- mm0809
- tinyynoob
- Actually, during my debugging, sometimes my code can pass all the test cases but sometimes cannot. I guess the root cause could be string copy during node insertion and deletion. But I have no evidence.
- Move on. But I will come back.
- This could probably give me some idea.
Implement a new command, shuffle, in qtest
commit: 27ebbc4
- Add a new command in qtest.
- In console_init(), use the ADD_COMMAND() macro to add a command in console_init.
- In qtest.c, implement do_shuffle() to call q_shuffle(), which is a queue API that I will implement latter.
- In queue.c, implement q_shuffle() to do Fisher-Yates shuffle.
- Time complexity: O(n2)
- Space complexity: O(1)
- After thinking for a while, a new idea come to my mind. I think I can improve the time complexity to O(n) by using an array of pointers.
commit: 27ebbc4
- In this implementation, there are three passes.
- In pass one (line 9-14), I traverse over the doubly linkded list, delete each node and put each of them in an array of pointers (
parray
).
- In pass two (line 18-27), based on the description of wiki, I implement the Fisher-Yates shuffle algorithm to shuffle
parray
. Because all the nodes' memory addresses are kept in parray
, shuffle parray
means shuffle queue nodes.
- In pass three (line 30-34), I insert all the nodes back to queue.
- Actually, I can combine pass two and three. Because I do shuffle and node insertion in one while-loop, the code will be more concise. The time complexity is also O(n).
commit: 587aa5e