# 基本資料

作者：[Michael Kerrisk](https://man7.org/) 譯者：[廖明沂（Aaron Liao）](http://aaron.netdpi.net/)、[楊竹星（Chu-Sing Yang）](https://www.itlab.ee.ncku.edu.tw/csyang/professor.html)

出版社：碁峯資訊（[上冊](http://books.gotop.com.tw/v_AXP015800)、[下冊](http://books.gotop.com.tw/v_AXP015900)）

範例程式：<http://man7.org/tlpi/code/index.html>

<figure><img src="/files/OKWPrPjGtW48kLogn58D" alt=""><figcaption></figcaption></figure>


# 評價

評價

* 「若在開發 Linux 系統程式時只能放一本書在電腦旁，這本就是首選。」－Martin Landers，Google 軟體工程師
* 「這本新書是美好的事物，你會因為書架有收藏這本書而引以為傲，不過因為你會經常使用它，因此你不會有機會將它束之高閣。」－Serge Hallyn，Linux Kernel 開發者
* 「我發現這本書非常實用，而且會一直想要翻閱，每一個對 Linux 程式開發有興趣的人都會沈浸在這樣的感覺裡。」－Jake Edge，LWN.net
* 「在這本書的詳細說明與範例中，它們涵蓋了你所需了解的每件事情以及 Linux 底層 API 的微小差異。無論讀者的程度如何，都可以從這本書獲得成長。」－Mel Gorman，Understanding the Linux Virtual Memory Manager 一書的作者
* 「作者將他堅持到底所獲得的正確資訊，以簡單明瞭的方式表達，使這本書成為程式設計師重要的參考資料。雖然這本書的主要對象是 Linux 系統開發人員，但也適用於 UNIX/POSIX 生態系統（ecosystem）的程式設計師。」－David Butenhof，Programming with POSIX Threads and Contributor to the POSIX and UNIX Standards 的作者
* 「簡單說來，這是從 Stevens 的 UNIX 程式設計系列至今，最優質的 Linux 程式設計書籍。」－Chris Barber，CB1，Inc.
* 「這本書涵蓋的內容博大精深，如教科書般貼心地提供豐富範例與習題。從理論到動手實作程式碼，每個主題都清楚與全面的涵蓋。教授、學生與教育訓練師們，這本是你們等待已久的 Linux/UNIX 用書。」－Anthony Robins，資訊科學系副教授，The University of Otago
* 「這本書不僅可以打通你的任督二脈、書中的內容也不難理解，透過 Linux 系統來介紹 UNIX 系統與網路程式設計。我很樂於將這本書推薦給想要學習 UNIX 程式設計的人，以及想了解熱門的 GNU/Linux 系統有何新奇之處的資深 UNIX 程式開發人員。」－Fernando Gont，網路安全研究員、IETF 與會者及 RFC 作者
* 「如果你覺得你已經都懂了，所以不會需要這本書，其實，我本來也這麼想，不過我錯了。」－Bert Hubert，電腦諮詢顧問，Netherlabs
* 「這本書非常與眾不同，書中詳細記載我能想到的每個相關主題，讓我一直沈浸在閱讀的享受。」－Federico Lucifredi，Slashdot.org
* 「這本書是認真的、專業的 Linux 及 UNIX 系統開發者不可或缺的資源。作者清楚說明與並使用範例導引，涵蓋 Linux 與 UNIX 系統所有關鍵 API 的使用，並強調下列這些標準的重要性與用處，比如：Single UNIX Specification 與 POSIX 1003.1。」－Andrew Josey，Director，Standards，The Open Group，and Chair of the POSIX 1003.1 Working Group
* 「站在系統開發者的立場，有誰能比一本包山包海的 Linux 系統開發大全更好，除了 man page 的維護人員本身，還有誰能寫出這樣的書呢？這本書的內容不僅廣泛又很周全，我深深地期待我書架上能有這麼一本不可或缺的書。」－Bill Gallmeister，POSIX.4 Programmer’s Guide: Programming for the Real World 的作者
* 「這本書的內容更為清楚與完整，而且寫的跟 Stevens 與 Rochkind 的書一樣好，它現在可能會成為 Linux/UNIX 系統程式設計的經典。」－Vladimir G. Ivanovic，軟體產品管理師
* 「對於 Linux 與 UNIX 系統程式設計最完整且最新的書，如果你是剛開始接觸 Linux 程式開發的人；或者你是 UNIX 老手，感興趣的是 Linux 系統的可攜性（portable）；或者你只是單純想找一本 Linux API 好書，那麼毫無疑問地，這本書就是你想要收藏於書架的好夥伴。」－LOÏC DOMAIGNÉ，CHIEF SOFTWARE ARCHITECT (EMBEDDED)，CORPULS.COM
* 「作者不僅寫了一本很棒的書來描述 Linux 開發及 Linux 與各種標準的關聯，而且也提到他所發現的錯誤與修正，也包含（大幅度）改善的 man page，因此，使開發 Linux 程式變的很簡單。這本書深入探討 The Linux Programming Interface．．．的主題，因此會是 Linux 程式設計的新手與老手都人手一本的開發手冊。」－Andreas Jaeger，專案管理者，OpenSUSE，Novell
* 「一本超乎水準的書，內含經典珍藏著作 Advanced Programming in the UNIX Environment 的風格，相當清晰地介紹每一個系統呼叫（system call），包含說明 Single UNIX Specification 規範以外的部分，以及各門派的 UNIX 比較。」－John Wiersba，Linux/UNIX 程式設計師，Thomson Reuters
* 「我對於這本書內容的精確、品質與細膩程度非常印象深刻，他是頂尖的 Linux 系統呼叫專家，而且樂於將他的知識及 Linux API 的了解與我們分享。」－Christophe Blaess，Programmation système en C sous Linux 一書的作者
* 「這本書毫無疑問地將成為 Linux 系統程式設計的經典導讀。」－Pedro Pinto，Lead Software Infrastructure Architect，Blue Capital Group

[More](http://www.google.com/url?q=http%3A%2F%2Fman7.org%2Ftlpi%2Freviews%2Findex.html\&sa=D\&sntz=1\&usg=AOvVaw1PIyxivbbNKDdgaTR9fkEG)


# 問與答

問與答

問：這本書與 “The Linux Programming Interface: A Linux and Unix System Programming Handbook” 這本原文書版本相同嗎 ？是2010-09-21的版本嗎 ？

答：目前為止（2016/10）這本原文書只有初版，因此中文版與原文版是相同的，而且中文版在送印刷前已經有遵循作者（[Michael Kerrisk](http://www.google.com/url?q=http%3A%2F%2Fman7.org%2F\&sa=D\&sntz=1\&usg=AOvVaw19S-WrykA1b3ZHuZYwKjIo)）的[勘誤表](http://www.google.com/url?q=http%3A%2F%2Fman7.org%2Ftlpi%2Ferrata%2Findex.html\&sa=D\&sntz=1\&usg=AOvVaw1eS-6qiKHnpJaSl9690c9W)將內容進行錯誤與更新等修訂。

問：如果發現書中的錯誤，如何回報給你們？

答：請將建議或問題發信到 <TLPI@netdpi.net>（或是透過臉書傳遞訊息：[https://www.facebook.com/TLPITW/](https://www.google.com/url?q=https%3A%2F%2Fwww.facebook.com%2FTLPITW%2F\&sa=D\&sntz=1\&usg=AOvVaw1GtUh-VVVFcI0B9Nv79NEX)），我們就可以收到您的回報，並提供在中文版的勘誤表（[http://tlpi.netdpi.net/errata](http://www.google.com/url?q=http%3A%2F%2Ftlpi.netdpi.net%2Ferrata\&sa=D\&sntz=1\&usg=AOvVaw2j4fTpzqRAXhmAvJrFv2TR)），謝謝。

問：哪裡可以買到這本書？

答：（如果尚未絕版）可以在出版社（[上冊](http://www.google.com/url?q=http%3A%2F%2Fbooks.gotop.com.tw%2Fv_AXP015800\&sa=D\&sntz=1\&usg=AOvVaw0kGwcT4tsMrll8FivY3fsZ)、[下冊](http://www.google.com/url?q=http%3A%2F%2Fbooks.gotop.com.tw%2Fv_AXP015900\&sa=D\&sntz=1\&usg=AOvVaw3IkhpwrMNg0JyBBC60AwVU)）或各大書局購買，如[天瓏書局](http://www.google.com/url?q=http%3A%2F%2Fwww.tenlong.com.tw\&sa=D\&sntz=1\&usg=AOvVaw13SMoJwSnSfA_x54OswUIg)、[博客來書局](http://www.google.com/url?q=http%3A%2F%2Fwww.books.com.tw\&sa=D\&sntz=1\&usg=AOvVaw3EUxTzWqmcenTK7_7pjkLp)、[誠品書局](http://www.google.com/url?q=http%3A%2F%2Fwww.eslite.com%2F\&sa=D\&sntz=1\&usg=AOvVaw0YBUpbZIATvxjQGoHY8v16)、[金石堂書局](http://www.google.com/url?q=http%3A%2F%2Fwww.kingstone.com.tw\&sa=D\&sntz=1\&usg=AOvVaw2OXLpgZoTdQ5nIWvoKUiri)、[三民書局](http://www.google.com/url?q=http%3A%2F%2Fwww.sanmin.com.tw\&sa=D\&sntz=1\&usg=AOvVaw1taU36nxp71OotgJZh9EHK)、[PC home書店](http://www.google.com/url?q=http%3A%2F%2F24h.pchome.com.tw%2Fbooks%2F\&sa=D\&sntz=1\&usg=AOvVaw1FfE4hBzBhf3T6I6hbhV36)、[MOMO 購物](https://www.google.com/url?q=https%3A%2F%2Fwww.momoshop.com.tw\&sa=D\&sntz=1\&usg=AOvVaw37Qn5p0ACp96D17Uqg4THv)等。要注意的是，這本書在中文版拆開分成兩冊獨立販售，上冊（第1章～第33章）與下冊（第34章～第64章）。

問：如何取得書中的範例程式？

答：由於作者會不定期更新程式碼內容，所以建議可以到作者維護的 [http://man7.org](http://www.google.com/url?q=http%3A%2F%2Fman7.org%2F\&sa=D\&sntz=1\&usg=AOvVaw19S-WrykA1b3ZHuZYwKjIo) 官方網頁下載最新版本（[下載點](http://www.google.com/url?q=http%3A%2F%2Fman7.org%2Ftlpi%2Fcode%2Findex.html\&sa=D\&sntz=1\&usg=AOvVaw0QQAmIiG82EgppDe0cr3wE)）。


# 勘誤表

我們將持續追蹤並提供修訂，此頁面提供修正內容，包含：原文更新、語意調整、錯字訂正等。若讀者有問題、建議、或內容、程式碼、實作等有不明瞭的地方，都可來信討論。

* * 讀者意見信箱：<TLPI@netdpi.net>
  * 英文版勘誤表（[http://man7.org/tlpi/errata/index.html](http://www.google.com/url?q=http%3A%2F%2Fman7.org%2Ftlpi%2Ferrata%2Findex.html\&sa=D\&sntz=1\&usg=AOvVaw1eS-6qiKHnpJaSl9690c9W)）
  * 中文版勘誤表：[http://tlpi-errfix.netdpi.net](http://www.google.com/url?q=http%3A%2F%2Ftlpi-errfix.netdpi.net\&sa=D\&sntz=1\&usg=AOvVaw0kbBXH5t9aAgy8vDu8hNah) （中文勘誤表已經追隨到原文勘誤表的 2018/08/20）
  * 中文勘誤表更新日期：2018年11月25日


# 範例程式解析

這個地方會不定期介紹書中的範例程式

TLPI (The Linux Programming Interface) 的價值之一就是有許多的範例程式，而原作也將這些範例程式以 GNU 授權的方式提供，即使沒有買到這本書，這些範例也是很有價值與幫助，小編將不定期抽出時間在這個專頁提供 TLPI 的範例程式解析。

這裡介紹的 source code 會以小編放在這個 github 的為主：

<https://github.com/myliao2007/tlpi-sample/>

原作的範例程式採 GNU 授權

All of the source code is copyright Michael Kerrisk, and each file is licensed under the either [GNU General Public License (Version 3)](http://www.fsf.org/licensing/licenses/gpl-3.0.html) (most files) or the [GNU Lesser General Public License (Version 3)](http://www.fsf.org/licensing/licenses/lgpl-3.0.html) (library code); see the individual files for details.

原作的最新範例程式

{% embed url="<https://man7.org/tlpi/code/index.html>" %}


# TLPI 程式碼問答集

原文來源：<https://man7.org/tlpi/code/faq.html>

## 一般的錯誤與警告

### 問題一：Warning: "\_XOPEN\_SOURCE" redefined

當使用提供的 makefile 編譯程式時，某些範例程式的“書籍”版本會產生以下編譯警告：

```bash
warning: "_XOPEN_SOURCE" redefined
```

要了解出現此警告的原因，需注意兩條有用的背景訊息：

* 本書中的所有原始碼範例都是寫出來的，因此它們預設會使用 GNU C 編譯器選項，或以下選項（對應於符合 SUSv3+C99 標準的編譯環境）進行編譯：

  <pre class="language-bash"><code class="lang-bash"><strong>$ cc -std=c99 -D_XOPEN_SOURCE=600
  </strong></code></pre>
* 程式的 makefile 包含了啟用大多數可能警告的編譯器選項 (-pedantic -Wall -W)。

範例程式中使用的一些介面需要有定義 \_XOPEN\_SOURCE。如果使用這些介面的程式是用 -std=c99 -D\_XOPEN\_SOURCE=600 編譯的，那麼一切都很正常。但是，如果此類程式在編譯時沒有定義 \_XOPEN\_SOURCE ，但又如上所述那樣打開了警告選項，則會導致錯誤或警告（例如，諸如函數 “xxxxxx” 的隱含宣告之類的訊息）。

我為這些程式決定的解決方案是，包含一個預先處理器（preprocessor）#define 語句，它適當地定義了 \_XOPEN\_SOURCE。此解決方案的局限性在於，當使用 -D\_XOPEN\_SOURCE=600 編譯這些程式，並且原始檔案使用不同的值重新定義 \_XOPEN\_SOURCE 時，編譯器會發出有關重新定義的警告。

這兩個範例對於避免編譯器警告，示範了更完整的解決方案：

```c
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE
#endif

#if ! defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 500
#define _XOPEN_SOURCE 500
#endif
```

由於代碼緊湊的原因，此方法未在「書籍」版本的程式碼中採用，但在「distribution」版本中會採用。

也可以參考 TLPI 書籍的 3.6.1 節。

### 問題二：error: sys/acl.h: No such file or directory

如果您遇到錯誤 sys/acl.h: No such file or directory，那是因為您的系統上沒有安裝所需的 ACL套件。

在 Debian 或是 Ubuntu 系統，用如下的指令安裝應該會有用：

<pre class="language-bash"><code class="lang-bash"><strong>$ sudo apt-get install libacl1-dev
</strong></code></pre>

在使用 RPM 安裝套件的系統上，你會需要安裝 libacl-devel 套件。

### 問題三：error: sys/capability.h: No such file or directory

如果你看到錯誤訊息「error sys/capability.h: No such file or directory」

這是因為你的系統沒有安裝所需的套件。在 Debian 或 Ubuntu 系統，你可以用下面的指令安裝看看，應該會有用：

<pre class="language-bash"><code class="lang-bash"><strong>$ sudo apt-get install libcap-dev
</strong></code></pre>

在使用 RPM 安裝套件的系統上，你會需要安裝 libcap-devel 套件。

### 問題四：error: selinux/selinux.h: No such file or directory

如果你看到這個錯誤訊息「selinux/selinux.h: No such file or directory」，這是因為你的系統又有套件沒有安裝。在 Debian 或 Ubuntu 系統，你可以用下面的指令安裝看看，應該會有用：

<pre class="language-bash"><code class="lang-bash"><strong>$ sudo apt-get install libselinux1-dev
</strong></code></pre>

在使用 RPM 安裝套件的系統上，你會需要安裝 libselinux-devel 套件。

### 編譯單一個檔案遇到的錯誤與警告

#### lib/cap\_functions.c

如果你看到錯誤訊息「sys/capability.h: No such file or directory」，請參考問題三。

#### lib/userns\_functions.c

如果你看到錯誤訊息「sys/capability.h: No such file or directory」，請參考問題三。

#### acl/acl\_update.c

如果你看到錯誤訊息「sys/capability.h: No such file or directory」，請參考問題二。

#### acl/acl\_view\.c

如果你看到錯誤訊息「sys/capability.h: No such file or directory」，請參考問題二。

#### cap/cap\_launcher.c

如果你看到錯誤訊息「sys/capability.h: No such file or directory」，請參考問題三。

#### cap/cap\_text.c

如果你看到錯誤訊息「sys/capability.h: No such file or directory」，請參考問題三。

#### cap/check\_password\_caps.c

有關編譯「書本」版本的程式碼時發生的「\_XOPEN\_SOURCE」重新定義警告的訊息，請參考問題一。

如果你看到錯誤訊息「sys/capability.h: No such file or directory」，請參考問題三。

#### cap/demo\_file\_caps.c

如果你看到錯誤訊息「sys/capability.h: No such file or directory」，請參考問題三。

#### cap/view\_cap\_xattr.c

如果你看到錯誤訊息「sys/capability.h: No such file or directory」，請參考問題三。

#### dirs\_links/list\_files\_readdir\_r.c

編譯此程式時，在使用 glibc 2.24 或更高版本編譯時，您可能會遇到警告 'readdir\_r' is deprecated \[-Wdeprecated-declarations]。 （[我如何得知系統上的 glibc 版本呢？](https://man7.org/tlpi/code/faq.html#glibc-version)）這是因為自 glibc 2.24 以來 readdir\_r() 已被棄用。有關詳細信息，請參閱[第 357 頁的勘誤表](http://man7.org/tlpi/errata/index.html#p_357)。

#### filesys/t\_mount.c

編譯此程式的「book」版本時，若保留各種MS\_\*常數為未宣告，可能會發生錯誤。如果系統上的 GNU C 函式庫比2.12版舊時，就會發生此錯誤，[因為舊版的 glibc 並未定義某些必要的常數](http://sources.redhat.com/bugzilla/show_bug.cgi?id=11235)。（[如何找出系統上有哪個版本的glibc？](https://man7.org/tlpi/code/faq.html#glibc-version)）

解決的方法是在使用較新的 glibc 版本的系統上編譯程式，或是編譯程式的「distribution」版本。

此程式的「distribution」版本不會發生問題，此版本包含的程式碼，可依據條件定義所需的常數（如果未在 glibc 表頭中定義)。請參考兩個程式版本的[差異](https://man7.org/tlpi/code/online/diff/filesys/t_mount.c.html)。

#### pgsjc/catch\_SIGHUP.c

如需 "\_XOPEN\_SOURCE" 重新定義警告（編譯此程式的 "book" 版本時發生）的說明，請參閱[此處](https://man7.org/tlpi/code/faq.html#_XOPEN_SOURCE-redefined)。

#### pgsjc/t\_setsid.c

如需 "\_XOPEN\_SOURCE" 重新定義警告（編譯此程式的 "book" 版本時發生）的說明，請參閱[此處](https://man7.org/tlpi/code/faq.html#_XOPEN_SOURCE-redefined)。

#### proc/setjmp\_vars.c

變數 'rvar' 可能會被 'longjmp' 或 'vfork' 警告所阻擋，在編譯此程式時可預期出現該警告。GNU C 編譯器正是在準確診斷這個程式所示範的問題。請見這本書第137頁。

#### shlibs/dynload.c

如果您遇到函式內不允許的 #pragma GCC 診斷錯誤，這是因為您使用的是舊版的 GNU C 編譯器，它不支援使用 #pragma 語法，因而程式若使用了 #pragma，則會導致編譯器發出警告。

如果可以，請升級至 gcc 4.6（2011年3月）或更新的版本。否則，請編輯程式，移除所有以 #pragma 開頭的那行程式碼並重新編譯（此時您會看到先前隱藏的警告訊息）。

#### signals/nonatomic\_uint64.c.c

如果發生錯誤 bits/wordsize.h：No such file or directory or sys/cdefs.h: No such file or directory。您執行 Linux 套裝版本 （例如Ubuntu或Debian）可能是需要安裝 gcc-multilib 套件，才能建置此程式的。類似下列的安裝動作應該能正常運作：

<pre><code><strong>$ sudo apt-get install gcc-multilib
</strong></code></pre>

#### threads/thread\_multijoin.c

在 64 位元的系統上編譯時，您會看到有關指標與不同大小的整數之間的型別轉換警告。我應該在這個程式中使用 intptr\_r 型別。但是，由於[第 649 頁勘誤表](https://man7.org/tlpi/errata/index.html#p_649)末尾所談的原因，我選擇不對程式碼或文字進行任何的更改。

#### sockets/scm\_security\_send.c

如果發生錯誤 selinux/selinux.h: No such file or directory, 請參考問題四。

#### time/strtime.c

在編譯此程式的書籍版本時，若出現警告重複定義 "\_XOPEN\_SOURCE", 請參考問題一。

#### time/t\_stime.c

For an explanation of the "\_XOPEN\_SOURCE" redefined warning that occurs when compiling the "book" version of this program, look [here](https://man7.org/tlpi/code/faq.html#_XOPEN_SOURCE-redefined).

#### users\_groups/check\_password.c

For an explanation of the "\_XOPEN\_SOURCE" redefined warning that occurs when compiling the "book" version of this program, look [here](https://man7.org/tlpi/code/faq.html#_XOPEN_SOURCE-redefined).

### General questions

#### Why are there "book" and "distribution" versions of the example source code?

The example source code is provided in two versions. The reasons for this are explained [here](https://man7.org/tlpi/code/why_diff.html).

#### Where is the file lib/ename.c.inc?

哈囉！感謝您的詢問。有些讀者指出，在 TLPI 的第 58 頁上顯示的 lib/ename.c.inc 檔案在從書籍網站下載的程式碼 tarball 中並不存在。這是因為當在 lib 目錄（或原始碼的根目錄）中執行 make(1) 時，此檔案會透過腳本自動建立。請參閱 lib/Makefile，其中呼叫 Build\_ename.sh。之所以採用這種方式，是因為錯誤名稱集可以因核心和 glibc 版本以及硬體架構而異。

#### What version of the Linux kernel do I have on my system?

To determine the version of the Linux kernel running on your system, use the following command:

<pre><code><strong>$ uname -sr
</strong>Linux 5.10.23-200.fc33.x86_64
</code></pre>

#### What version of *glibc* do I have on my system?

On a sufficiently recent system, you can determine the version of *glibc* running on your system using the following command:

<pre><code><strong>$ getconf GNU_LIBC_VERSION
</strong>glibc 2.26
</code></pre>

If that command doesn't work (perhaps because you have an older system), then you can obtain the *glibc* version by executing the library shared object. On many systems, this is as simple as the following:

<pre><code><strong>$ /lib/libc.so.6
</strong></code></pre>

The first line of output of this command shows the *glibc* version on your system.

However, on some systems, *glibc* resides at a different path, so that the above command won't work. The following command obtains the pathname of the library by applying ldd to a dynamically linked executable at a known location, and then nests the resulting output inside command substitution to execute that pathname, producing the same results as the simpler command above.

<pre class="language-bash"><code class="lang-bash"><strong>$ ldd /bin/ls | grep libc.so | awk '{print $3}'
</strong>/lib/i386-linux-gnu/libc.so.6
<strong>$ $(ldd /bin/ls | grep libc.so | awk '{print $3}')
</strong>GNU C Library (GNU libc) stable release version 2.26, by Roland McGrath et al.
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 7.3.1 20180303 (Red Hat 7.3.1-5).
Available extensions:
	crypt add-on version 2.1 by Michael Glad and others
	GNU Libidn by Simon Josefsson
	Native POSIX Threads Library by Ulrich Drepper et al
	BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
&#x3C;http://www.gnu.org/software/libc/bugs.html>
</code></pre>

#### Why do I get warnings "\_BSD\_SOURCE and \_SVID\_SOURCE are deprecated, use \_DEFAULT\_SOURCE"?

You are compiling the code on a system that has *glibc* 2.20 or later installed. In *glibc* 2.20, the \_BSD\_SOURCE and \_SVID\_SOURCE feature test macros were deprecated. They continue to expose the definitions that they exposed in earlier glibc versions, but their use produces the warning noted above. Instead, the \_DEFAULT\_SOURCE macro should be used. The simplest fix for this problem is to [download](https://man7.org/tlpi/code/index.html) the latest code tarball, which fixes the problem by modifying the Makefile.inc file to add -D\_DEFAULT\_SOURCE to the definition of the IMPL\_CFLAGS macro.

\\


# 執行範例程式的前置作業

如果你在編譯全部的範例程式時發生下面這個錯誤，請試著安裝 libpca-dev：

```bash
aron@aaron-Virtual-Machine:/tmp/tlpi-sample/tlpi-dist/lib$ make
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o cap_functions.o cap_functions.c
In file included from cap_functions.c:20:
cap_functions.h:22:10: fatal error: sys/capability.h: No such file or directory
   22 | #include <sys/capability.h>
      |          ^~~~~~~~~~~~~~~~~~
compilation terminated.
make: *** [<builtin>: cap_functions.o] Error 1
```

如果你使用的是 Ubuntu Linux 系統，可以直接使用 apt-get 安裝

```bash
$ sudo apt-get install libcap-dev
```

然後編譯 TLPI 的 library，執行結果如下：

```bash
$ cd tlpi-dist/lib
$ make
aaron@aaron-Virtual-Machine:/tmp/tlpi-sample/tlpi-dist/lib$ make
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o alt_functions.o alt_functions.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o become_daemon.o become_daemon.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o binary_sems.o binary_sems.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o cap_functions.o cap_functions.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o create_pid_file.o create_pid_file.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o curr_time.o curr_time.c
sh Build_ename.sh > ename.c.inc
echo 1>&2 "ename.c.inc built"
ename.c.inc built
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o error_functions.o error_functions.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o event_flags.o event_flags.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o file_perms.o file_perms.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o get_num.o get_num.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o inet_sockets.o inet_sockets.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o itimerspec_from_str.o itimerspec_from_str.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o print_rlimit.o print_rlimit.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o print_rusage.o print_rusage.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o print_wait_status.o print_wait_status.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o pty_fork.o pty_fork.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o pty_master_open.o pty_master_open.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o rdwrn.o rdwrn.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o read_line.o read_line.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o read_line_buf.o read_line_buf.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o region_locking.o region_locking.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o scm_functions.o scm_functions.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o signal.o signal.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o signal_functions.o signal_functions.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o tty_functions.o tty_functions.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o ugid_functions.o ugid_functions.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o unix_sockets.o unix_sockets.c
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -I../lib -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter   -c -o userns_functions.o userns_functions.c
rm -f ../libtlpi.a
ar rs ../libtlpi.a *.o
ar: creating ../libtlpi.a
```

上面的最後一行，可以看到有產生 TLPI 的靜態函式庫 (libtlpi.a)。


# 檔案處理


# 複製檔案

說明：這個程式的目的是示範複製檔案的功能，在程式設計層面可以學習到檔案的處理，基本的流程是開啟檔案、對檔案進行讀取或寫入的動作、最後不再存取檔案時則關閉檔案。

* 開啟檔案（並指定開啟檔案的權限模式）: open()
* 讀取檔案: read()
* 寫入檔案: write()
* 關閉檔案: close()

程式碼路徑：tlpi-dist/fileio/copy.c

{% code title="Listing 4-1: copy.c" lineNumbers="true" %}

```c
/*************************************************************************\
*                  Copyright (C) Michael Kerrisk, 2022.                   *
*                                                                         *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the   *
* Free Software Foundation, either version 3 or (at your option) any      *
* later version. This program is distributed without any warranty.  See   *
* the file COPYING.gpl-v3 for details.                                    *
\*************************************************************************/

/* Listing 4-1 */

/* copy.c 
   將 argv[1] 指名的檔案複製到 argv[2] 指名的新檔案
*/
#include <sys/stat.h>
#include <fcntl.h>
#include "tlpi_hdr.h"

#ifndef BUF_SIZE        /* 在 cc 編譯時，可以帶入編譯參數，例如：使用 "-D BUF_SIZE=512" 的方式來以 512 複蓋這裡的定義值 1024 */
#define BUF_SIZE 1024
#endif

int
main(int argc, char *argv[])
{
    int inputFd, outputFd, openFlags;
    mode_t filePerms;
    ssize_t numRead;
    char buf[BUF_SIZE];

    if (argc != 3 || strcmp(argv[1], "--help") == 0)
        usageErr("%s old-file new-file\n", argv[0]);

    /* 開啟檔案做為讀取輸入與寫入輸出 */

    /* 開啟要複製的檔案來源，以唯讀模式開啟 */
    inputFd = open(argv[1], O_RDONLY);
    if (inputFd == -1)
        errExit("opening file %s", argv[1]);

    openFlags = O_CREAT | O_WRONLY | O_TRUNC;
    filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
                S_IROTH | S_IWOTH;      /* rw-rw-rw- */

    /* 開啟要複製的檔案目的，需要能夠寫入 */                
    outputFd = open(argv[2], openFlags, filePerms);
    if (outputFd == -1)
        errExit("opening file %s", argv[2]);

    /* 持續複製資料，直到遇到檔案結尾或是發生錯誤為止 */
    while ((numRead = read(inputFd, buf, BUF_SIZE)) > 0)
        if (write(outputFd, buf, numRead) != numRead)
            fatal("write() returned error or partial write occurred");
            
    if (numRead == -1)
        errExit("read");

    if (close(inputFd) == -1)
        errExit("close input");
        
    if (close(outputFd) == -1)
        errExit("close output");

    exit(EXIT_SUCCESS);
}
```

{% endcode %}

編譯程式：

```bash
tlpi-dist/fileio$ gcc copy.c ../libtlpi.a -o copy -I ../lib
```

執行程式：測試複製檔案 copy.c 到 copy1.c。

```
$ ./copy copy.c copy1.c
```

如果想在編譯時指定 BUF\_SIZE，可以用 -D 編譯參數定義 BUF\_SIZE 的數值：

```bash
tlpi-dist/fileio$ gcc copy.c ../libtlpi.a -o copy -I ../lib -DBUF_SIZE=2048
```


# 檔案緩衝區處理

讀寫檔案設定適當的緩衝區（buffer）空間，可以提升讀寫的效能。


# 直接讀取檔案

說明：使用 O\_DIRECT 旗標來繞過緩衝區，直接讀寫設備或檔案。使用memalign()函數分配一塊記憶體，該記憶體區塊與其第一個參數以倍數對齊。

此範例使用O\_DIRECT旗標開啟與讀寫文件，這個程式最多接受四個參數，依次指定要讀取的文件、要從文件中讀取的位元數、程式在從文件讀取之前應該查詢的偏移量，以及傳遞給 read() 的資料緩衝區。最後兩個參數是非必要的選項，預設是 0 偏移量以及 4096 個位元組。

程式碼路徑：[tlpi-sample](https://github.com/myliao2007/tlpi-sample)/[tlpi-dist](https://github.com/myliao2007/tlpi-sample/tree/main/tlpi-dist)/[filebuff](https://github.com/myliao2007/tlpi-sample/tree/main/tlpi-dist/filebuff)/**direct\_read.c**

**Listing 13-1**

{% code title="direct\_read.c" lineNumbers="true" %}

```c
/*************************************************************************\
*                  Copyright (C) Michael Kerrisk, 2022.                   *
*                                                                         *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the   *
* Free Software Foundation, either version 3 or (at your option) any      *
* later version. This program is distributed without any warranty.  See   *
* the file COPYING.gpl-v3 for details.                                    *
\*************************************************************************/

/* Listing 13-1 */

/* direct_read.c

   Demonstrate the use of O_DIRECT to perform I/O bypassing the buffer cache
   ("direct I/O").

   Usage: direct_read file length [offset [alignment]]

   This program is Linux-specific.
*/
#define _GNU_SOURCE     /* Obtain O_DIRECT definition from <fcntl.h> */
#include <fcntl.h>
#include <malloc.h>
#include "tlpi_hdr.h"

int
main(int argc, char *argv[])
{
    int fd;
    ssize_t numRead;
    size_t length, alignment;
    off_t offset;
    char *buf;

    if (argc < 3 || strcmp(argv[1], "--help") == 0)
        usageErr("%s file length [offset [alignment]]\n", argv[0]);

    length = getLong(argv[2], GN_ANY_BASE, "length");
    offset = (argc > 3) ? getLong(argv[3], GN_ANY_BASE, "offset") : 0;
    alignment = (argc > 4) ? getLong(argv[4], GN_ANY_BASE, "alignment") : 4096;

    fd = open(argv[1], O_RDONLY | O_DIRECT);
    if (fd == -1)
        errExit("open");

    /* memalign() allocates a block of memory aligned on an address that
       is a multiple of its first argument. By specifying this argument as
       2 * 'alignment' and then adding 'alignment' to the returned pointer,
       we ensure that 'buf' is aligned on a non-power-of-two multiple of
       'alignment'. We do this to ensure that if, for example, we ask
       for a 256-byte aligned buffer, we don't accidentally get
       a buffer that is also aligned on a 512-byte boundary. */

    buf = memalign(alignment * 2, length + alignment);
    if (buf == NULL)
        errExit("memalign");

    buf += alignment;

    if (lseek(fd, offset, SEEK_SET) == -1)
        errExit("lseek");

    numRead = read(fd, buf, length);
    if (numRead == -1)
        errExit("read");
    printf("Read %ld bytes\n", (long) numRead);

    exit(EXIT_SUCCESS);
}
```

{% endcode %}


