apue.2e安裝詳解

linux下APUE源碼編譯錯誤解決

(2013-03-01 15:14:20)
  分類: Linux

文章出處:http://www.diybl.com/course/3_program/c++/cppjs/2008617/126036.html

相信很多跟我一樣想要學習unix編程的朋友在興沖沖拿到《unix環境高級編程》後,準備拿源碼練練手時,執行第一個myls就出現一大堆的錯誤,這未免時個不小的打擊。今天把解決方法寫下來,第一自己有個記錄,第二也幫助那些被同樣問題困擾的朋友儘快的進入linux美麗的世界。(只限 linux系統)
首先需要make一次源代碼
編輯源碼解壓生成的apue.2e文件夾下的Make.defines.linux
修改WKDIR=/home/var/apue.2e爲你的apue.2e目錄,比如我的apue源碼解壓在/usr/local,那我就改爲:
WKDIR=/usr/local/apue.2e
然後進入apue.2e/std 目錄,編輯linux.mk。修改裏面所有的nawk爲awk。
最後返回apue.2e目錄,執行make命令。
以下是編譯源碼時的錯誤提示跟解決方法(假定你的工作目錄跟我的一樣,爲/usr/local/apue.2e)
錯誤提示1:
myls.c:1:19: apue.h: No such file or directory
myls.c: In function `main':
myls.c:13: error: `NULL' undeclared (first use in this function)
myls.c:13: error: (Each undeclared identifier is reported only once
myls.c:13: error: for each function it appears in.)
解決辦法:
拷貝apue.h到系統默認頭文件目錄中
$cp /usr/local/apue.2e/include/apue.h /usr/include
錯誤提示2:
/tmp/ccBBopm0.o(.text+0x2b): In function `main':
: undefined reference to `err_quit'
/tmp/ccBBopm0.o(.text+0x5f): In function `main':
: undefined reference to `err_sys'
collect2: ld returned 1 exit status
解決辦法:
err_quit跟err_sys是作者自己定義的錯誤處理函數,需要單獨定義頭文件
在/usr/include 下新建一個名爲myerr.h的文件,拷貝在原書的附錄B中頭文件到其中。
《UNIX環境高級編程》源碼編譯方法
這裏要談到的一個問題就是該書中的源代碼編譯的問題。此書中差不多每個歷程中,都會有這樣一行源碼:
#include "ourhdr.h"


在第二版中改爲:
#include "apue.h"

這個頭文件是作者把把每個例程中常用的標準頭文件,一些常用的出錯處理函數(err_**()之類的函數)和一些常用的宏定義給整理在一個頭文件中。這個可以省去在每個例程中錄入較多的重複代碼,這樣可以減少每個例程的長度。但是,這樣就給讀者帶來了不少麻煩。因爲我們還要去搞明白如和把這個頭文件編譯,然後做成庫文件,添加到我們的系統中。特別讀於初學者,本來滿懷信心的,結果在編譯第一個程序的時候就出現了問題。我也沒有搞明白如何把 "ourhdr.h"靜態的編譯到系統中。

不過,不明白如何使用"ourhdr.h"這個頭文件,並不會影響我們學習APUE,也不會影響我們編譯和運行每一個例程。其實,簡單的想一下,如果一個 C程序要能順利的編譯和運行,除了我們要語法正確等方面外,最根本的是要保證我們程序中所調用的函數以及宏等等都要有完整的來源,也就是必須包含所有調用函數和宏所在的頭文件。對於一個具體的源程序,如果我們正確的包含了頭文件,那麼剩下的就是程序本生語法方面應該注意的事項。

如何確定系統調用函數包含在那個頭文件中呢?這在Unix/Linux系統下並非一件難事。Unix/Linux下命令man可以幫助我們找到。man命令不僅可以幫助我們查找一般命令的用法,同時提供不同層次的幫助諸如系統調用或者管理員級別的命令等等(譬如FreeBSD6.1中,man 1是用戶專用手冊,man 2是系統調用,man 3是庫函數查詢等等)。

下面我們就以APUE書中程序1-1 (實現ls命令部分功能)爲例,來說明如何將書中的程序改編成全部使用標準頭文件的程序。其中,操作系統用的是FreeBSD6.1,經過相應的修改可以在書中所說的幾個Unix系統及Linux系統中運行,我也曾在Debian Linux下成功編譯和運行該程序。書中1-1.c的原始代碼如下:

#include
#include
#include "ourhdr.h"

int
main(int argc, char *argv[])
{
DIR *dp;
struct dirent *dirp;

if (argc != 2)
err_quit("usage: ls directory_name");

if ((dp = opendir(argv[1])) == NULL)
err_sys("can't open %s", argv[1]);
while ((dirp = readdir(dp)) != NULL)
printf("%s/n", dirp->d_name);

closedir(dp);
exit(0);
}

從書後面的附錄中可以看到"ourhdr.h"的內容比較多,包含了比較多的常用頭文件,一些宏定義和一些常用函數和出錯函數的定義。其實,對於每一個具體的程序,我們只需要找到該程序中用到的頭文件即可。

該1-1.c中所用到的系統函數調用有:opnedir(),readdir(),printf(),closedir()和exit()。
其中,對於常用的函數prinft()和exit(),它們所在的頭文件一般都知道,分別是和。而對於
opnedir (),readdir()和closedir(),我們可以通過man opendir,man readdir,man closedir得到這三個關於目錄操作的函數所在的頭文件都是:和。這兩個頭 文件在源程序中也已經列出。

其次,1-1.c中還用到了作者自定義的兩個函數:err_quit()和err_sys()。這兩個函數主要使用來進行出錯處理的。當然,使用這兩個函數對錯誤信息的處理是比較完善的。但是,作爲我們學習來講,瞭解程序的核心功能是首要的,我們可以將出錯處理簡化一點,即當遇到錯誤的時候,我們只簡單的使用printf()函數來提示一下有錯誤發生。當然,用printf()來進行出錯處理並不是一種很合理的方法,而且往往我們看不到更關鍵的錯誤信息,但對於我們僅僅作爲學習來用還是可以接受的。畢竟我們要理解的核心部分是程序的功能實現,出錯處理在於其次。

通過以上的說明,我們可以將1-1.c修改爲如下內容:

#include
#include
#include
#include
int main(int argc, char* argv[])
{
DIR *dp;
struct dirent *dirp;

if(argc != 2)
{
printf("You need input the directory name./n");
exit(1);
}

if((dp = opendir(argv[1])) == NULL)
{
printf("cannot open %s/n", argv[1]);
exit(1);

}

while ((dirp = readdir(dp)) != NULL)
printf("%s/n", dirp->d_name);


closedir(dp);

exit(0);
}

這樣修改後的程序已經與作者的頭文件"ourhdr.h"沒有關係,可以單獨的進行編譯。我使用的是root用戶,執行命令:

# gcc 1-1.c //生成目標文件a.out
或者
# gcc -o 1-1 1-1.c //生成目標文件1-1

沒有任何錯誤和警告,說明編譯成功。這時我們執行生成的目標文件:

# ./a.out /home
或者
# ./1-1 /home

則會列出/home路徑下的所有文件,包括目錄(.)和(..)。

通過這樣的方法,基本上我們可以將該書中所有的例程修改成不包含"ourhdr.h"的程序。這樣,我們就可以單獨的編譯每一個例程,而不用顧及作者所給的雜湊的頭文件。同時這種比較笨的方法,反而有利於幫助我們瞭解不同系統調用所對應的頭文件,對於學習來說,這應該是一件好事。

現在,我也才學到APUE的第四章了。前四章的程序,我都是採用這種方法進行編譯和運行。如果也有在學習APUE的朋友,我們可以一起交流。

APUE2作者提供的源碼編譯方法及單個源碼編譯的實現
按照源代碼文件夾中的README的步驟,對整個源代碼進行了編譯。

(一)作者提供的編譯方法的實現

README文件中給出的編譯方法如下:

To build the source, edit the Make.defines.* file for your system and set WKDIR to the pathname of the tree containing the source code. Then just run "make". It should figure out the system type and build the source for that platform utomatically.

參照該方法,我將源碼的編譯分爲三步。整個步驟都是在root超級用戶下進行的,如果其他用戶沒有權限進行編譯,可以通過su命令切換到超級用戶。

第一步,編輯Make.defines.*文件。由於我所使用的操作系統是FreeBSD6.1,所以應該編輯文件Make.defines.freebsd。其實,編輯該文件的內容主要是修改其中的WKDIR,即我們源碼所在文件夾的絕對路徑名。原文件中WKDIR/home/sar/apue.2e,我們可以根據我們實際文件夾所在的位置進行相應的修改。我的apue.2e文件夾直接放在/home下了,所以我將WKDIR修改爲WKDIR/home/apue.2e。其餘內容不用修改,保存修改後的文件。

第二步,修改腳本文件systype.sh的權限。由於原始的systype.sh文件不具有可執行的權限。通過執行命令:

#chmod +x systype.sh

給當前用戶及其所在組和其他組添加可執行權限;

或者

#chmod u+x systype.sh

僅給當前用戶添加可執行權限。

該腳本文件的功能主要是檢測操作系統的類型。它可以檢測到系統的類型有:FreeBSDLinuxMacOSSolaris等。如果單獨執行這個shell腳本:

./systype.sh

則輸出結果爲:freebsd。即檢測本機的操作系統爲FreeBSD

第三步,進行源碼的編譯。在命令行下執行make命令。通過查看Makefile文件可知,make之後,首先執行systype.sh腳本,即首先確定操作系統的類型,然後在進行源碼的編譯。在編譯的過程中,會有一些Warning。這些都是正常的,導致警告的原因可能是採用編譯起的版本不同或者是同一類型操作系統的版本不同。但是,只要make的過程不出現error,就會順利的生成可執行文件。我的在編譯過程中沒有出現error,因此意味着編譯成功。

注意:編譯的過程中可能會出現的一個問題,也是一個網友曾經問到的問題,就是在編譯中出現這個的錯誤,提示nawk command cannot be found。這個問題可能的原因是,有些操作系統的內核版本較低,可能還不支持nawk(new awk)這個命令。但應該支持awk命令。因此,問題的解決方法就是將相關文件中的nawk命令替換爲awk,或者爲系統添加一個別名aliasalias nawk awk。這樣在編一的過程中,遇到nawk命令時,實際會去執行awk命令。如果還有其他問題,可以去網上搜索相關的解決方法。因爲我在編譯的過程中沒有遇到這樣的問題,不再一一贅述。

(二)編譯生成可執行文件的位置

在路徑/home/apue.2e/下雖然有所有的源文件,都是以figx.x的形式命名。但是實際編譯的過程並不是編譯的這些文件。而是編譯在該路徑下各個文件夾中的後綴名爲*.c的程序。作者把同一章節或者相近幾個章節的源代碼放在某一個文件夾下面(includelib文件夾除外)。而文件夾的命名一般是和該章對應的標題是一致的,採用的是英文標題的全稱或簡寫。譬如,advio文件夾對應Chapter 14. Advanced I/O,該章的代碼就放在該文件夾下面。還有文件夾proc對應Chapter 8. Process Control,文件夾termios對應Chapter 18. Terminal I/O等等,基本上每一章的代碼都可以在這些文件夾中找到。

(三)如何編譯單獨的源文件

通過make命令是直接將所有的源程序編譯成可執行文件的。對於喜歡修改和調試程序的朋友來說,make生成的可執行文件顯然不具有這樣的功能,而且,也不可能修改了一個源文件,然後還要make。如何需要編譯和調試單個程序的話,方法如下:

1.首先還是要用make對所有文件進行編譯。成功編譯後,會在WKDIRlib/下生成庫文件libapue.a,主要是將apue.h(位於WKDIRinclude/)中定義的所有內容生成一個靜態的庫,這樣可以方便調用。

2.我們以WKDIR/下的fig1.3(實現ls部分功能)文件爲例說明需要修改的地方。將fig1.3文件重命名爲fig1.3.c,然後編輯該文件,將包含頭文件的一行代碼:

#include "apue.h" //默認所引用頭文件的位置爲當前的路徑WKDIR=/home/apue.2e

修改爲

#include "include/apue.h"

即頭文件apue.h的位置爲當前路徑下inlucde文件夾中,這個就正確的指定了apue.h的位置。

這樣就可以進行編譯了,但在編譯的時候還要加上庫文件libapue.a,因爲該文件實現了apue.h中的所有功能,主要有常用頭文件,宏定義以及自定義函數的實現。

輸入命令

#gcc fig1.3.c lib/libapue.a

則會生成可執行文件a.out。執行命令

#./a.out /home

則列出我的/home路徑下的所有文件和文件夾:

.

..

david

simsun .ttc

simkai.ttf

simsun.ttf

MYKERNEL

unix_advance_program

freebsd

APUE Source Code

LumaQQ

apue.2e

bash-script

lumaqq_2005_patch_2006.01.22.15.00.zip

lumaqq_2005-linux_gtk2_x86_with_jre.tar

apue_src_complied.tar

當然,如果需要編譯的是各個文件夾中的一個源程序時,則只需對所包含的頭文件apue.h的路徑作相對修改,改爲

#include "../include/apue.h"

以及編譯是庫文件的位置也相應修改,改爲:

#gcc sourcefile.c ../lib/libapue.a

至此,APUE第二版作者提供的源碼編譯方法和單獨源碼的編譯都已經實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章