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 →
2021 年暑期「Linux 核心」課程大部分教材 (含解說錄影) 可公開存取 (因此就算報名失敗,也可免費學習),但之所以要求報名者先通過測驗 (依據作答表現排序) 才能完成報名,是因為授課教師會逐一批改作業和針對學員的學習狀況進行一對一討論,若不限制名額,實在難以進行,還請包涵。 – jserv
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 →
注意事項
- 測驗時間: 2021 年 7 月 6 日到 2021 年 7 月 18 日 23:00
- 可優先作答下方測驗題 到 (並及早填寫作答表單),至於測驗題 可在指定的 HackMD 建立後,再逐步改進和補充
- 作答表單
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 →
- 測驗的目的是為了控制學員的數量,並非評價學員的能力,請斟酌進行
- 可使用 Google 搜尋,但請不要借助技術論壇提問來進行這些題目,儘量「誠實面對自己」
- 建議用 25 分鐘 (或更長時間) 來進行測驗題
- 所有測驗題的程式碼都該符合 C99 或 C11 規範
- 作答儘量用簡短的程式碼,且二元運算子 (binary operator,如
+
[加法], -
[減法], *
[乘法], ^
, &
[逐位元 AND] 等等) 和運算元 (operand) 之間用一個空白字元 (不多不少!) 區隔,如 (v >> c)
、逗點 (,
) 後應該有空白或換行符號,並避免非必要的小括號 (即 (
和 )
),也該刪去 (trim) 起始/結尾的空白字元
- 顧及批改的便利,本測驗採用 Google 表單自動比對參考答案,難免會有疏漏的答案組合 (特別是程式碼風格的落差),請發訊息到授課教師的 Facebook 粉絲專頁告知和討論
- 測驗結束後,凡符合報名條件者,會由授課教師以電子郵件聯繫 (來自
<embedded.master2015@gmail.com>
),通知課程事宜和提供電子書
測驗
考慮到位元旋轉 (bitwise rotation 或 bit rotation) 的操作:
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 →
以下程式碼可建立針對不同位元的向左/向右位元旋轉的實作:
使用案例:
請填補上述 LLL
和 RRR
區域的程式碼,限制只能用 +
, -
, |
, &
, ^
, <<
, >>
等運算子,不得出現 ternary conditional operator (即 ?
搭配 :
),且不該出現 bits
。
作答區
LLL = ?
RRR = ?
延伸問題:
- 舉出 Linux 核心原始程式碼裡頭 bit rotation 的案例並說明
- x86_64 指令集具備
rotr
和 rotl
指令,上述 C 程式碼經過編譯器最佳化 (例如使用 gcc
) 後,能否運用到這二個指令呢?
測驗
以下程式碼可針對給定的 alignment 數值,輸出大於等於 alignment 的記憶體對齊地址:
已知 alignment 不為 0
,參考執行輸出:
請填補上述 MMM
標注的程式碼,使得執行結果符合預期。限制只能用 +
, -
, |
, &
, ^
, ~
, <<
, >>
等運算子,不得出現 ternary conditional operator (即 ?
搭配 :
),且不該出現 alignment
作答區
MMM = ?
延伸問題:
- 說明上述程式碼的運作原理
- 在 Linux 核心原始程式碼找出類似
align_up
的程式碼,並舉例說明其用法
測驗
在 GNU/Linux 環境,假設我們編譯的程式都會動態連結到 glibc 提供的 libc.so.6
,考慮以下程式碼: (檔名: fork.c
)
編譯程式碼:
執行並統計有多少 -
字元輸出:
若要讓 ./fork | wc -c
的輸出為 49152
,那上方程式碼的 NNN
應為何值?注意:應該考慮 buffered I/O 的影響,可參考 CS:APP 第 10 章。
作答區
NNN = ?
延伸問題:
- 解釋上述程式碼輸出
-
字元數量的原理
測驗
考慮以下 C11 程式碼,實作並行 (concurrent) 的多個生產者、多個消費者的佇列 (multi-producer, multi-consumer queue),列表如下: (檔名: queue.c
)
程式碼編譯方式:
假設 malloc
和 free
執行過程不會遇到任何錯誤,請補完程式碼,讓這樣的並行佇列得以正確運作,且不會輸出任何錯誤訊息。
作答區
AAA = ?
BBB = ?
CCC = ?
測驗
考慮以下程式碼藉由 mmap 系統呼叫來實作 memory pool:
對應的測試程式如下:
該 memory pool 初始化時,指定 到 範圍的物件預先配置,大於 的物件配置會藉由 mmap 系統呼叫達成。
假設上方程式碼的 mmap
, munmap
, malloc
, free
, realloc
均可正確執行,請補完程式碼,使得進行壓力測試的過程中,不會印出錯誤訊息和觸發 assertion。
作答區
XXX = ?
YYY = ?
延伸問題:
- 解釋上述程式碼運作原理
- 提出效能和功能的改進策略,並予以實作
測驗
以下程式碼嘗試透過「高效 Web 伺服器開發」提到的 poll 系統呼叫和「以 sendfile 和 splice 系統呼叫達到 Zero-Copy」提到 Linux 特有的 splice 系統呼叫,實作出具體而微的 Port forwarding。考慮一個情境:我們對外有一台防火牆,在 DNS 設定方面,我們設定 ftp.mydomain.com
及 www.mydomain.com
都指向這台防火牆。但我們希望所有 HTTP 連線都重新導向到內部的 192.168.0.2
這台機器上,而所有 FTP 連線都交由 192.168.0.3
來處理。這時候我們就可以使用 port forwarding 的方式來達成。對應的 NAT (Network Address Translation) 的設定如下:
第 1 行的目的就是將 port 80 的 tcp 連線重新導向到 192.168.0.2
的 port 80,而第 2 和第 3 行是將 port 20 及 port 21 的連線交由 192.168.0.3
來處理。在 192.168.0.2
及 192.168.0.3
這二台機器上,我們只要設定它們的 gateway 為防火牆的 IP,例如 192.168.0.1
即可。
使用 splice 系統呼叫,我們有機會在網路介面控制器的支援下,達到 Zero-copy 資料傳輸。
原始程式碼可見 proxy.c
。
假設本地機器系統 port 8081 已有網頁伺服器在等待連線。proxy
的測試方式為
等程式執行後,在另一個終端機畫面中輸入下列命令:
接著你就可以輸入 HTTP 請求字串,如 GET /index.html
。
此外,你還可以把 port 1922 轉向到 Google 首頁:
先找出 www.google.com
的 IP 地址:
得到以下輸出:
修改上述命令:
重複上述 telnet
命令,這時候就會看到 Google 首頁的字串。
以下是 proxy.c
程式碼列表:
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <unistd.h>
static int connect_to(char *ip, int port)
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
printf("Failed to create socket.\n");
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (inet_pton(AF_INET, ip, &addr.sin_addr) <= 0) {
perror("inet_pton");
return -1;
}
if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
printf("Failed to connect.\n");
return -1;
}
return sockfd;
}
static void move(int in_fd, int out_fd, int pip[2])
{
int ret = splice(in_fd, NULL, pip[1], NULL, 8, SPLICE_F_MOVE);
if (ret == -1) {
perror("splice(1)");
return;
}
ret = splice(pip[0], NULL, out_fd, NULL, 8, SPLICE_F_MOVE);
if (ret == -1) {
perror("splice(1)");
return;
}
}
static void proxy(int cl_fd, int target_fd)
{
if (target_fd == -1)
return;
int fds[2];
if (pipe(fds) == -1) {
perror("pipe");
return;
}
struct pollfd polls[2] = {
[1] = {III},
[0] = {JJJ},
};
int ret;
while ((ret = poll(polls, 2, 1000)) != -1) {
if (ret == 0)
continue;
int from_client = polls[0].revents & POLLIN;
if (from_client)
move(cl_fd, target_fd, fds);
else
move(target_fd, cl_fd, fds);
}
perror("poll");
}
#define PORT 1922
int main(int argc, char *argv[])
{
if (argc < 3) {
fprintf(stderr, "Usage: %s <target IP address> <target port>\n",
argv[0]);
return -1;
}
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(PORT);
int optval = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
bind(listenfd, (struct sockaddr *) &addr, sizeof(addr));
listen(listenfd, 1);
while (1) {
int connfd = accept(listenfd, (struct sockaddr *) NULL, NULL);
int target_fd = connect_to(argv[1], atoi(argv[2]));
if (target_fd >= 0)
proxy(connfd, target_fd);
close(connfd);
}
return 0;
}
請補完程式碼,使其執行符合上述,並且儘量用字元數較少的答覆 (但仍要符合 .clang-format 排版規範)。
作答區
III = ?
JJJ = ?
延伸問題:
- 解釋上述程式碼運作原理
- 以 epoll 系統呼叫改寫程式碼,並設計實驗來驗證 proxy 程式碼的效率
測驗
自上述測驗題 到 選出至少二項延伸問題,建立 HackMD 頁面、設定權限為「已登入者可編輯」(這樣授課教師才能給予眉批)、用固定網址發布筆記 (建議命名為 linux2021-quiz
),並依據下方格式來答覆延伸問題:
- 標題: linux2021: 你的 GitHub 帳號名稱
- 內文: 用
##
開頭並標注題目,如 ## 測驗
表示「測驗 」的第一個延伸問題
延伸閱讀:
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