owned this note
owned this note
Published
Linked with GitHub
# 2025-05-27 問答簡記
## 生成式 AI 年會

> [出處](https://x.com/lovecankill/status/1926180549393060127)
生成式 AI 為企業帶來的最大價值,在於把過去因人力、資源受限而被迫妥協的服務品質重新「撐起來」── 組織不必再對使用者冷漠,也不用在妥協中失去初心。從「能否立即動手用 AI 嘗試解決痛點」的行動力角度來看,未來的人才分群將超越「會不會搬重物」「文組理組」「會不會 coding」等舊框架;當困難出現時,立刻調用 AI 並快速迭代的人將自然跑在前面。近年 vibe coding 結合 coding agents 的工作模式正逐步取代傳統手寫程式碼:從早期的自動完成功能,到現在直接要求大語言模型產生整段邏輯、甚至整合整個專案,少打一個字就是一小步,少寫一段程式就是一大步。雖然大語言模型輸出的程式碼仍可能含有 bug,,但這種落差其實只是對轉變的抗拒;隨著描述需求、調整提示與設計工作流的學習曲線被克服,大規模智慧化遲早成形。
> 報導: [Peggy Lo](https://www.inside.com.tw/feature/2025-generative-ai/38545-ai-coding-ngoer)
九歲開發者「海馬」分享自己如何透過 Claude、Gemini、Grok、ChatGPT 進行 vibe coding。過去他開發 Apple Watch App《9 歲記帳》從學 Python、寫介面到完成功能花了半年;今年他利用 AI,一個月內就打造出「海家帶你游程式」品牌網站。沒有 AI 時,他在螢幕上同時開啟程式碼、教材與翻譯,一步步排錯;如今 ChatGPT 成了即時家教,教材若有筆誤可立即指出。
> 報導: [小學生海馬自學App與網站開發之路](https://www.businessyee.com/article/6216-leethi)
摘錄[林彥廷博士演講](https://www.inside.com.tw/feature/2025-generative-ai/38553-how-are-ai-benchmarks-real-and-useful):
* 「不只是評估 AI,更是評估人、組織與自己。」
* 「你插了一根旗之後,就有一座山可以去攻頂。真正重要的是你插旗的位置,而不是你有沒有攻頂。」
* 「你認真看現在大家拿來 當作基準的題目,很多其實根本沒人在乎。那些升學考、會考、高普考的資料集,早就被 Google 和 OpenAI 解得一乾二淨。」
* 「你看那些高分評測指標,問問自己:這些東西,跟你的應用有關嗎?如果沒有關,就看看就好。」
---
## 轉向自行設計移動裝置專用晶片的策略意義
Google 大手筆投資自家 Tensor G 系列系統整合晶片 (SoC),而非直接採用 Qualcomm 或 MediaTek 的旗艦方案,長久以來是業界爭論焦點。Google 的戰略重心並非單純追求 CPU/GPU 的極限跑分,而是聚焦於「縱向整合」──把軟體服務、端側人工智慧與延長產品維護週期整合成協同優勢。
Tensor SoC 內建的專屬元件,例如 Tensor Processing Unit (TPU) 與 Image Signal Processor (ISP),使 Pixel 8 系列得以提供媲美雲端的 Gemini Nano 生成式 AI、即時語音轉文字與更精準的 HDR 影像處理。若僅使用市售通用 SoC,要客製化這些功能十分困難。
> 參見 [Google Tensor G3: The new chip that gives your Pixel an AI upgrade](https://blog.google/products/pixel/google-tensor-g3-pixel-8/)
早期 Tensor(特別是以 Samsung Exynos 為基礎的 G1 與 G2)在遊戲場景暴露散熱與效能瓶頸,[引發「換回 Snapdragon 即可解決」的批評](https://www.digitaltrends.com/mobile/google-pixel-8-tensor-g3-chip-good-or-bad-explained/)。Google 隨後在 Tensor G3 採用 9 核異質配置並升級製程,以壓低峰值功耗並改善散熱。
業界普遍預期 2025 年問世的 Pixel 10 會搭載 Tensor G5 ── Google 首款完全內部設計、並以台積電 3 nm 製程量產的 SoC。新晶片將整合全新自研 ISP 與第二代 TPU,並承諾七年作業系統與韌體更新,進一步鞏固 Google 在 Android 生態中的 AI 佈局。
> 參見 [Android Authority](https://www.androidauthority.com/google-tensor-g5-rumors-3401883/)
與此同時,小米亦走上相似道路,宣布 10 年內投入 500 億元人民幣,將晶片設計團隊擴編至 2,500 名工程師(來源:[Business Insider](https://www.businessinsider.com/xiaomi-lei-jun-mobile-chips-apple-multibillion-dollar-plan-2025-5 "小米效法 Apple,斥資 70 億美元研發行動晶片")、[Reuters](https://www.reuters.com/business/media-telecom/xiaomi-invest-least-69-billion-chip-design-founder-says-2025-05-19/ "小米創辦人:至少投資 69 億美元開發晶片"))。公司預定 2025 年 5 月量產自研 XRING O1 SoC(玄戒 O1,3 nm 製程,採 Arm Cortex-X925 CPU 與 Immortalis-G925 GPU),首發機型包含 Xiaomi 15S Pro 與 Pad 7 Ultra。透過自家 ISP、電源管理 IC 與 AI 協同加速器,小米期望降低對 Qualcomm 高階晶片的依賴,並以影像與端側 AI 效能差異化(參見 [TechNode](https://technode.com/2025/05/16/xiaomi-unveils-xuanjie-o1-a-self-developed-phone-chip-to-launch-in-late-may/ "小米發表自研玄界 O1 手機晶片,五月底啟動量產"))。
以上二間 Android 陣營要角的自研策略,顯示高階行動裝置競爭已從單純比拚原始運算能力,轉向「軟硬共同設計+專用運算單元」的差異化路線。唯有同時掌控 SoC 內部邏輯模組與對應的作業系統、應用服務,才能在 AI 時代爭奪運算話語權,並為未來的延展實境 (XR) 與車載平台奠定基礎。
---
## 以開放原始碼為槓桿的商業策略:以 Docker 為例
利用公開程式碼吸引使用者、匯聚社群能量、快速獲得回饋,進而在利基市場中建立護城河,是許多開放原始碼專案常見的獲利模式。軟體的價值與其採用度高度正相關;唯有使用者數足夠龐大,才可能帶來更多貢獻者與商業機會。然而,開放原始碼並不等同免費慈善,營運團隊仍需發展穩健的收費機制,否則再響亮的品牌也難敵現實壓力。
### 從 DotCloud 到 Docker:「公開」帶來的意外推力
DotCloud 公司由 Solomon Hykes 等人於 2010 年創立,最初是一家專注於雲端平台即服務 (Platform as a Service, PaaS) 的新創公司,致力於簡化應用程式的部署和管理。在激烈的市場競爭中,DotCloud 決定於 2013 年將其內部使用的容器化技術 Docker 釋出為開放原始碼專案,初期希望藉此提升品牌曝光度,並為自家的 PaaS 平台帶來更多流量。出乎意料的是,Docker 大幅簡化 Linux 核心中如 `cgroup` 和 `namespace` 等底層技術的繁瑣設定,並透過映像檔 (image) 的封裝方式,讓「容器」這項技術幾乎成為人人皆可透過單一指令操作的便利工具。社群對 Docker 的熱情瞬間引爆,其受歡迎程度遠超 DotCloud 原本的 PaaS 服務,促使 DotCloud 公司在 2014 年更名為 Docker Inc.,全力發展容器相關業務。為掌握容器技術的標準話語權,Docker Inc. 更在 2015 年聯合其他業界夥伴發起開放容器倡議 (Open Container Initiative, OCI),旨在制定開放的容器格式與執行時期標準。
### 市場洗牌:Kubernetes 與容器管理生態的再平衡
Docker 的巨大成功也為其帶來相應的壓力。Google 在 2014 年開放原始碼其內部容器編排系統 Kubernetes,為企業提供大規模容器部署、管理與自動化擴展的強大功能。Kubernetes 憑藉其優越的架構設計、可擴充性以及活躍的社群動能,迅速獲得市場認可。緊隨其後,Microsoft Azure、Amazon AWS、IBM 等主要雲端服務供應商也紛紛推出各自的託管 Kubernetes 服務 (如 Azure Kubernetes Service - AKS, Amazon Elastic Kubernetes Service - EKS),極大降低企業採用 Kubernetes 的門檻,進一步鞏固 Kubernetes 的市場主導地位並加速其生態系統的繁榮。相較之下,Docker 自家的編排工具 Docker Swarm 雖然力圖簡潔易用,但在功能完整性和社群支持上難以匹敵。Docker 公司當時的商業版 Docker Enterprise (Docker EE) 持續與自家 Swarm 深度綁定,未能及時擁抱並與 Kubernetes 生態進行更緊密的整合,這使其錯失重要的市場發展窗口,競爭力也因此迅速下滑。在開放原始碼的世界裡,即使是領先者,若停止快速迭代與適應市場變化,很快就會被更具活力或擁有更多資源的後起專案超越。
### 捐贈 containerd:拋開技術本位,守住影響力
面對 Kubernetes 陣營及眾多科技巨頭的環伺,Docker 公司認知到,若持續試圖獨佔容器底層技術的控制權,市場參與者最終可能會尋找或開發替代方案,進而邊緣化 Docker。於是,Docker 公司在 2017 年做出一項重要策略決定:將其容器執行時期的關鍵元件 containerd 捐贈給雲原生運算基金會 (Cloud Native Computing Foundation, CNCF)。此舉使 Kubernetes 及其他容器相關專案能夠更放心、更中立地採用 containerd 作為其底層執行時期,同時也讓 Docker 公司能透過基金會的治理模式,繼續在容器生態中保持其技術影響力。這種「鬆手關鍵技術、持續經營周邊生態」的策略,讓 Docker 得以將有限的資源更集中於開發者工具鏈以及雲端相關服務的開發,避免在容器編排戰場上與 Kubernetes 陣營進行資源消耗巨大的正面對決。
### 重新聚焦:工具與平台服務成為營收骨架
在調整策略後,Docker Inc. 將其商業重心轉向以下幾個方面:
* Docker Desktop:一款為 Windows 與 macOS 使用者設計的圖形化容器開發與管理工具。它極大簡化在本機環境中設定和使用容器的複雜度,深受開發者喜愛。對於大型企業用戶,Docker Desktop 採用訂閱收費模式,這已成為 Docker 公司現階段主要的營收來源。
* Docker Hub:一個雲端映像檔託管與分享的平台服務。它採用 Freemium 模式,對個人開發者和小型團隊提供免費的公開映像檔儲存庫,而對企業用戶則提供私有儲存庫、自動化建置、安全掃描等加值功能並收取費用。Docker Hub 的策略與 GitHub 相似,旨在透過建立強大的平台網路效應,吸引並鎖定大量使用者,從而換取長期的商業動能與價值。
### 觀察與啟示
1. 開放是手段,生存與發展才是目的:Docker 的每一次策略轉變,無論是公開關鍵技術、參與標準制定,還是捐贈重要專案,其背後都是基於商業利益的深思熟慮與權衡,而非單純的理想主義驅動。
2. 護城河取決於生態系統的整體優勢,而非單一技術或專案:開放原始碼雖然降低技術的入門門檻,但也同時降低技術被複製或替代的成本。唯有透過建立技術標準、培養活躍社群、提供有價值的商業服務,並將這些元素組合成一個難以替代的生態系統,才能在競爭中維持領先地位。
3. 大型企業的資源與背書,往往是開放原始碼專案競爭中的決勝關鍵:Kubernetes 之所以能迅速崛起並成為市場主導者,Google、Red Hat 等大型科技公司的早期投入與持續支持功不可沒,無論在資金、工程師資源還是市場推廣規模上,都對初創階段的 Docker Inc. 形成巨大壓力。
Docker 的發展歷程清晰說明:在快速變動的開放原始碼領域,「先行者優勢」並非牢不可破的保證。唯有持續創新、不斷迭代產品,並且靈活調整商業模式以適應生態系統的演變,企業才能在激烈的競爭中保持長期的生命力與競爭力。
### 容器引擎與開發商
| 公司 | 代表專案/產品及其競爭焦點 |
| ---- | ---------------------- |
| Red Hat (IBM) | Podman、Buildah、Skopeo;並整合到 OpenShift (企業版 Kubernetes 發行版):這些工具提供無守護行程 (Daemon-less) 的命令列介面 (CLI),專注於映像檔建置、管理與分享,特別強調符合 OCI 標準及「原生無根 (rootless)」模式的安全性,旨在作為 Docker Engine 的直接替代方案。Red Hat 將它們作為其 OpenShift 平台和整體容器策略的基礎。 (資料來源: Red Hat) |
| SUSE | Rancher Desktop(桌面版)與 Rancher(多叢集管理平台):Rancher Desktop 提供一個整合 Kubernetes (可選 k3s, kine 等) 和容器管理的桌面應用,旨在取代 Docker Desktop,提供更貼近 Kubernetes 原生的開發體驗。Rancher 平台則專注於企業級多 Kubernetes 叢集的部署、管理與維運。 (資料來源: Rancher Labs/SUSE) |
| Canonical | MicroK8s(輕量 Kubernetes)與 LXD(系統容器):MicroK8s 是一個輕量、符合 CNCF 標準的 Kubernetes 發行版,易於在開發者桌面或邊緣設備上部署。LXD 提供完整的系統容器體驗,更像輕量級虛擬機。Canonical 以此打通從桌面開發到雲端部署的鏈條,並提供長期支援 (LTS)。 (資料來源: microk8s.io, Canonical) |
| Mirantis | Lens IDE、k0s Kubernetes,並承接前 Docker Enterprise 業務:Mirantis 在收購 Docker Enterprise 後,繼續為其客戶提供支持。同時,透過 Lens (一個流行的 Kubernetes 圖形化管理介面) 和 k0s (一個精簡、易部署的 Kubernetes 發行版),Mirantis 聚焦於簡化從單機到多雲環境的 Kubernetes 管理體驗。 (資料來源: Mirantis) |
| Portainer Inc. | Portainer Community Edition (CE) / Business Edition (BE):Portainer 提供一個直觀的 Web 圖形化介面,用於管理 Docker、Docker Swarm、Kubernetes (包括邊緣裝置上的 K3s/MicroK8s) 和 Nomad 環境,旨在簡化容器化應用的部署、監控和管理,降低操作複雜性。 (資料來源: Help Net Security, Portainer.io) |
### 平台供應商
| 公司 | 代表專案/產品及其競爭焦點 |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Google | Kubernetes (K8s),並透過 CNCF 治理:作為容器編排領域的事實標準,Kubernetes 提供強大的自動化部署、擴展和管理容器化應用的能力。Google 將其捐贈給雲原生運算基金會 (CNCF) 進行中立治理,確保其開放性與廣泛的社群參與,已在市場上取代 Docker Swarm 成為主流。 (資料來源: Kubernetes.io) |
| HashiCorp | Nomad(自 2024 年起主要版本轉為 Business Source License, BSL,但其主要程式碼仍有部分公開):Nomad 是一個靈活的工作負載排程器,不僅支援容器,還能調度虛擬機、Java 應用、批次處理等。它以其簡潔性、可擴展性和對多種工作負載的統一管理為主要賣點,常被視為比 Kubernetes 更輕量且易於維運的選擇。 (資料來源: Develeap, HashiCorp) |
| SUSE | Rancher(Kubernetes 管理平台)與 RKE2 (Rancher Kubernetes Engine 2):SUSE Rancher 是一個廣受歡迎的企業級 Kubernetes 管理平台,提供多叢集部署、集中化管理、安全強化及簡化維運等功能。RKE2 是一個高度安全且符合標準的 Kubernetes 發行版,專為生產環境設計。 (資料來源: SUSE) |
| Red Hat (IBM) | OpenShift Container Platform:OpenShift 是 Red Hat 的旗艦級企業 Kubernetes 平台,它在 Kubernetes 主體之上整合了開發者工具、CI/CD 流程、自動化維運以及企業級安全特性,提供一個從開發到生產的完整應用程式平台即服務 (PaaS) 體驗。 (資料來源: Red Hat) |
| VMware (Broadcom) | Tanzu(企業級 Kubernetes 產品組合)與 Harbor Registry:VMware Tanzu 是一套全面的產品和服務,用於在企業內部和多雲環境中建構、執行和管理基於 Kubernetes 的現代應用。Harbor (CNCF 畢業專案) 是一個企業級開源映像檔登錄中心,常與 Tanzu 平台集成,提供安全、合規的映像檔管理。 (資料來源: VMware Blogs) |
### 映像檔註冊中心與供應鏈
| 公司 | 代表專案/產品及其競爭焦點 |
| ----- | ----------------------- |
| GitLab Inc. | GitLab Container Registry(基於 CNCF Distribution 專案建置):GitLab 將其容器映像檔註冊中心深度整合到其 DevOps 平台中,允許用戶在同一個環境中管理原始碼、執行 CI/CD 管線並儲存和部署容器映像檔,對 Docker Hub 的商業方案構成競爭。 (資料來源: about.gitlab.com) |
| Microsoft (GitHub) | GitHub Container Registry:作為 GitHub Packages 的一部分,GitHub Container Registry 允許開發者將容器映像檔與其原始碼儲存庫一起託管在 GitHub,實現無縫的工作流程整合,特別吸引已使用 GitHub 進行專案管理的開發者和團隊。 (資料來源: The GitHub Blog) |
| Red Hat (IBM) | Quay.io 與 Red Hat Quay:Quay 提供企業級的私有和公開映像檔倉庫服務,具備進階的存取控制、團隊協作、安全漏洞掃描、映像檔建置自動化以及地理複製等功能,適用於對安全性和合規性有較高要求的組織。 (資料來源: Red Hat, Quay.io) |
| Harbor (CNCF 專案) | Harbor(CNCF 已畢業專案):Harbor 是一個開源的企業級雲原生映像檔登錄中心,提供多租戶管理、基於角色的存取控制 (RBAC)、映像檔複製、漏洞掃描 (可整合 Trivy, Clair 等)、內容信任 (透過 Notary 或 Sigstore 簽章) 等功能,常被企業用於自建私有映像檔倉庫。 (資料來源: VMware Blogs, goharbor.io) |
---
## 任務:模擬科技公司面試

## kurtislin
作業系統中 Stack 跟 Heap 的差別?
==TODO==: 以 glibc 提供的函式來舉例 (搭配 glibc 原始程式碼,至少 2 個案例),說明哪些會用到 heap (不是資料結構,而是實際記憶體佈局)
### __getdelim()
```c
ssize_t
__getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp)
{
ssize_t result;
ssize_t cur_len = 0;
ssize_t len;
if (lineptr == NULL || n == NULL || fp == NULL)
{
__set_errno (EINVAL);
return -1;
}
// 檢查是否需要分配初始緩衝區
if (*lineptr == NULL || *n == 0)
{
*n = 120; // 預設初始大小為 120 bytes
*lineptr = (char *) malloc (*n); // 使用 malloc 在 heap 上分配記憶體
if (*lineptr == NULL)
{
result = -1;
goto unlock_return;
}
}
// ... 讀取資料的邏輯 ...
for (;;)
{
// 讀取字元
i = _IO_getc_unlocked (fp);
if (i == EOF)
{
result = -1;
break;
}
// 檢查緩衝區是否需要擴充
if (cur_len + 1 >= *n)
{
size_t needed = 2 * *n + 1; // 將緩衝區大小擴充為兩倍
char *new_lineptr;
if (needed < cur_len)
{
result = -1;
goto unlock_return;
}
new_lineptr = (char *) realloc (*lineptr, needed); // 使用 realloc 在 heap 上重新分配
if (new_lineptr == NULL)
{
result = -1;
goto unlock_return;
}
*lineptr = new_lineptr;
*n = needed;
}
(*lineptr)[cur_len] = i;
cur_len++;
if (i == delimiter)
break;
}
(*lineptr)[cur_len] = '\0';
result = cur_len ? cur_len : result;
unlock_return:
return result;
}
```
### strdup()
strdup() 函式用於複製字串,它會在 heap 上配置記憶體。
```c
char *
__strdup (const char *s)
{
size_t len = strlen (s) + 1; // 計算字串長度(包含 '\0')
void *new = malloc (len); // 在 heap 上配置記憶體
if (new == NULL)
return NULL;
return (char *) memcpy (new, s, len); // 複製字串內容並返回
}
```
Function call 跟 stack 有沒有關係?
:有,每次
Stack frame 在指什麼?
## thwuedwin
為什麼我們拿 Stack 描述 Function call?
Stack 跟 Heap 的作用分別是甚麼?
:Stack 用來存像是靜態配置記憶體的
:Heap 用來存動態配置的記憶體 e.g. malloc
==TODO==: malloc 是否有 mutex lock?若有,跟 heap 的關聯在哪?列出 glibc/newlib 程式碼來說明
根據 Linux 核心手冊,malloc 有 mutex lock
> To avoid corruption in multithreaded applications, mutexes are used internally to protect the memory-management data structures employed by these functions.
若在多執行緒時,malloc 會建立 arena 管理 lock
> glibc creates additional memory allocation arenas if mutex contention is detected.
`malloc` 會用到兩個不一樣的 mutex lock
1. arena 內的 mutex lock,每個 arena 會有自己的鎖
2. 管理可用 arena 的 free list 的 mutex lock
以下為 [glibc](https://elixir.bootlin.com/glibc/glibc-2.41.9000/source/malloc/malloc.c#L2533) `malloc/malloc.c` 中 `__libc_malloc` 在多執行緒情況下的實作
```c
arena_get (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
/* Retry with another arena only if we were able to find a usable arena
before. */
if (!victim && ar_ptr != NULL)
{
LIBC_PROBE (memory_malloc_retry, 1, bytes);
ar_ptr = arena_get_retry (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
}
if (ar_ptr != NULL)
__libc_lock_unlock (ar_ptr->mutex);
```
在 `malloc/arena.c` 中可以找到 `arena_get` 的定義。`arena_get` 會嘗試取得 arena,若成功取得 arena 則會將該 arena 的 mutex 上鎖。反之會由 `arena_get2` 尋找可用的 arena,或是建立新的 arena,此時就會使用到管理 arena free list 的 mutex lock。
```c
#define arena_get(ptr, size) do { \
ptr = thread_arena; \
arena_lock (ptr, size); \
} while (0)
#define arena_lock(ptr, size) do { \
if (ptr) \
__libc_lock_lock (ptr->mutex); \
else \
ptr = arena_get2 ((size), NULL); \
} while (0)
```
最後看一下 heap 的定義,我們可以知道 heap 會從屬於某個 arena,且一個 arena 可以包含多個 heap。heap 本身不包含 mutex lock 而是由 arena 管理。
```c
typedef struct _heap_info
{
mstate ar_ptr; /* Arena for this heap. */
struct _heap_info *prev; /* Previous heap. */
size_t size; /* Current size in bytes. */
size_t mprotect_size; /* Size in bytes that has been mprotected
PROT_READ|PROT_WRITE. */
size_t pagesize; /* Page size used when allocating the arena. */
/* Make sure the following data is properly aligned, particularly
that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of
MALLOC_ALIGNMENT. */
char pad[-3 * SIZE_SZ & MALLOC_ALIGN_MASK];
} heap_info;
```
## devarajabc
Stack 跟 Heap 哪個比較大?
:好像看起來是 Heap。
==TODO==: 解釋為何系統設計者偏好將系統資源留給 heap 更大的範圍
> 工程人員說話要精準,不要說「好像是」
`$ cat /proc/1/map | less`
Stack 的範圍在哪裡?
:7ffe51982000 - 7ffe519a3000
Heap 的範圍?
:582f9bae4000 - 582f9bfce000
為什麼 Linux 開發者要這樣設計
什麼時候會需要頻繁的配置記憶體?
:動態二進制轉換
為什麼動態二進制轉換需要頻繁配置?
:因為比幾重新轉換,從記憶體中找已經轉換過得會比較快。
為什麼需要 Code Cache?
Binary Translation 和直接轉譯 (interpretation) 的差異在於前者會以 "Block" 為單位進行轉譯,且轉譯的 Block 會被儲存起來以利後續的使用。
>mapping each individual source binary instruction to its own customized target code.
當一個 Block 的內容被執行完 (即遇到 jump 或 branch)時,要
此處的 Block 的大小與傳統的 [Basic block](https://en.wikipedia.org/wiki/Basic_block?utm_source=chatgpt.com) 不同
>A basic block is a straight-line code sequence with no branches in except to the entry and no branches out except at the exit
要怎麼查詢對應的 block?
> 參考 Virtual Machines: Versatile Platforms for Systems and Processes
轉譯後的程式碼會對應到 Target Program Counter, TPC),
接著 Emulation Manager 會利用 Map Table 來追蹤它與原始二進位檔案中(Source Program Counter, SPC) 之間的關係。
>The SPC keeps the correct architected source state,
and the TPC is used for actually fetching the predecoded instructions.

因此,可以利用
要用什麼系統呼叫才能建立執行緒?
:可以用 clone,CLONE_VM 可以共享虛擬記憶體,CLONE_FS 可以共享檔案系統資訊。
## Lien-Hsiang-En
Stack overflow 發生在哪裡?
:7ffe519a3000
為什麼 Linux 開發者要這樣設計?
:stack 設計給使用者有足夠的空間作 Function call 但不鼓勵無止盡的遞迴
==TODO==: Linux 核心內部的 stack 範圍多大?核心模式的 heap 又是有多大呢?以 x86-64 和 Arm64 架構來說明,搭配 Linux 原始程式碼。注意:可能會有多種答覆
## Max042004
Linux 中 Thread 跟 Process 的差別?
:Thread 之間有共享記憶體空間,Process 之間沒有
:Thread 可以存取親代 Process 的 Address space
:process 和 thread 的用處不同。process 視為是執行中的程式,概念上提供每一個運行中的程式擁有假想的獨立運行環境,每一個 process 都認為自己是電腦上唯一執行的程式,獨享 CPU、記憶體和硬體資源。thread 則是隸屬於 process 的執行單元。
在記憶體層面上,process 之間彼此隔離互不影響,而屬於相同親代的 thread 則可以共享記憶體和其他資源
以排程的角度,排程的單位都是 task。
在 Linux 中,process 和 thread 皆以 task 表示,每一個 task 都用一個 `task_struct` 儲存其資訊,在 `task_struct` 中,其中一個 field 為 `mm_struct`:
```c
struct task_struct {
...
struct mm_struct *mm;
struct mm_struct *active_mm;
...
}
```
`mm_struct` 儲存 process 虛擬記憶體的資訊:
```c
struct mm_struct {
struct {
struct vm_area_struct *mmap;
...
} __randomize_layout;
...
/**
* @mm_users: The number of users including userspace.
*
* Use mmget()/mmget_not_zero()/mmput() to modify. When this
* drops to 0 (i.e. when the task exits and there are no other
* temporary reference holders), we also release a reference on
* @mm_count (which may then free the &struct mm_struct if
* @mm_count also drops to 0).
*/
atomic_t mm_users;
...
};
```
`mm_struct` 的一個指標 `mmap` 指向儲存所有 virtual memory area 的鏈結串列(在 v6.1 起改為 maple tree,因此最新版中 mm_struct 已不見 mmap,改名為 mm_mt),virtual memory area 以 `vm_area_struct` 儲存,`mmap` 便是指向其中一個 virtual memory area,以下是 v6.0.19 版本的 `vm_area_struct`,可見 vm_area_struct 儲存其隸屬的 process 的每一塊 virtual memory:
```c
/*
* This struct describes a virtual memory area. There is one of these
* per VM-area/task. A VM area is any part of the process virtual memory
* space that has a special rule for the page-fault handlers (ie a shared
* library, the executable area etc).
*/
struct vm_area_struct {
/* The first cache line has the info for VMA tree walking. */
unsigned long vm_start; /* Our start address within vm_mm. */
unsigned long vm_end; /* The first byte after our end address
within vm_mm. */
/* linked list of VM areas per task, sorted by address */
struct vm_area_struct *vm_next, *vm_prev;
struct rb_node vm_rb;
/*
* Largest free memory gap in bytes to the left of this VMA.
* Either between this VMA and vma->vm_prev, or between one of the
* VMAs below us in the VMA rbtree and its ->vm_prev. This helps
* get_unmapped_area find a free area of the right size.
*/
unsigned long rb_subtree_gap;
/* Second cache line starts here. */
struct mm_struct *vm_mm; /* The address space we belong to. */
/*
* Access permissions of this VMA.
* See vmf_insert_mixed_prot() for discussion.
*/
pgprot_t vm_page_prot;
unsigned long vm_flags; /* Flags, see mm.h. */
...
/* Function pointers to deal with this struct. */
const struct vm_operations_struct *vm_ops;
...
} __randomize_layout;
```

若使用系統呼叫 `clone()` 搭配 `CLONE_VM` 做為參數,則新建立的 task 的 `mm` 會指向與其親代相同的 `mm_struct` 實體,使其 address space 與親代共享,並且在該共用的 `mm_struct` 實體中的 `mm_users` 紀錄共用者數量。
`clone()` 系統呼叫由 `kernel_clone()` 啟動,定義在 `kernel/fork.c`
```c
pid_t kernel_clone(struct kernel_clone_args *args)
{
...
p = copy_process(NULL, trace, NUMA_NO_NODE, args);
...
}
```
`copy_process()` 內部呼叫 `copy_mm()`,如果有 `CLONE_VM` 參數的話,就將新的 task 的 `mm` 沿用親代的 `mm`:
```c
static int copy_mm(unsigned long clone_flags, struct task_struct *tsk)
{
struct mm_struct *mm, *oldmm;
...
tsk->mm = NULL;
tsk->active_mm = NULL;
/*
* Are we cloning a kernel thread?
*
* We need to steal a active VM for that..
*/
oldmm = current->mm;
if (!oldmm)
return 0;
if (clone_flags & CLONE_VM) {
mmget(oldmm);
mm = oldmm;
} else {
mm = dup_mm(tsk, current->mm);
if (!mm)
return -ENOMEM;
}
tsk->mm = mm;
tsk->active_mm = mm;
sched_mm_cid_fork(tsk);
return 0;
}
```
[參考資料](https://www.kernel.org/doc/gorman/html/understand/understand007.html)
為什麼 Linux 中不特別區分 Thread 跟 Process
:Thread 跟 Process 都可以用 clone 建立,因此可以共用程式碼。從前面的程式碼,可以看到不管是 process 還是 thread,在 linux 皆以 `task_struct` 儲存資訊,建立新的 process 或 thread 同樣也使用 `clone()`,只需要選擇合適的 flag 規定其共享的資源就能滿足 process 和 thread 各自的需求。因此便沒有在實作上區分 thread 和 process 的必要。
==TODO==: 列出 Linux 核心對應的原始程式碼並討論
為什麼 Address space 有階層關係
## HenryChaing
1. 為什麼 Linux 中不特別區分 Thread 跟 Process
==TODO==: 討論 `task_struct` 和 scheduling entity 的作用
在閱讀完 cpu-shced-book 後,會得知 CPU 排程的最小單位是 thread, 並且每個可以被排程的單元都具備 `task_struct` 結構體,回到問題一, Linux 核心還是有區別 Process 及 Thread, 但是 Process 可以被視為 Thread 的容器, 並且 Process 當中至少會有一個 Thread ,這些 Thread 會彼此共享 address space ,除了 Thread 之間彼此獨立的 CPU 暫存器 (context) 以及 stack 。
以 CPU 排程的觀點來看,不論是 Thread 或是 Process (這裡指 TGID $≡$ PID 的執行緒) 都具備 `task_struct` 並且都有屬於自己的 address space ,因此可被排程並且可以直接操作 address space 。
2. 那些系統呼叫可以建立新的 PID
> fork, clone vfork
3. 沒有 MMU 的環境下為什麼沒有辦法有效的實作 fork?
> 新的行程必須有獨立的 address space , 因此只能在實體記憶體新增新行程的 address space 。
[`do_execv`](https://elixir.bootlin.com/linux/v6.15/source/fs/exec.c#L2036)
3. vfork() + execve() 會不會導致 blocked parent process 永遠被 blocked 。
> 節錄至 Linux Manual Page 內容:
> vfork() differs from fork(2) in that the ==calling thread is suspended until the child terminates (either normally, by calling _exit(2), or abnormally, after delivery of a fatal signal), or it makes a call to execve(2).== Until that point, the child shares all memory with its parent, including the stack.
也就是說,在呼叫 execve() 的當下, child process 就不會與 parent process 共享 address space 了。
4. ==TODO==: 從 CPU 排程器的角度解釋 vfork 系統呼叫的行為 (是 clone 系統呼叫的特例)
> 參照 fork 前世今生
首先先解釋 clone() 系統呼叫,它與 fork() 系統呼叫相同會建立子行程,但是與沒有參數的 fork 不同的地方在於 clone 可以指定新建立行程(或是 CLONE_THREAD 建立執行緒)的參數,讓新行程不必在建立後還要自行更改,並且可以與親代行程共用指定的資訊。
至於為何 vfork 是 clone 的特例是因為 clone 有 `CLONE_VFORK` 讓它與 vfork 行為相同,兩個系統呼叫都會 block 親代行程,==讓子行程直接使用親代行程的 address space==,直到子行程呼叫 __exit(), execve() 這兩個系統呼叫。
以 CPU 排程器的觀點來看,系統呼叫成立的當下,親代行程會進到 `TASK_UNINTERRUPITBLE` ,子行程則是 `TASK_RUNNING` ,直到上述的特定事件發生,親代行程才會回到 `TASK_UNINTERRUPITBLE` 。因此兩個行程在此期間共用 address space ,但是有其獨立的 `task_struct` 。
## alexc-313
MMU 最基本的操作單元是甚麼?
:Page
當 fork 產生時在此圖中對應到什麼?

==TODO==: 搭配課程教材 (特別是 [Linux 核心 Copy On Write 實作機制](https://hackmd.io/@linD026/Linux-kernel-COW-content)),解釋 fork 過程中的 CoW 和 KSM 處理機制,搭配 Linux 核心原始程式碼解說
:在呼叫 fork 之後,會先複製原先 process 的 mm_struct , vm_area_struct 以及 page table ,並且讓每個 page 的 flag 設為 read-only 。最後,當有作更改時則會利用 COW 機制進行處理。
:當多個程序中有內容相同的頁面 (例如 library),KSM 可以將它們合併成一頁,而在 Linux 中,背景程序 ksmd 定期掃描這些頁面,尋找可合併的。
## LeoriumDev
Copy on write 的功能與目的是甚麼?
:如果有多個行程要存取相同資料的時候,這些行程會存取同一份資料 (利用 MMU 將 virtual address 做 address translation 到同一塊 physical memory),只有在行程要更改資料的時候,kernel 才會建立副本給該行程去使用,這麼做的目的是減少記憶體資源的浪費,不用每次存取都複製一份。
==TODO==: 探討 CoW 的限制和適用場景
:CoW 的使用場景是當多個行程要存取同一份資料,且並沒有要頻繁的修改資料的情況下就很適合使用。例如,在使用 fork() system call 的時候,新建立的行程不會複製原有行程的 address space,而是利用 CoW 機制,在有修改資料的情況下才複製。
(追蹤 dirty page)
:然而,CoW 並不是完美的,如果要頻繁的寫入資料,CoW 這個機制會造成 write overhead,因為一開始被存取的 page 會被標記為 read-only,如果有行程要寫入這段記憶體,kernel 會觸發 page fault,接著會複製原有資料到新的 page 上,行程的 page table 的資料會指向新的 page。如果是直接存取/寫入的話,就不需要配置新的記憶體。在頻繁的修改資料的情況下,CoW 也會造成 external fragmentation,導致記憶體會被切割成很多小區塊。
> https://www.kernel.org/doc/gorman/html/understand/understand016.html
> https://source.android.com/docs/core/perf/lmkd
> Pressure stall information (PSI)
> https://www.kernel.org/doc/Documentation/vm/overcommit-accounting
一旦系統遇到 Out Of Memory (OOM) 時,作業系統無法有效進行 page walking,就無法建立新的 process(要找到有效的 page!),自然無法進行 CoW
以手機為例,舉出頻繁寫入的例子。
:除此之外,有一個 Linux 中有關 CoW 的漏洞 [Dirty CoW](https://en.wikipedia.org/wiki/Dirty_COW),這個漏洞會導致 user 能夠將 read-only 變為 writable 的資料映射,也就是能寫入原先 read only 的資料。
Fork 的目的是甚麼?
:建立子行程
這張圖在講什麼?

:VMA 到 PMA 的映射
如果要存取灰色的 page 時會發生 Page fault 是哪種 page fault。
:Invalid page fault
Page fault 有分三種:
- Minor (soft) page fault - 如果 page 在 fault 產生的同時載入到記憶體,但 MMU 沒有更新 page table 就稱為 minor page fault。
- Major (hard) page fault - 作業系統會延後將程式的某些部分載入記憶體 (lazy loading?),直到程式嘗試使用這段資料,就會產生 page fault。如果發生 page fault 時 page 未載入到記憶體中,就稱為 major page fault。
- Invalid page fault - 如果行程 (程式) 嘗試存取不屬於自己 VMA 內的資料,就會導致沒有對應的 page,觸發 page fault,這被稱為 invalid page fault。通常 OS 的 page fault handler 會回傳 segmentation fault,接著終止該行程。
==TODO==: 搭配 Linux 核心原始程式碼予以解說,以 RISC-V 或 Aarch64 架構為例
在 `linux/arch/arm64/mm/fault.c` 有一個函式 `do_page_fault` 會處理 Aarch64 (ARM64) 架構下的 page fault。
在這段程式碼可以看到如果 kernel 找不到行程的 vma 映射,就會把 `si_code` 設為 `SEGV_MAPERR`,`SEGV_MAPERR` 是一種 segmentation fault (SEGV, segmentation violation) 的類別,`MAPERR` (mapping error) 是代表沒有找到物件相對應的映射,詳見 `linux/include/uapi/asm-generic/siginfo.h` 的 `SIGSEGV si_codes` (可以利用 [Bootlin Elixir Cross Referencer](https://elixir.bootlin.com/linux/v6.15/source) 來查找 type 或 macro 的定義)。接著會跳到 `bad_area` label。
另外,`unlikely` 是 linux 中的一個 macro,目的是優化 branch prediction,加上 `unlikely` 可以讓 compiler 減少跳到這段程式的機會,還有相對應的 `likely`。([stack overflow](https://stackoverflow.com/questions/109710/how-do-the-likely-unlikely-macros-in-the-linux-kernel-work-and-what-is-their-ben))
```c
retry:
vma = lock_mm_and_find_vma(mm, addr, regs);
if (unlikely(!vma)) {
fault = 0;
si_code = SEGV_MAPERR;
goto bad_area;
}
```
在 bad_area 底下這段程式,註解有提到「如果嘗試存取記憶體映射外的記憶體」會執行下面這段程式,就是上面在探討的存取灰色區塊會觸發的 error。這裡 `SEGV_PKUERR` (**SEG**mentation-**V**iolation: **P**rotection-**K**ey for **U**serspace **ERR**or) 是有關於 [Memory Protection Keys](https://docs.kernel.org/core-api/protection-keys.html) 機制 (Mechanism) 的 error。`else` 下面函式 `arm64_force_sig_fault` 就是接著要討論的內容。
```c
bad_area:
...
/* Something tried to access memory that out of memory map */
if (si_code == SEGV_PKUERR)
arm64_force_sig_fault_pkey(far, inf->name, pkey);
else
arm64_force_sig_fault(SIGSEGV, si_code, far, inf->name);
```
用 [Bootlin Elixir Cross Referencer](https://elixir.bootlin.com/linux/v6.15/source/arch/arm64/kernel/traps.c#L258) 可以找到,`arm64_force_sig_fault` 的定義。
```c
void arm64_force_sig_fault(int signo, int code, unsigned long far,
const char *str)
{
arm64_show_signal(signo, str);
if (signo == SIGKILL)
force_sig(SIGKILL);
else
force_sig_fault(signo, code, (void __user *)far);
}
```
這個函式會先將 signal 相關的錯誤資訊顯示出來,然後根據 signal 類型選擇不同的方式來送出 signal。我們這裡關注的是 `SIGSEGV` 的情況,接著 `force_sig_fault` 會呼叫 `force_sig_fault_to_task`。
```c
int force_sig_fault(int sig, int code, void __user *addr)
{
return force_sig_fault_to_task(sig, code, addr, current);
}
```
接著,會建立一個 kernel_siginfo 結構體,並作為參數呼叫 `force_sig_info_to_task` 函式。
```c
int force_sig_fault_to_task(int sig, int code, void __user *addr,
struct task_struct *t)
{
struct kernel_siginfo info;
clear_siginfo(&info);
info.si_signo = sig;
info.si_errno = 0;
info.si_code = code;
info.si_addr = addr;
return force_sig_info_to_task(&info, t, HANDLER_CURRENT);
}
```
最後就來到最終執行 signal 操作的程式了。首先可以看到 `spin_lock_irqsave` 這個函式,
```c
static int
force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t,
enum sig_handler handler)
{
unsigned long int flags;
int ret, blocked, ignored;
struct k_sigaction *action;
int sig = info->si_signo;
spin_lock_irqsave(&t->sighand->siglock, flags);
action = &t->sighand->action[sig-1];
ignored = action->sa.sa_handler == SIG_IGN;
blocked = sigismember(&t->blocked, sig);
if (blocked || ignored || (handler != HANDLER_CURRENT)) {
action->sa.sa_handler = SIG_DFL;
if (handler == HANDLER_EXIT)
action->sa.sa_flags |= SA_IMMUTABLE;
if (blocked)
sigdelset(&t->blocked, sig);
}
/*
* Don't clear SIGNAL_UNKILLABLE for traced tasks, users won't expect
* debugging to leave init killable. But HANDLER_EXIT is always fatal.
*/
if (action->sa.sa_handler == SIG_DFL &&
(!t->ptrace || (handler == HANDLER_EXIT)))
t->signal->flags &= ~SIGNAL_UNKILLABLE;
ret = send_signal_locked(sig, info, t, PIDTYPE_PID);
/* This can happen if the signal was already pending and blocked */
if (!task_sigpending(t))
signal_wake_up(t, 0);
spin_unlock_irqrestore(&t->sighand->siglock, flags);
return ret;
}
```
duplicate 跟 copy 的差異?
:在 Linux manual pages - fork(2) 裡面有提到:
> fork() creates a new process by **duplicating** the calling process. The
new process is referred to as the child process. The calling process
is referred to as the parent process.
這裡的 duplicate 的意思是建立與親代行程一模一樣的記憶體空間的副本 (如果不考慮 COW 的話),而如果是 copy 可能是僅複製一小段,是局部範圍的複製,不是整個行程的副本。如同 `strdup` 是配置新的記憶體空間,複製完整的字串,所以函式名稱是 string duplicate。
dirty page 是什麼?
:當 pages 被載入到記憶體中,如果程式對該資料進行了修改,就會導致這個 page 被標記為 dirty page,之後 OS 會把修改後的資料寫回硬碟。
## markarz
建立執行緒的時 Heap 的內容是否有更動?
==TODO==: 以 strace 或 [uftrace](https://github.com/namhyung/uftrace) 一類的工具,追蹤 `pthread_create` 函式成功執行前後,heap 內容的變化
pthread_create 是否是系統呼叫?
:不是
要用什麼系統呼叫才能建立執行緒?
:clone()
$\to$ 觀察 https://gist.github.com/ankurs/179778
`watch -n 1 ./thread`
glibc 提供 malloc_stats() 和 malloc_info() 函式,可顯示 process 的 heap 資訊
pthread_create 一定會運用 malloc (或類似的函式) 來配置記憶體
## kuanyu0712
什麼系統呼叫可以建立行程? (即在系統取得新的 PID)
:clone()
那怎麼用 clone() <s>創建</s> 一個threads?
:CLONE_THREAD
==TODO==: `CLONE_THREAD` 旗標的作用?為何 `CLONE_DETACHED` 選項被棄置?參照 Linux man-pages 描述並搭配 git log,揣摩 Linux 核心開發者的考量
NPTL: native POSIX thread library (Linux v2.5/v2.6)
1:1 threading model : Linux 核心採用的模型
TGID (Thread Group ID)
process 本質上是一種「容器」(比喻),裡頭可「置放」若干個 thread
: https://hackmd.io/@sysprog/linux-scheduler
同一個 thread group,對於 CPU 排程有何影響
[kill](https://pubs.opengroup.org/onlinepubs/9690949499/functions/kill.html) vs. [pthread_kill](https://pubs.opengroup.org/onlinepubs/9690949499/functions/pthread_kill.html) 都是「傳遞」signal,區別是「範圍」
使用 gdb 時,要留意 threads
> https://sourceware.org/gdb/current/onlinedocs/gdb.html/Threads.html
> LWP (lightweight process) = thread
main thread 總是會存在
```
(gdb) info threads
Id Target Id Frame
* 1 process 35 thread 13 main (argc=1, argv=0x7ffffff8)
2 process 35 thread 23 0x34e5 in sigpause ()
3 process 35 thread 27 0x34e5 in sigpause ()
at threadtest.c:68
```
> PID 為 35, TID (thread ID)
> `sigpause` 是 signal handler
## leowu0411
Nabla Containers 的目的是甚麼
:想要用更輕量化的方式去監測系統呼叫
==TODO==: 從之前 IBM 的技術報告去闡述 Nabla container 的著重議題和考量
詳細介紹已經整理於 [Linux 核心專題: 輕量級隔離運行環境](https://hackmd.io/@sysprog/H1mxn47-gx)
為什麼他不想用 KVM
==TODO==: KVM 搭配 VirtIO 的執行時期成本?探討過程中的資料複製次數和成本
當虛擬化環境為 QEMU/KVM 搭配 VirtIO 時,其傳送資料時會有如下行為:
* 客體核心將欲傳送資料透過 VirtIO PCI 驅動寫入與 VirtIO 裝置共享的記憶體區段稱 Virtqueue
* 觸發 VM exit,通知 KVM,KVM 通知 VirtIO 裝置從 Virtqueue 讀取資料並寫入宿主裝置
* 以網路裝置為例,VirtIO 裝置透過 `/dev/net/tun` 描述子,將資料從 Virtqueue 寫入宿主的 tap 虛擬網路裝置,接著由宿主核心接手傳送封包。
* 過程中資料的複製涉及客體核心將資料寫入 Virtqueue,VirtIO 裝置將資料寫入宿主核心空間
* 目前的理解:主要會帶來效能衝擊的應該不在資料複製,而是 VM exit 帶來的 CPU 開銷
> 參見 [Improving the Performance of VM Workloads by Optimizing Polling Time](https://www.intel.com/content/www/us/en/developer/articles/technical/improving-performance-vm-workloads-opt-poll-time.html)
處理器對虛擬化的支援,是透過稱為 VMX operation 的處理器運作模式提供的。VMX 運作模式中有兩種主要的轉換:
* 進入 VMX non-root 運作稱為 VM entry
* 從 VMX non-root 運作回到 VMX root 運作稱為 VM exit
處於 VMX root 運作時,處理器行為與一般 CPU 運作相似。而在 VMX non-root 運作時,處理器行為會受到限制與修改,以利虛擬化的實現;在 VMX non-root 狀態下,某些指令與事件會引發 VM exit,將控制權轉交給虛擬機監控器(VMM)。(於此討論的狀況即為 VirtIO 驅動寫入 Virtqueue 觸發)
處理器必須在發生 exit 時,儲存該 VM 當下執行狀態的快照(暫存器狀態),以使用 KVM 作為 hypervisor 並針對 Intel 處理器,會發生如下動作:

在 VMM 執行完其系統管理功能後,會進行對應的 VM entry,將處理器控制權從 VMM 轉回 VM,並以相反順序重複以上步驟。由此可見,VM exit 的過程需要相當多的處理:光是一次轉換就可能耗費數百到上千個 CPU cycle,展示 VM exit 本身造成顯著開銷的原因。
對比 Nabla Linux,其作為運行於宿主作業系統的一般行程,不依賴處理器對虛擬化的支援,同樣以客體行程欲傳送網路封包為例,中間的資料複製唯涉及客體核心將資料透過 `/dev/net/tun` 描述子將資料寫入宿主的 tap 虛擬網路裝置,避免儲存/恢復處理器快照、通知 VirtIO 裝置等開銷,相當於只須完成 VirtIO 裝置的任務,就成功將客體行程封包傳遞給宿主核心。