owned this note
owned this note
Published
Linked with GitHub
# 2018q3 Homework3 (review)
contributed by < [`datuiji`](https://github.com/datuiji) >
## 第二週測驗題 測驗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 找出應用案例
:::
- void (*handler)(int)
- *handler 是一個 function pointer 傳入參數型態為 int 回傳值為 void
- signal(int sig, void(*handler)(int))
- signal 有兩個參數,一個是 int sig,一個是 void(*handler)(int)
- void (*signal(int sig, void (*handler)(int))) (int)
- *signal 為一個 function pointer 傳入參數資料型態為 int,回傳 void
- [Understanding the Linux Kernel, 3rd Edition](http://johnchukwuma.com/training/UnderstandingTheLinuxKernel3rdEdition.pdf)
- 11.1. The Role of Signals 中找到 signal(2) 的說明,signal(2) 代表的是偵測到鍵盤指令的中斷。對應名稱為 SIGINT
- 1.6.6. Signals and Interprocess Communication
- Asynchronous notifications
- For instance, a user can send the interrupt signal SIGINT to a foreground process by pressing the interrupt keycode (usually Ctrl-C) at the terminal.
- 應用範例:[windows kill](https://github.com/alirdn/windows-kill/blob/2f14d5296dd3e1c73b3c2ae2dc65bc9b1d655e17/windows-kill-library/sender.cpp),在 windows 中沒有 signal 機制,所以作者想要實現 Linux kill 在 windows 上。
```clike=
#include "stdafx.h"
#include "sender.h"
#include "ctrl-routine.h"
#include "signal.h"
#include "remote-process.h"
namespace WindowsKillLibrary {
using std::string;
using std::invalid_argument;
/*
TODO: According to the test, the ctrl routine address for both of CTRL_C_EVENT and CTRL_BREAK_EVENT are same.
So maybe it's not necessary to have separated ctr routine for each of events.
*/
/*
NOTE: I've just tested the addresses on my own laptop (Windows 7 64bit). Also i can't find any document/article about
this topic. So i think there is not enough evidence to merge these ctrl routines.
*/
/// <summary>
/// Ctrl routine for CTRL_C Signal.
/// </summary>
CtrlRoutine Sender::ctrl_c_routine = CtrlRoutine(CTRL_C_EVENT);
/// <summary>
/// Ctrl routine for CTRL_BREAK Signal.
/// </summary>
CtrlRoutine Sender::ctrl_break_routine = CtrlRoutine(CTRL_BREAK_EVENT);
void Sender::send(Signal the_signal) {
RemoteProcess the_remote_process;
the_remote_process.setSignal(&the_signal);
if (the_signal.getType() == CTRL_C_EVENT) {
ctrl_c_routine.findAddress();
the_remote_process.setCtrlRoutine(&Sender::ctrl_c_routine);
}
else {
ctrl_break_routine.findAddress();
the_remote_process.setCtrlRoutine(&Sender::ctrl_break_routine);
}
the_remote_process.open();
the_remote_process.startRemoteThread();
}
void Sender::warmUp(const string& which) {
string all("ALL");
string sigInt("SIGINT");
string sigBreak("SIGBREAK");
if (which.compare(all) == 0) {
ctrl_c_routine.findAddress();
ctrl_break_routine.findAddress();
}
else if (which.compare(sigInt) == 0) {
ctrl_c_routine.findAddress();
}
else if (which.compare(sigBreak) == 0) {
ctrl_break_routine.findAddress();
}
else {
throw invalid_argument(string("Invalid which argument."));
}
}
}
```
## 第二週測驗題 測驗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)
```
- 因為 list_for_each_prev 是在前置處理階段,又因為 () 優先權大於 -> 所以如果不加括號會有非預期錯誤。
## 第二週測驗題 測驗5
以下程式是合法 C99 程式嗎?
```C
#include <stdio.h>
int main() { return (********puts)("Hello"); }
```
繼續思考以下是否合法:
```C
#include <stdio.h>
int main() { return (**&puts)("Hello"); }
```
繼續變形:
```C
#include <stdio.h>
int main() { return (&**&puts)("Hello"); }
```
也會合法嗎?為什麼?請翻閱 C 語言規格書解說。
請搭配 C 語言規格書解釋
- [ 6.3.2.1 ] A function designator is an expression that has function type
- **Except** when it is the operand of the **sizeof** operator or the unary **&** operator, a **function designator** with type ‘‘function returning type’’ **is converted to** an expression that has type ‘‘**pointer to function returning type’**’.
- `*` is Right associative operator
- [ 6.5.3.2-4 ] The unary * operator denotes indirection. **If the operand points to a function, the result is a function designator**
- 程式碼 `(********puts)` 可看作 `(*(*(*(*(*(*(*(*puts))))))))`,最裡面的括號 `(*puts)` 由 [ 6.5.3.2-4 ] 可知它是一個 function designator。再根據 [ 6.3.2.1 ],它最後會被轉為 pointer to function returning type。往外延伸一個括號 `(*(*puts))` 由於最裡面的括號是一個 function designator,再多一個 * operator 它還是一個 function designator,最後也會被轉為 pointer to function returning type,依此類推最後 `(********puts)` 仍會是一個 function designator。
- 參考以下實驗:使用 gdb 把 位置印出來
```clike=
#include<stdio.h>
void print_hello(){
printf("Hello world\n");
return;
}
int main(){
print_hello();
(&*print_hello)();
(*&*print_hello)();
(**&print_hello)();
(&**&print_hello)();
(&*&*print_hello)();
return 0;
}
```
```clike=
(gdb) ptype print_hello
type = void ()
(gdb) ptype &*print_hello
type = void (*)()
(gdb) ptype *&*print_hello
type = void ()
(gdb) ptype **&print_hello
Attempt to take contents of a non-pointer value.
(gdb) ptype &**&print_hello
type = void (*)()
(gdb) ptype &*&*print_hello
type = void (*)()
(gdb) print print_hello
$1 = {void ()} 0x400526 <print_hello>
(gdb) print &*print_hello
$2 = (void (*)()) 0x400526 <print_hello>
(gdb) print *&*print_hello
$3 = {void ()} 0x400526 <print_hello>
(gdb) print **&print_hello
$4 = {void ()} 0x400526 <print_hello>
(gdb) print &**&print_hello
$5 = (void (*)()) 0x400526 <print_hello>
(gdb) print &*&*print_hello
$6 = (void (*)()) 0x400526 <print_hello>
```
:::info
問題:為什麼 ptype **&print_hello 的結果和 print **&print_hello 的結果有點出入? ptype **&print_hello 這邊提到 Attempt to take contents of a non-pointer value.
:::
- 參考資料:[你所不知道的C語言:指標篇](/hqJBzualRcOrb2wMhheKCQ),[C pointer to function 函式指標 學習心得 ](https://jielite.blogspot.com/2014/11/c-pointer-to-function.html)
## 第三週測驗題 測驗1
考慮以下程式碼:
```C=
#include <stdio.h>
#include <stdint.h>
struct test {
unsigned int x : 5;
unsigned int y : 5;
unsigned int z;
};
int main() {
struct test t;
printf("Offset of z in struct test is %ld\n",
(uintptr_t) &t.z - (uintptr_t) &t);
return 0;
}
```
在 GNU/Linux x86_64 環境中執行,得到以下輸出:
```
Offset of z in struct test is 4
```
倘若將第 10 和第 11 換為以下:
```C=10
printf("Address of t.x is %p", &t.x);
```
會發生什麼事?
- 將第 10, 11 行換成 printf("Address of t.x is %p", &t.x); 會得到這個錯誤訊息
```clike
error: cannot take address of bit-field ‘x’
```
- C99 [3.2] alignment
- requirement that objects of a particular type be located on storage boundaries with addresses that are particular multiples of a byte address.
- C99 [6.7.2.1] The unary & (address-of) operator cannot be applied to a bit-field object; thus, there are no pointers to or arrays of bit-field objects.
- 有趣的問題:為什麼對齊?
- 因為電腦的記憶體通常會設計成以word-sized進行存取會最有效率。word是存取記憶體最自然的單位。word的大小是電腦架構所定義,一般現代的系統通常word不是4bytes(32bit)就是8bytes(64bit)。早期的電腦記憶體只能以word為單位進行存取,因此所有的記憶體存取都要落在以word為倍數的邊界上。但現代的電腦架構通常能夠存取小至單獨的byte,大至多個word。
- 參考資料:[記憶體對齊](http://opass.logdown.com/posts/743054-about-memory-alignment)
## 第三週測驗題 測驗2
考慮以下程式碼,在 little-endian 硬體上,會返回 `1`,反之,在 big-endian 硬體上會返回 `0`:
```C
int main() {
union { int a; char b;
} c = { .a = K1 };
return c.b == K2;
}
```
補完上述程式碼。
==作答區==
K1 = ?
* `(a)` 0
* `(b)` 1
* `(c)` -1
* `(d)` 254
K2 = ?
* `(a)` 0
* `(b)` 1
* `(c)` 254
:::success
延伸問題: 解釋運作原理,並找出類似的程式碼
:::
- K1 = 1, K2 = 1
- A union is a special data type available in C that allows to store different data types in the same memory location. You can define a union with many members, but only one member can contain a value at any given time. Unions provide an efficient way of using the same memory location for multiple-purpose.
- Big-endian:高位元放在低位址。
![](https://i.imgur.com/P0Tc5w7.png)
- Little-endian:低位元放在低位址。
![](https://i.imgur.com/LvjaLqZ.png)
- 解題思路:因為 union 會共用同一個記憶體位置,所以 char, int 都可以讀到 0x01 ,當機器是 little-endian 的時候,記憶體位置由低到高分別會存 0x01 0x00 0x00 0x00 ,反之,當機器是 big-endian 的時候記憶體位置由低到高分別為 0x00 0x00 0x00 0x01。所以藉由 char b 的值就能判斷機器為 big-endian 或 little-endian。
- 以上圖為例的類似程式碼:
```clike=
#include <stdio.h>
typedef union {
unsigned long l;
unsigned char c[4];
} EndianTest;
int main() {
EndianTest et;
et.l = 0x12345678;
printf("本系統位元組順序為:");
if (et.c[0] == 0x78 && et.c[1] == 0x56 && et.c[2] == 0x34 && et.c[3] == 0x12) {
printf("Little Endian\n");
} else if (et.c[0] == 0x12 && et.c[1] == 0x34 && et.c[2] == 0x56 && et.c[3] == 0x78) {
printf("Big Endian\n");
} else {
printf("Unknown Endian\n");
}
printf("0x%lX 在記憶體中的儲存順序:\n", et.l);
for (int i = 0; i < 4; i++) {
printf("%p : 0x%02X\n", &et.c[i], et.c[i]);
}
return 0;
}
```
- 參考資料:[Union](https://www.tutorialspoint.com/cprogramming/c_unions.htm), [Big-endian, little-endian](https://blog.gtwang.org/programming/difference-between-big-endian-and-little-endian-implementation-in-c/)