# 2018q3 Homework3(review)
contributed by < `type59ty` >
###### tags: `sysprog2018`, `hw3`, `review`
---
# 1
### (2018q3 第 1 週測驗題)
### 測驗 `1`
考慮以下程式碼:
```C
int my_xor(int x, int y) { return (x | y) OP1 (OP2 x OP3 OP4 y); }
int main() {
int x = 3, y = 5;
return my_xor(x, y);
}
```
`my_xor` 嘗試不用 XOR 運算子做出等效的 XOR 運算,其中 OP1, OP2, OP3, OP4 都是 operator,請補完實作。
OP1 = ==&==
OP2 = ==~==
OP3 = ==|==
OP4 = ==~==
## 解題思路
題目要求做出 xor 等效運算,所以可以從邏輯閘的角度去想,用 and, or gate 組出 xor gate
xor 真值表如下
| A | B |$A \oplus B$|
|--|--|---|
| 0 | 0 | 0|
| 0 | 1 | 1|
| 1 | 0 | 1|
| 1 | 1 | 0|
根據真值表寫成布林代數表示式:$A \oplus B$ = $\overline{\rm A}B + A\overline{\rm B}$
![](https://i.imgur.com/9efZK1g.png)
:::info
運用 [demorgan's law](https://zh.wikipedia.org/wiki/%E5%BE%B7%E6%91%A9%E6%A0%B9%E5%AE%9A%E5%BE%8B) ,可以對原本的 xor 做等效電路
![](https://i.imgur.com/WWElycI.png)
:::
簡單來說: **先相乘再反相 = 先反相再相加,先相加再反相 = 先反相再相乘**
- 而反相再反相,不會改變原本電路
$\overline{A}B + A\overline{B}$ $=$ $\overline{{\overline{\overline{\rm A}B + A\overline{B}}}}$
$=$ $({\overline{\overline{\overline{A}B})({\overline {A\overline{B}}}}})$
- 再根據 demorgan's law ,將第一個 bar 後面的 A,B 化成
$=$ ${(\overline{A+\overline{B}) (\overline{A}+B})}$
- 將裡面的 A,B 乘開
$=$ ${(\overline{\rm \overline{A}.\overline{B}) + (AB})}$
- 最後再將第二個 bar 後面化成
$=$ $(A+B) (\overline{A}+\overline{B})$
- 所以原本的 xor: $\overline{\rm A}B + A\overline{\rm B}$ 可以等效化成 $(A+B) (\overline{\rm A}+\overline{\rm B})$
補上測試程式碼:
```C
#include <stdio.h>
int my_xor(int x, int y) { return (x | y) & (~ x | ~ y); }
int main() {
int x = 3, y = 5;
printf("xor(%d, %d) = %d\n", x, y, my_xor(x, y));
return 0;
}
```
測試結果:
```shell
$ gcc -o test_xor test_xor.c
$ ./test_xor
xor(3, 5) = 6
```
---
# 2
### (2018q3 第 2 週測驗題)
### 測驗 `2`
[指標篇](https://hackmd.io/s/HyBPr9WGl) 提到 signal 系統呼叫的原型宣告:
```C
void (*signal(int sig, void (*handler)(int))) (int);
```
該如何解析呢?
提示: 參閱 manpage: [signal(2)](http://man7.org/linux/man-pages/man2/signal.2.html)
:::success
延伸問題: 解釋 signal(2) 的作用,並在 GitHub 找出應用案例
:::
**解釋:**
- signal 函式接受兩個參數: int 和 function pointer ( sig and handler )
- handler 接受 int 並回傳 void (沒有回傳值)
- 而 signal 本身也是一個 function pointer 回傳給一個接受 int 的函式,這個函式沒有回傳值
在 [stack overflow](https://stackoverflow.com/questions/15739500/how-to-read-this-prototype) 上有個更清楚的解釋方式:
```c
signal -- signal
signal( ) -- is a function
signal( sig ) -- with a parameter named sig
signal(int sig, ) -- of type int
signal(int sig, handler ) -- and a parameter named handler
signal(int sig, *handler ) -- which is a pointer
signal(int sig, (*handler)( )) ) -- to a function
signal(int sig, (*handler)(int)) ) -- taking an int parameter
signal(int sig, void (*handler)(int)) ) -- and returning void
*signal(int sig, void (*handler)(int)) ) -- returning a pointer
( *signal(int sig, void (*handler)(int)) )( ) -- to a function
( *signal(int sig, void (*handler)(int)) )(int) -- taking an int parameter
void ( *signal(int sig, void (*handler)(int)) )(int); -- and returning void
```
作用:
在 linux 系統中, signal 用於 process 之間的訊號傳遞,概念像是作業系統中的 interrupt ,當使用者使用 kill 指令, kernel 就會調用此函式發送訊號給 process ,通知 process 目前發生某個事件。
案例:[signal.c](https://github.com/torvalds/linux/blob/master/kernel/signal.c)
---
# 3
### 測驗 `3`
Linux 核心程式碼 [include/linux/list.h](https://github.com/torvalds/linux/blob/master/include/linux/list.h) 提到以下程式碼,為何每個 `head` 使用時都要先加上 `()` 呢?
```C
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
```
:::success
延伸問題: 在 Linux 核心原始程式碼找出類似上述「走訪節點」的片段,討論其實作技巧和考量點
:::
**解釋:**
這邊定義了一個 macro ,在 preprocess 階段會先被執行,然後在 compile 階段裡面的變數才會代換成真正要執行的值,其中 head 是一個 pointer ,假設它為 *q ,如果沒加`()`就會變成:
```c
pos = *q->prev;
```
其中`->`的[優先權](https://zh.wikipedia.org/zh-tw/C%E5%92%8CC%2B%2B%E9%81%8B%E7%AE%97%E5%AD%90)大於`*` ,所以執行順序會變成
```c
pos = *(q->prev);
```
這跟原本預期的結果不同,所以加`()`是為了防止執行順序出錯
---
# 4
### 測驗 `7`
推敲以下程式碼的作用:
```C=
void hex2(unsigned int x) {
do {
char c = "0123456789abcdef" [x & 0xf];
printf("char %c for %d\n", c, x);
x >>= 4;
printf("char %c for %d\n", c, x);
} while (x);
}
```
:::success
延伸問題: 在 [glibc](https://www.gnu.org/software/libc/) 原始程式碼找出類似作用和寫法的程式碼,並探討其實作技巧
:::
**解釋:**
x 為一個 32 bit 整數,每次取最後4個 bits 的值當作 index ,取出字串 "0123456789abcdef" 中對應的值,取完後右移 4 個 bits,取下次的最後 4 個 bits 為 index,直到 x 為0才停止
e.g
```c
hex2(127)
則 x = 0x0111 1111
執行第3行後
c = "0123456789abcdef"[0x1111] = 'f'
再來執行 x>>=4 後
x = 0x0111
c = "0123456789abcdef"[0x1111] = '7'
再執行 x>>=4 後
得到 x = 0
下一次判斷就直接跳出 while 迴圈
```
---
# 5
### (2018q3 第 3 週測驗題)
### 測驗 `2`
考慮以下程式碼,在 little-endian 硬體上,會返回 `1`,反之,在 big-endian 硬體上會返回 `0`:
```C
int main() {
union { int a; char b;
} c = { .a = K1 };
return c.b == K2;
}
```
補完上述程式碼。
==作答區==
K1 = 254
K2 = 254
:::success
延伸問題: 解釋運作原理,並找出類似的程式碼
:::
---