2018q3 Homework2 (lab0)
===
contributed by < [`ChingChieh`](https://github.com/ChingChieh) >
###### tags: `sysprog2018`
## 開發紀錄
### 環境
* 系統版本:unbuntu 18.04.1
* gcc 版本:gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0
## 過程
### queue_t
```c
typedef struct {
list_ele_t *head;
list_ele_t *tail;
size_t size;
} queue_t;
```
因為 [q_insert_tail](#q_insert_tail) 和 [q_size](#q_size) 要達到 $O(1)$ 所以需要額外記住 queue size 和 tail 位子
---
### q_size
```c
int q_size(queue_t *q)
{
/* You need to write the code for this function */
/* Remember: It should operate in O(1) time */
if (q == NULL || q->head == NULL) {
return 0;
}
return q->size;
}
```
---
### q_free
```c
void q_free(queue_t *q)
{
if (q == NULL) {
return;
}
list_ele_t *tmp_next;
while (q->head != NULL) {
tmp_next = q->head->next;
free(q->head->value);
free(q->head);
q->head = tmp_next;
}
free(q);
}
```
要記得先free(q->head->value)再free(q->head)
因為我一開始就沒有用strdup的方式複製字串,所以沒遇到free(q->head->value)會錯誤的問題
---
### q_insert_head
```c=
bool q_insert_head(queue_t *q, char *s)
{
list_ele_t *newh;
/* What should you do if the q is NULL? */
if (q == NULL) {
return false;
}
newh = (list_ele_t *) malloc(sizeof(list_ele_t));
if (newh == NULL) {
return false;
}
size_t s_len = strlen(s) + 1; // Don't forget the \0!!!!!!!
/* Don't forget to allocate space for the string and copy it */
newh->value = (char *) malloc(s_len * sizeof(char));
if (newh->value == NULL) {
free(newh);
return false;
}
strcpy(newh->value, s);
/* What if either call to malloc returns NULL? */
newh->next = q->head;
if (newh->next == NULL) { // Change the tail
q->tail = newh;
}
q->head = newh;
q->size++;
return true;
}
```
要記得malloc的時候要多給他一個 char 的空間存 '\0'
---
### q_insert_tail
```c=
bool q_insert_tail(queue_t *q, char *s)
{
/* You need to write the complete code for this function */
/* Remember: It should operate in O(1) time */
if (q == NULL) {
return false;
}
list_ele_t *newt;
newt = (list_ele_t *) malloc(sizeof(list_ele_t));
if (newt == NULL) {
return false;
}
long int slen = strlen(s) + 1;
newt->value = (char *) malloc(sizeof(char) * slen);
if (newt->value == NULL) {
free(newt);
return false;
}
strcpy(newt->value, s);
if (q->tail != NULL) {
q->tail->next = newt;
} else {
q->head = newt;
}
q->tail = newt;
newt->next = NULL;
q->size++;
return true;
}
```
---
### q_remove_head
```c=
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
if (q == NULL || q->head == NULL || bufsize <= 0) {
return false;
}
if (sp != NULL) {
strncpy(sp, q->head->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
list_ele_t *headnext = q->head->next;
free(q->head->value);
free(q->head);
q->head = headnext;
q->size--;
return true;
}
```
---
### q_reverse
```c
void q_reverse(queue_t *q)
{
if (q == NULL || q->size < 2) {
return;
}
q->tail = q->head;
list_ele_t *prev = NULL;
list_ele_t *temp;
list_ele_t *curr = q->head;
while (curr != NULL) {
temp = curr->next;
curr->next = prev;
prev = curr;
curr = temp;
}
q->head = prev;
}
```
## 進度
```
$ make test
scripts/driver.py
...
...
--- trace-15-perf 7/7
--- TOTAL 100/100
```
## makefile
* rm *~ 意思
* 把檔案結尾是~的檔案刪掉,這種檔案是 editor 產生的備用檔。如果在 .vimrc 設定set backup也可以產生這種備用檔
## 遭遇問題
1. [queue.c:79]: (error) Memory leak: newh
* 原因:當要對 queue 做 insert 的時候總共要 `malloc()` 兩次,如果回傳結果是 NULL 就 return false,但在第二次檢查的時候忘記把第一次 `malloc()` 的空間 free 掉
```clike=
newh = (list_ele_t *) malloc(sizeof(list_ele_t));
if (newh == NULL) {
return false;
}
newh->value = (char *) malloc(s_len * sizeof(char));
if (newh->value == NULL) {
free(newh); //要記得 free 掉之前成功 malloc 的空間
return false;
}
```
## 自動評分系統運作原理
## qtest行為&技巧
用有沒有 define INTERNAL 來判斷是否使用自定義的malloc和free
在 harrness.h 裡面有
```c
#ifdef INTERNAL
...
#else
#define malloc test_malloc
#define free test_free
#endif
```
帶表如果有先定義INTERNAL就不會把 malloc、free 改成 test_malloc、test_free
---
### 如何偵測buffer overflow
test_malloc 程式碼:
```c=
void *test_malloc(size_t size)
{
...
block_ele_t *new_block =
malloc(size + sizeof(block_ele_t) + sizeof(size_t));
if (new_block == NULL) {
report_event(MSG_FATAL, "Couldn't allocate any more memory");
error_occurred = true;
}
new_block->magic_header = MAGICHEADER;
new_block->payload_size = size;
*find_footer(new_block) = MAGICFOOTER;
void *p = (void *) &new_block->payload;
memset(p, FILLCHAR, size);
new_block->next = allocated;
new_block->prev = NULL;
if (allocated)
allocated->prev = new_block;
allocated = new_block;
allocated_count++;
return p;
}
```
每次呼叫test_malloc(size)分配的空間:
```c
malloc(size + sizeof(block_ele_t) + sizeof(size_t));
```
test_malloc return 的是指向size起始address的pointer:
```c
void *p = (void *) &new_block->payload;
```
這裡用到的技巧是arrays of length zero,通常是用在struct的最後一個element,這樣的好處是可以把payload當成block_ele_t後面記憶體位址的開頭
![](https://i.imgur.com/JiqsWNH.png)
此外再用find_footer()定出size結尾的記憶體位址,然後把後面那塊sizeof(size_t)記憶體的值改成MAGICFOOTER
```c
static size_t *find_footer(block_ele_t *b)
{
size_t *p = (size_t *) ((size_t) b + b->payload_size + sizeof(block_ele_t));
//payload_size 是使用者可以用的空間大小
return p;
}
*find_footer(new_block) = MAGICFOOTER;
```
加上之前把magic_header的值設成MAGICHEADER,所以使用者能合法使用的空間就像被兩塊記憶體空間夾在中間,使用超出size的空間就會改到MAGICFOOTER或MAGICHEADER因此可以在test_free時被偵測出來
![](https://i.imgur.com/APwUae9.png)