版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
1、1 Linux程序設(shè)計學(xué)習(xí)筆記01入門什么是 Linux Linux 是一個類 UNIX 內(nèi)核的可以自由發(fā)布的實現(xiàn)版本,是一個操作系統(tǒng)的底層核心。幾乎所有為 UNIX 編寫的程序都可以在 Linux 上編譯運行。 Linux 是由赫爾辛基大學(xué)的 Linus Torvalds 開發(fā)的,期間得到了因特網(wǎng)上廣大 UNIX 程序員的幫助。它最初只是受 Andy Tanenbaum 教授的 Minix (一個小型類 UNIX 系統(tǒng))啟發(fā)而開發(fā)的個人愛好的程序,但后來逐步發(fā)展成為一個擁有自己版權(quán)的完整系統(tǒng)。其目的是,保證 Linux 除包含自由發(fā)布的代碼外,不會集成任何私有代碼 。 GNU 項目和自由軟件
2、基金會 Linux 社團支持自由軟件的概念,即軟件本身不應(yīng)受限,它們應(yīng)該遵守 GNU 通用公共許可證( GPL )。雖然獲得軟件可能要支付一定的費用,但此后就可以隨意使用,并且它們通常是以源代碼的形式發(fā)布的。 在 GPL 規(guī)則限制下,所有基于這種概念開發(fā)的軟件都應(yīng)遵循 GPL 。大家可以在 上找到更多關(guān)于自由軟件的概念。 Linux 程序 Linux 應(yīng)用程序表現(xiàn)為兩種特殊類型的文件:可執(zhí)行文件和腳本文件。可執(zhí)行文件 是計算機可以直接運行的程序,它們相當(dāng)于 Windows 中的 exe 文件。腳本文件 是一組指令的集合,這些指令將由另一個程序(解釋器,比如
3、shell 或者 perl )來執(zhí)行,它們相當(dāng)于 Windows 中的 bat 文件、 cmd 文件或解釋執(zhí)行的 BASIC 程序。 與 Windows 相比, Linux 程序并不要求可執(zhí)行程序或腳本具有特殊的文件名或擴展名。當(dāng)?shù)卿?Linux 系統(tǒng)時,我們與一個 shell 程序(通常是 bash )進行交互,它像 Windows 中的命令提示窗口一樣運行程序。在當(dāng)前環(huán)境下,必定有一組環(huán)境變量與之匹配,其中 PATH 變量 指明了當(dāng)前可以自動搜索的目錄:當(dāng)需要執(zhí)行的程序在 PATH 指定的目錄中時,你將不需指明待執(zhí)行程序的全路徑(除非有同名程序存在);否則必須指定需要執(zhí)行程序的路徑(相對路
4、徑或者絕對路徑)。 如果你還是 Linux 環(huán)境下的初學(xué)者,一定要注意 Linux 使用正斜線( / ) 分割文件名里的目錄名,而不是像 Windows 那樣使用反斜線( )。 建議 :如果你剛剛接觸 Linux ,請先學(xué)會使用 Linux 再來學(xué)習(xí)如何在 Linux 環(huán)境下編程。 C 語言編譯器 這里我們使用 GNU C 編譯器,簡稱為 gcc 。因為它隨 Linux 的發(fā)行版一起提供,并且它支持 ANSI C 的標(biāo)準(zhǔn)語法。在 上可以獲取 gcc 軟件包。 建議 :如果你是初學(xué) C 語言,請編寫經(jīng)典的 HelloWorld 程序來開始你的學(xué)習(xí)之旅。 開發(fā)系
5、統(tǒng)導(dǎo)引 對 Linux 開發(fā)人員來說,了解軟件工具和開發(fā)資源在系統(tǒng)中存放的位置是很重要的。 應(yīng)用程序 應(yīng)用程序通常存放在系統(tǒng)為之保留的特定目錄中。系統(tǒng)為正常使用提供的程序,包括用于程序開發(fā)的工具,都可以在目錄 /usr/bin 中找到;系統(tǒng)管理員為某個特定的主機或本地網(wǎng)絡(luò)添加的程序通常可在目錄 /usr/local/bin 或 /opt 中找到。 建議 :在 /usr/local 目錄結(jié)構(gòu)下編譯、運行自己的程序,并訪問必須的文件。 頭文件 對 C 語言來說,頭文件幾乎總是在 /usr/include 目錄及其子目錄下。那些依賴于特定 Linux 版本的頭文件通??梢栽谀夸?/usr/inclu
6、de/sys 和 /usr/include/linux 中找到。 在調(diào)用 C 語言編譯器時,我們可以使用 -I 標(biāo)志來包含保存在子目錄或非標(biāo)準(zhǔn)位置中的頭文件,比如: $ gcc I/usr/openwin/include fred.c 。它指示編譯器不僅在標(biāo)準(zhǔn)位置,也在 /usr/openwin/include 目錄中查找 fred.c 中包含的頭文件。 提示 :可以使用 grep 命令來搜索包含某些特定定義和函數(shù)原型的頭文件。 庫文件 庫是一組預(yù)先編譯好的函數(shù)的集合,這些函數(shù)都是按照可以重用的原則編寫的。標(biāo)準(zhǔn)庫文件一般存儲在 /lib 和 /usr/lib 目錄中。 庫文件的名字總是以 li
7、b 開頭,隨后的部分指明這是什么庫(比如, libm 就代表了數(shù)學(xué)庫)。文件名的最后部分以 . 開始,然后給出庫文件的類型: .a 代表傳統(tǒng)的靜態(tài)函數(shù)庫; .so 代表共享函數(shù)庫。 提示 :如何編寫及使用兩種函數(shù)庫可以參看精通 UNIX 下 C 語言編程及項目實踐的 學(xué)習(xí)筆記 12 Linux程序設(shè)計學(xué)習(xí)筆記02Shell程序設(shè)計管道和重定向 重定向分為輸入重定向 < 、輸出重定向 > 和附加輸出重定向 >> 。 提示 :默認(rèn)情況下,如果使用 > 操作符把輸出重定向到一個文件而該文件已經(jīng)存在時,它的內(nèi)容將被覆蓋;如果想改變該默認(rèn)行為,可以使用 set C 命令設(shè)置
8、 noclobber 選項。 提示 :可以使用 UNIX 的通用“回收站” /dev/null 來有效的丟棄輸出信息,比如: $ kill l 1234 >/dev/null 2>&1 我們可以使用管道操作符 | 來連接進程。 Linux 和 MS-DOS 不同,在 Linux 下通過管道連接的進程可以同時運行,并且隨著數(shù)據(jù)流在它們之間的傳遞可以自動地進行協(xié)調(diào)。舉個例子: $ grep l POSIX* | more 它將輸出包含 POSIX 字符串的文件名。實際上,上述命令還有另外兩種編寫方式: $ more grep l POSIX* 或者 $ more $(grep
9、l POSIX*) Shell 腳本程序 Shell 腳本非常靈活,在 Linux 的使用中往往經(jīng)常使用它來完成某些繁瑣但又必須的工作。鑒于本筆記主要針對 Linux 下的 C 語言編程,這里并不會用太多的篇幅來介紹 Shell 腳本。 運行腳本程序有兩個辦法:一種是在命令行上直接輸入命令 PATH=$PATH:. 或者編輯 .bash_profile 文件,將剛才的命令添加在文件的末尾,然后退出登錄再重新登錄進來;另一種就是在保存腳本程序的目錄中先鍵入 ./ 再輸入腳本命令,如此做的作用是把腳本程序的完整的相對路徑告訴 Shell 。 建議 :考慮到系統(tǒng)的安全性,最好的辦法是在當(dāng)前目錄中的所
10、有命令前都加上一個 ./ 。 推薦 :如果你想深入地學(xué)習(xí)使用 Shell ,可以去 ChinaUnix 的 Shell 子論壇 。 Find 命令 只要你是在 UNIX 或者類 UNIX 環(huán)境下,一個必不可少的命令就是 find 命令。它的功能是查找文件,比如說,我們在編譯某個 C 程序時,發(fā)現(xiàn)錯誤提示說它不識別 STDIN ;那么,很明顯的,你的 C 程序缺少某個頭文件,但是如何才能知道它是在哪個頭文件中定義的呢?一個非常有效地辦法就是結(jié)合 grep 使用 find 命令: $ find /usr/include name “*.h” | xargs grep “STDIN” 上面命令先在
11、/usr/include 目錄下搜索所有包含了 .h 的文件,繼而使用 grep 命令對這些頭文件查找 STDIN 字符串。當(dāng)然,你可能說我知道 STDIN 定義在 <stdio.h> ,不用這么麻煩!但在實際的研發(fā)工作中,我們經(jīng)常會在某個并不熟悉的環(huán)境(比如設(shè)備驅(qū)動程序的開發(fā))下編寫程序,我們會不可避免的查找某個函數(shù)或者宏定義的來源。 提示: find 命令是一個非常復(fù)雜的命令。它有很多的選項,如果你想深入的了解它,一個有效地途徑就是使用 man 文檔。很多情況下,比如不熟悉的命令或者函數(shù)都可以從 man 文檔中得到解答。 3 Linux程序設(shè)計學(xué)習(xí)筆記03文件操作在 Linux
12、 中,一切(或幾乎一切)都是文件。 文件和設(shè)備 硬件設(shè)備在 Linux 操作系統(tǒng)中通常被映射為文件??梢允褂?mount 命令加載 CD-ROM 、 Windows 下的文件系統(tǒng)或者其他的設(shè)備。 UNIX 和 Linux 中比較重要的設(shè)備文件有三個: 1. dev/console 該設(shè)備代表系統(tǒng)控制臺,錯誤信息和診斷信息通常會被發(fā)送到這個設(shè)備上。在現(xiàn)代的工作站和 Linux 上,它通常是“活躍”的虛擬控制臺;而在 X 窗口系統(tǒng)中,它會是屏幕上一個特殊的控制臺窗口。 2. dev/tty 如果一個進程有控制終端的話,那么特殊文件 /dev/tty 就是這個控制終端(鍵盤、顯示屏或者窗口)的別名(
13、邏輯設(shè)備)。 注意 :雖然 /dev/console 設(shè)備只有一個,但通過 /dev/tty 卻能夠訪問許多不同的物理設(shè)備。 3. /dev/null 這是空設(shè)備,所有寫向該設(shè)備的輸出都將被丟棄。 提示 :還有一個特殊設(shè)備 /dev/zero 經(jīng)常被用到,它的作用是以內(nèi)容為 null 字節(jié)的源文件來來創(chuàng)建零長度文件。它經(jīng)常用在 dd 命令的 if 參數(shù)中。 掃描目錄 與目錄操作相關(guān)的函數(shù)在 dirent.h 頭文件中聲明。其中,目錄的讀寫函數(shù)在 UNIX 環(huán)境下 C 語言編程及項目實踐的 讀書筆記 3 中有過介紹。除了 opendir 、 closedir 和 readdir 這三個常見的函數(shù)
14、外,還有兩個函數(shù): telldir 和 seekdir 。 telldir 函數(shù)的返回值記錄了一個目錄流里的當(dāng)前位置;接著,我們可以在隨后的 seekdir 調(diào)用中利用這個值將目錄指針重置到之前的位置。 在 Linux 系統(tǒng)上一個常見的問題就是對目錄進行掃描,也就是確定一個特定目錄下存放的文件。在上述提到的讀書筆記中曾給出了一個掃描實例,但是該實例只是掃描了當(dāng)前目錄下的文件,并沒有深入到子目錄的層面。這里給出一個掃描當(dāng)前目錄下(包含子目錄)所有文件的實例 printdir /* * function: print all files and directories under 'dir
15、' directory * paremeters: * dir in point to the directory needed to print * depth in width align to left, incrementing while nested */ void printdir(char *dir, int depth) DIR *dp; struct dirent *entry; struct stat statbuf; if(dp = opendir(dir) = NULL) fprintf(stderr, "Can't open dirento
16、ry: %sn", dir); return; chdir(dir); while(entry = readdir(dp) != NULL) lstat(entry->d_name, &statbuf); if(S_ISDIR(statbuf.st_mode) if(strcmp(".", entry->d_name)=0 | strcmp(".", entry->d_name)=0) continue; printf("%*s%s/n", depth, "", entry-&g
17、t;d_name); printdir(entry->d_name, depth+4); else printf("%*s%sn", depth, "", entry->d_name); chdir("."); closedir(dp); 提示 :為了在輸出時對于不同層次的目錄有縮進,這里使用了可變字段寬度 %*s 。其中 * 可以由一個整形值來指定,代表了在輸出后面字符串時所要求的寬度。 /proc 文件系統(tǒng) Linux 提供了一個特殊的文件系統(tǒng) procfs ,它通常表現(xiàn)為 /proc 目錄。該目錄中包含了許多特殊文件
18、以允許對驅(qū)動和內(nèi)核信息進行高層訪問。只要應(yīng)用程序有正確的訪問權(quán)限,它們就可以通過讀寫這些文件來獲得信息或設(shè)置參數(shù)。 /proc 目錄中的文件會隨系統(tǒng)的不同而不同,當(dāng) Linux 版本中有更多的驅(qū)動和設(shè)施支持 procfs 文件系統(tǒng)時,該目錄中就會包含更多的文件。不過,該目錄下有許多東西是在任何 Linux 系統(tǒng)中都存在的。 大多情況下,只需要直接讀取這些文件 就可以獲取信息。比如 /proc/cpuinfo 、 /proc/meminfo 、 /proc/version 和 /proc/net/sockstat 就分別給出了 CPU 、內(nèi)存、 Linux 版本和網(wǎng)絡(luò)套接字的信息。 其實, /p
19、roc 目錄下的有些文件不但可以讀取,還可以修改。比如說,系統(tǒng)中所有運行的程序同時能夠打開的文件總數(shù)是 Linux 內(nèi)核的一個參數(shù)。它的值可以從 /proc/sys/fs/file-max 文件得到;同樣地,你也可以直接修改該文件來更改可以直接打開的文件總數(shù)。 提示 :對 /proc 目錄中文件進行寫操作需要超級用戶的權(quán)限。在修改數(shù)據(jù)時一定要小心,寫入不當(dāng)?shù)闹岛芸赡軙?dǎo)致嚴(yán)重的后果。 /proc 目錄中還有一類文件以數(shù)字命名 。比如,當(dāng)我們使用 ps 命令查看當(dāng)前正在運行的進程時,會顯示每個進程的 PID 。每個進程都會對應(yīng) /proc 目錄下一個以該 pid 值命名的文件。如果你要查看該進程
20、的具體信息,可以直接讀取文件 /proc/(pid) 。在列出的文件中, cmdline 文件會顯示該進程由誰啟動的;你可以使用 cat 命令或者 od 命令來查看。4 Linux程序設(shè)計學(xué)習(xí)筆記04Linux環(huán)境當(dāng) Linux 編寫程序時,必須考慮到程序?qū)⒃谝粋€多任務(wù)環(huán)境中運行。這意味著在同一時間會有多個程序運行,它們共享內(nèi)存、磁盤空間和 CPU 周期等機器資源。甚至同一程序也會有多個實例同時運行。最重要的是,這些程序能夠互不干擾,了解他們的環(huán)境,并且能正確運行以避免沖突例如試圖與其他程序同時寫同一個文件。 程序參數(shù) 無論操作系統(tǒng)何時啟動新程序,參數(shù) argc 和 argv 都被設(shè)置并傳遞給
21、 main 。這些參數(shù)通常由其他程序提供,這個程序一般是 shell ,它請求操作系統(tǒng)啟動該新程序。 注意 : Linux 的 shell 一般會在設(shè)置 argc 和 argv 之前對文件名參數(shù)進行通配符擴展,而 MS-DOS 的 shell 則期望程序接受帶通配符的參數(shù)并執(zhí)行它們自己的通配符擴展。 命令行參數(shù) 在向程序傳遞信息方面是很有用的。許多工具程序都是有命令行參數(shù)來改變程序的行為或設(shè)置選項。這些參數(shù)大多以短橫線( - )開頭;不帶后續(xù)參數(shù)的選項還可在一個短橫線后歸并到一起。 當(dāng)需要在自己的程序中處理這些命令行參數(shù)時,我們需要自己對這些參數(shù)解析從而判斷出有效的參數(shù)。當(dāng)參數(shù)個數(shù)很多時,這一
22、過程將是非常繁瑣的。實際上, X/Open 規(guī)范定義了命令行選項的標(biāo)準(zhǔn)用法,同時定義了在 C 語言程序中提供命令行開關(guān)的標(biāo)準(zhǔn)編程接口: getopt 函數(shù)。 getopt 函數(shù) 將傳遞給 main 程序的 argc 和 argv 作為參數(shù),同時接受一個選項指定字符串 optstring ,該字符串高速 getopt 哪些選項可用,以及每個選項是否有關(guān)聯(lián)值。程序?qū)⒀h(huán)調(diào)用 getopt 對選項參數(shù)進行處理,直到 getopt 返回 -1 時處理完畢。關(guān)于 getopt 的詳細(xì)信息請查閱 man 手冊。 我們在實際使用中可能會選擇長參數(shù),它以雙短橫線( - )表示。 GNU C 庫包含了 geto
23、pt 的另一個版本,稱為 getopt_long ,它能同時接受短參數(shù)和長參數(shù)。 getopt_long 函數(shù) 比 getopt 函數(shù)多了兩個參數(shù),一個被定義為 option 結(jié)構(gòu)(它指定了函數(shù)可以接受的長參數(shù)和函數(shù)對應(yīng)返回的值),另一個通常被置 NULL 。 提示 :在使用 getopt_long 函數(shù)時,除了要包含頭文件 getopt.h 外,還需要把常量 _GNU_SOURCE 一同包含進來。 無參數(shù)和 void 參數(shù) 在定義自己的程序時,當(dāng)不需要傳遞參數(shù)時我們可能置參數(shù)列表為空或者填入 void 。那么這兩種方式相同么?考慮下面兩個函數(shù) void foo1(); void foo2(v
24、oid); 當(dāng)我們使用 foo1(1,2) 調(diào)用 foo1 函數(shù)時編譯器將不報任何錯誤,同時可以正常運行;它的執(zhí)行結(jié)果與調(diào)用 foo1() 的結(jié)果一致。然而,當(dāng)我們使用 foo2(1,2) 調(diào)用 foo2 函數(shù)時編譯器將報錯。因此,無參數(shù)和 void 參數(shù)使得函數(shù)顯現(xiàn)出兩種不同的行為。 實際上, void 參數(shù)指定函數(shù)在調(diào)用時不能有任何參數(shù);而無參數(shù)則沒有對參數(shù)的傳遞做任何的規(guī)范,也就是說,你可以傳遞任何類型的任意個數(shù)的參數(shù)。 環(huán)境變量 環(huán)境變量是一把雙刃劍,使用它的時候要小心!與命令行選項相比,它們對用戶來說更加“隱蔽”,這樣就使得調(diào)試變得更加困難。從某種意義上來說,環(huán)境變量就像全局變量一樣
25、,它們會改變程序的行為,產(chǎn)生不可預(yù)期的結(jié)果。 對于環(huán)境環(huán)境變量的讀寫操作可以有兩種方式,在 Unix 環(huán)境下 C 語言編程及項目實踐的 讀書筆記 4 中有詳細(xì)的介紹。 時間和日期 通常能確定時間和日期對一個程序來說是非常有用的。 我們可以使用 time 函數(shù)獲取一個 time_t 類型的時間值,該值是從格林威治時間到當(dāng)前時間點的秒數(shù)。函數(shù) localtime 將一個 time_t 類型的時間值轉(zhuǎn)換為 tm 結(jié)構(gòu),通過該結(jié)構(gòu)可以清晰得了解當(dāng)前的年、月、日、時、分等。函數(shù) mktime 的功能則相反,它將一個 tm 結(jié)構(gòu)的時間值轉(zhuǎn)換為 time_t 類型。上面的幾個函數(shù)在精通 Unix 下 C 語
26、言編程及項目實踐的 讀書筆記 5 中有詳細(xì)的介紹。 為了得到更“友好”的時間和日期表示,像 date 命令輸出的那樣,我們可以使用 asctime 函數(shù)和 ctime 函數(shù)。實際上,為了對時間和日期字符串的格式有更多的控制, Linux 和現(xiàn)代的類 UNIX 系統(tǒng)提供了 strftime 和 strptime 函數(shù)。它很像是一個針對時間和日期的 sprintf 和 sscanf 函數(shù),具體的信息請查看 man 手冊。 注意 :編譯包含了 strptime 函數(shù)的程序時,需要在包含 time.h 頭文件的語句之前包含 _XOPEN_SOURCE 宏定義。 提示 :我們在程序中經(jīng)常使用 sleep
27、 函數(shù)來完成指定時間的睡眠。實際上, sleep 函數(shù)只能指定秒級的時間,如果要精確到微妙級 (10 -6 s) 可以使用 usleep 函數(shù) 。除此之外,還有一個 timeval 結(jié)構(gòu) 可以完成微妙級的操作。 臨時文件 很多情況下,程序會利用一些文件形式的臨時存儲手段。這些臨時文件可能保存著一個計算的中間結(jié)果,也可能是關(guān)鍵操作的文件備份。 臨時文件的用法很常見,但必須確保應(yīng)用程序為臨時文件選取的文件名是唯一的。 GNU C 提供了兩個函數(shù) tmpname 和 tmpfile 來創(chuàng)建臨時文件。詳細(xì)情況請參考 man 手冊。 提示 :當(dāng)需要創(chuàng)建臨時文件且需要對其進行讀寫時,請優(yōu)先考慮使用 tmp
28、file 函數(shù)。該函數(shù)同時創(chuàng)建和打開臨時文件,這樣就避免了使用 tmpnam 函數(shù)時可能有另一個程序用同樣的名字打開文件的風(fēng)險。 用戶及主機信息 程序能夠通過檢查環(huán)境變量和讀取系統(tǒng)時鐘來在很大程度上了解它所處的運行環(huán)境。除此之外,程序還可以發(fā)現(xiàn)它的使用者的相關(guān)信息。 函數(shù) getuid 和 getlogin 分別獲取程序關(guān)聯(lián)的 UID 和與該關(guān)聯(lián) ID 相應(yīng)的登錄名。 實際上,系統(tǒng)文件 /etc/passwd 包含了一個用戶賬戶數(shù)據(jù)庫。要獲取某個用戶的信息, UNIX 系統(tǒng)并不推薦直接對該系統(tǒng)文件讀寫,它定義了一組函數(shù)來提供一個標(biāo)準(zhǔn)二又有效地獲取用戶信息的編程接口 getpwuid 和 get
29、pwnam 。它們均返回一個指向與某個用戶對應(yīng)的 passwd 結(jié)構(gòu)指針。 提示 :你可以對程序進行設(shè)置,讓它們的運行看上去好像是由另一個用戶啟動的。當(dāng)一個程序的 SUID 權(quán)限被置位時,它的運行就好像是由該可執(zhí)行文件的屬主啟動的。一個典型的例子是 su 命令。 在 UNIX 環(huán)境下,可以使用 uname 系統(tǒng)調(diào)用來獲取主機信息。它將主機信息寫入一個 utsname 結(jié)構(gòu)。詳細(xì)細(xì)節(jié)請查閱 man 手冊。 日志 許多應(yīng)用程序需要記錄它們的活動。系統(tǒng)程序經(jīng)常需要向控制臺或日志文件寫消息。這些消息能指示錯誤、警告或是與系統(tǒng)狀態(tài)有關(guān)的一般信息。 提示 :當(dāng)程序短小時我們經(jīng)常使用 gdb 工具來調(diào)試;但
30、當(dāng)程序比較龐大時,一個有效地調(diào)試手段就是使用日志功能,在日志中搜索出錯信息。 UNIX 規(guī)范為所有程序提供了一個接口,通過 syslog 函數(shù)來產(chǎn)生日志信息。這些信息往往被記錄在日志文件 /var/log/message 中,部分調(diào)試信息可能記錄在 /var/log/debug 中。 通過 syslog 函數(shù)向系統(tǒng)的日志文件發(fā)送的每條日志信息都有一個優(yōu)先級。而且不同程序?qū)懭氲娜罩拘畔⒉⒉荒苊黠@地區(qū)分開來。實際上, UNIX 提供了幾個函數(shù)來改變?nèi)罩居涗浶袨椋?openlog 、 closelog 和 setlogmask 函數(shù)。有效地使用這三個函數(shù)可以在日志文件中與其他程序?qū)懭氲娜罩緟^(qū)分開。關(guān)
31、于詳細(xì)信息請查閱 man 手冊。 5 Linux程序設(shè)計學(xué)習(xí)筆記05終端對終端進行讀寫 在編寫程序時,我們往往需要從終端讀入數(shù)據(jù)。一種情況是需要連續(xù)地讀入用戶鍵入的選擇項,這往往出現(xiàn)在數(shù)據(jù)庫程序中。程序員往往會使用 getchar 函數(shù)來讀取數(shù)據(jù),繼而判斷輸入的數(shù)據(jù)是否有效,從而做出反應(yīng)。其實如此做帶有很大的風(fēng)險,一個實例程序如下 #include <stdio.h> char *menu = "a - add new record", "d - delete record", "q - quit", NULL ; int
32、 getchoice(char *choices) int chosen = 0; int selected; char *option; do option = choices; while(*option) printf("%sn", *option); option+; selected = getchar(); option = choices; while(*option) if(option0 = selected) chosen = 1; break; option+; if(!chosen) printf("Incorrect choice, se
33、lect again.n"); while(!chosen); return selected; int main() int choice = 0; do choice = getchoice(menu); printf("You choose %cn", choice); while('q' != choice); exit(0); 實例程序中,用戶需要鍵入“ A/ 回車 /Q/ 回車”才能做出選擇。但這種處理有著很大的風(fēng)險,讀者可以自己測試一下。這也是初學(xué)者經(jīng)常碰到的問題。 默認(rèn)情況下,只有當(dāng)用戶按下回車鍵后,程序才能讀到終端的輸入。這種處理
34、方式是規(guī)范模式或標(biāo)準(zhǔn)模式 。在這種模式下,所有的輸入都給予行進行處理,在一個輸入行完成前,終端接口負(fù)責(zé)管理所有的用戶鍵盤輸入,包括退格鍵,應(yīng)用程序讀不到用戶輸入的任何字符。 與標(biāo)準(zhǔn)模式相對的另一種模式為非標(biāo)準(zhǔn)模式 ,這種模式下,應(yīng)用程序?qū)τ脩糨斎胱址奶幚頁碛懈蟮目刂茩?quán)。 在上述程序中, Linux 會暫存用戶讀入的內(nèi)容,直到用戶按下回車鍵,然后將用戶選擇的字符及緊隨其后的回車符一起傳送到程序。所以,每當(dāng)你選擇一個菜單時,程序就調(diào)用 getchar 函數(shù)處理該字符,而當(dāng)程序在下一次循環(huán)再次調(diào)用 getchar 函數(shù)時,它會立刻返回一個回車符。一個解決方案是程序在每次讀入數(shù)據(jù)前首先清空回車鍵之
35、前的所有數(shù)據(jù),典型代碼如下: do selected = getchar(); while('n' != selected); 終端驅(qū)動程序和通用終端接口 有時,程序需要更加精細(xì)的終端控制能力,而不是僅通過簡單的文件操作來完成對終端的一些控制。 Linux 提供了一組編程接口,這使得我們能夠控制終端驅(qū)動程序的行為,從而允許我們對終端的輸入和輸出進行更好的控制。 有一組函數(shù)調(diào)用( GTI )用作控制終端,這組函數(shù)調(diào)用與用于讀寫數(shù)據(jù)的函數(shù)是分離的,這就使得讀寫數(shù)據(jù)的接口非常簡潔,同時又保證用戶可以對終端的行為進行更精細(xì)的控制。 termios 結(jié)構(gòu) 通過設(shè)置 termios 結(jié)構(gòu)中
36、的值和使用一組函數(shù)調(diào)用,我們就可以對終端接口進行控制。 提示 :使用 termios 結(jié)構(gòu)及相關(guān)的函數(shù)調(diào)用,需要包含 termios.h 頭文件;同時需要包含 curses 函數(shù)庫。 控制終端的操作模式有以下幾種:輸入模式、輸出模式、控制模式、本地模式和特殊的控制字符。具體操作由 tcgetattr 函數(shù)和 tcsetattr 函數(shù)來完成。其中,本地模式是最常用,也是最重要的一種操作模式。 注意 :程序要將終端設(shè)置恢復(fù)到程序開始運行之前的狀態(tài),這一點是非常重要的。首先保存這些值,然后在程序結(jié)束時恢復(fù)它們,這永遠(yuǎn)是程序的職責(zé)。 輸入模式控制輸入數(shù)據(jù)在被傳遞給程序之前的處理方式。通過設(shè)置 term
37、ios 結(jié)構(gòu)中的 c_iflag 成員的標(biāo)志對它們進行控制。 輸出模式控制輸出字符的處理方式,即由程序發(fā)送出去的字符在傳遞到串行口或屏幕之前是如何處理的。通過設(shè)置 termios 結(jié)構(gòu)中 c_oflag 成員的標(biāo)志對輸出模式進行控制。 控制模式控制終端的硬件特性。通過設(shè)置 termios 結(jié)構(gòu)中的 c_cflag 成員的標(biāo)志對控制模式進行配置。控制模式主要用于串行線連接調(diào)制解調(diào)器的情況。 本地模式控制終端的各種特性。通過設(shè)置 termios 結(jié)構(gòu)中的 c_lflag 成員的標(biāo)志對本地模式進行配置。其中最常用的兩個標(biāo)志是 ECHO 和 ICANON 。前者抑制鍵入字符的回顯,后者將終端在兩個截然
38、不同的接收字符處理模式之間進行切換。如果設(shè)置了 ICANON 標(biāo)志 ,就啟用標(biāo)準(zhǔn)輸入行處理模式,否則就啟動非標(biāo)準(zhǔn)模式。 當(dāng)用戶鍵入類似 Ctrl-C 這樣的組合鍵時,終端會采取一些特殊的處理方式。 termios 結(jié)構(gòu)中的 c_cc 數(shù)組成員將各種特殊的控制字符映射到對應(yīng)的支持函數(shù)。每個字符的位置是由一個宏定義的,但不限制這些字符必須是控制字符。 注意 :在兩種不同的模式(標(biāo)準(zhǔn)模式和非標(biāo)準(zhǔn)模式)下, c_cc 數(shù)組的下標(biāo)值有一部分是重疊的。出于這個原因,一定要注意不要將兩種模式各自的下標(biāo)值混淆。 可以通過 stty 命令 查詢及修改終端模式。 通過 termios 結(jié)構(gòu)我們還可以控制終端的傳入
39、和傳出的速度(波特率)。 終端的輸出 編寫能夠應(yīng)付連接到 UNIX 系統(tǒng)上的各種不同類型終端的程序看上去是一件非常讓人畏懼的事情。因為這樣的程序必須針對各種類型的終端編寫相應(yīng)的代碼。 termifo 軟件包的出現(xiàn)解決了這一問題。在絕大多數(shù)現(xiàn)代的 UNIX 系統(tǒng)上,這個軟件包和另一個軟件包 curses 集成在一起。 注意 :在 Linux 系統(tǒng)上,在使用 termifo 軟件包時可能需要包含 ncurses 庫;該庫實現(xiàn)了 curses 軟件包的所有功能。 termifo 的功能標(biāo)識由屬性描述,它們被保存在一組編譯好的 terminfo 文件中,而這些文件可以方便地在 /usr/lib/ter
40、info 或 /usr/share/terinfo 目錄下找到。例如, VT100 終端的定義就放在文件 /usr/share/terminfo/v/vt100 中。你可以使用 infocmp 程序輸出 terminfo 編譯數(shù)據(jù)項的可讀版本。 虛擬控制臺 在 Linux 的典型安裝中將配置 12 個虛擬控制臺。虛擬控制臺通過字符設(shè)備文件 /dev/ttyN 使用, tty 是 Teletype 的縮寫,而 N 代表一個數(shù)字,從 1 開始。 通過 who 和 ps 命令,可以查看目前登錄進系統(tǒng)的用戶,以及目前在使用的虛擬控制臺及其上運行的 shell 和程序。 Linux 系統(tǒng)一般在前六個虛擬
41、控制臺上運行一個 getty 進程,這樣用戶即可用同一個屏幕、鍵盤和鼠標(biāo)在六個不同的虛擬控制臺上登錄。可以通過組合鍵 Ctrl+Alt+F<N> 在這六個不同的虛擬控制臺之間進行切換。 如果 Linux 系統(tǒng)使用的是圖形登錄界面或者使用 startx 切入圖形界面, X 視窗系統(tǒng)將使用第一個未使用的控制臺,通常是 /dev/tty7 。 而偽終端 由字符設(shè)備文件 /dev/pty 使用,其中 pty 是 pseudo tty 的縮寫。它與 tty 終端的區(qū)別在于偽終端沒有對于的硬件設(shè)備。 運程登錄的終端 由字符設(shè)備文件 /dev/pts/N 使用。 6 Linux程序設(shè)計學(xué)習(xí)筆記0
42、6curses函數(shù)庫Curses 標(biāo)準(zhǔn)作為過渡,位于簡單的文本行程序和完全圖形化界面(一般也更難于編程)的 X 視窗系統(tǒng)程序(如 GTK/GNOME 和 Qt/KDE )之間。 Curses 函數(shù)庫的名稱來自它所提供的功能,它能夠優(yōu)化光標(biāo)的移動并減少需要對屏幕進行的刷新,因此它也減少了必須向字符終端發(fā)送的字符數(shù)目。 基本使用方法 Curses 例程工作在屏幕、窗口和子窗口上。所謂“屏幕”就是正在寫的設(shè)備(通常是終端屏幕,也有可能是 xterm 屏幕)。 Curses 函數(shù)庫使用兩個數(shù)據(jù)結(jié)構(gòu)來映射終端屏幕,它們是 stdscr 和 curscr 。其中 stdscr 數(shù)據(jù)結(jié)構(gòu)對應(yīng)的是“標(biāo)準(zhǔn)屏幕”
43、,它的工作原理和 stdio 函數(shù)庫中的標(biāo)準(zhǔn)輸出 stdout 非常相似,它是 curses 程序中的默認(rèn)輸出插口;而 curscr 數(shù)據(jù)結(jié)構(gòu)和 stdscr 相似,但它對應(yīng)的是當(dāng)前屏幕的樣子。 一個使用 curses 函數(shù)庫的典型例程如下: #include <unistd.h> #include <stdlib.h> #include <curses.h> int main() initscr(); move(5,15); printw("%s", "Hello World!"); refresh(); endwi
44、n(); exit(0); 當(dāng)對使用 curses 函數(shù)庫的程序進行編譯時,必須在程序中包含頭文件 curses.h ,它是需要在編譯命令行中用 -lcurses 選項對 curses 函數(shù)庫進行鏈接。 從上面的程序可以看到,所有 curses 程序必須以初始化函數(shù) initscr 開始,以函數(shù) endwin 結(jié)束。函數(shù) initscr 在一個程序中只能調(diào)用一次。 提示 :我們可以先調(diào)用 endwin 函數(shù)退出 curses ,然后通過調(diào)研 clrearok(strscr,1) 和 refresh 函數(shù)繼續(xù) curses 操作。這樣,實際上是首先讓 curses 忘記物理屏幕的樣子,然后強迫它
45、執(zhí)行一次完整的屏幕原文重現(xiàn)。 函數(shù) move 和 printw 的功能是移動光標(biāo)和在當(dāng)前位置上輸出文本。在調(diào)用 refresh 函數(shù)之前,輸出到 stdscr 上的內(nèi)容是不會顯示在屏幕上的。 refresh 函數(shù)的作用就是刷新物理屏幕。 當(dāng)需要在屏幕上顯示比較松散的多行文本時,典型方式就是通過 move 函數(shù)與 printw 函數(shù)的配合來完成。 簡單來說, Curses 函數(shù)庫有幾種函數(shù):屏幕輸出函數(shù)、輸入函數(shù)、清除函數(shù)和光標(biāo)移動函數(shù)。通過這幾種函數(shù)的配合,我們就可以實現(xiàn)一個簡單的全屏界面。 字符屬性 :每個 curses 字符都可以有特定的屬性,該屬性控制著該字符在屏幕上的顯示方式,前提是用
46、于顯示的硬件設(shè)備能夠支持要求的屬性。預(yù)定義的屬性有 A_BLINK 、 A_BOLD 、 A_DIM 、 A_REVERSE 、 A_STANDOUT 和 A_UNDERLINE 。相關(guān)函數(shù)有 attron 、 attroff 和 attrset 等。一個典型的使用片段如下: move(5,15); attron(A_BOLD); printw("%s", "Hello World!"); attroff(A_BOLD); refresh(); 鍵盤 : curses 函數(shù)庫還提供了控制鍵盤的簡單方法。通過調(diào)用兩個 echo 函數(shù),我們可以簡單地關(guān)閉或開
47、啟輸入字符的回顯功能。通過調(diào)用 break 函數(shù),可以將輸入模式設(shè)置為字符中止模式,在這種模式下,字符一經(jīng)鍵入立刻傳遞給程序,而不是像在行模式中那樣首先緩存字符,知道用戶按下回車鍵才將用戶輸入的字符傳遞給程序。通過調(diào)用兩個 raw 函數(shù)則可以關(guān)閉或開啟特殊字符的處理。 提示 : curses 環(huán)境下,輸入模式分行模式和字符中止模式。默認(rèn)輸入模式是行模式,當(dāng)用戶鍵入回車符時才會將輸入的數(shù)據(jù)傳遞給程序;而字符中止模式則當(dāng)字符一經(jīng)鍵入就傳遞給程序。 窗口與子窗口 Curses 函數(shù)庫在物理屏幕上能夠同時顯示多個不同尺寸的窗口。 在 curses 環(huán)境下,窗口由 WINDOW 數(shù)據(jù)結(jié)構(gòu)來表示。實際上,
48、標(biāo)準(zhǔn)屏幕 stdscr 只是 WINDOW 結(jié)構(gòu)的一個特例。下面是一個使用了窗口的例程 #include <unistd.h> #include <stdlib.h> #include <curses.h> int main() WINDOW *new_window; int x,y; char letter = 'a' initscr(); for(y=0;y<LINES-1;y+) for(x=0;x<COLS-1;x+) mvwaddch(stdscr, y, x, letter); if(+letter > '
49、;z') letter = 'a' refresh(); sleep(2); new_window = newwin(10,20,5,5); box(new_window, '|', '-'); mvwprintw(new_window, 2, 2, "%s", "Hello World"); wrefresh(new_window); sleep(2); mvwin(new_window, 10, 10); wrefresh(new_window); sleep(2); delwin(new_wi
50、ndow); endwin(); return 0; 上面的例程中,先在全屏幕上填滿字符,然后創(chuàng)建一個 10*20 的新窗口,繼而在新窗口上輸出“ Hello , World ”。 新窗口的建立是由 newwin 函數(shù)來實現(xiàn)的,它指定了新窗口的大小和位置。刪除一個窗口時則使用 delwin 函數(shù)。函數(shù) box 的作用在于使用特殊的字符來界定新窗口。 用于窗口的通用函數(shù)有幾類:前綴 w 用于窗口、前綴 mv 用于光標(biāo)移動、前綴 mvw 用于在制定窗口中移動光標(biāo)。 wrefresh 函數(shù)用于刷新窗口。而 mvwin 函數(shù)的作用是移動指定的窗口到指定的位置;如果移動后窗口超出屏幕范圍, mvwin
51、函數(shù)調(diào)用將會失敗。 子窗口 是多窗口的一種特例,我們使用 subwin 函數(shù)和 delwin 函數(shù)創(chuàng)建和刪除子窗口。與前面提到的新窗口相比,子窗口沒有自己獨立的屏幕字符存儲空間,它們與它們的父窗口(在調(diào)用 subwin 時指定)共享同一字符存儲空間。這意味著,對子窗口中內(nèi)容的任何修改都會反映到它的父窗口中,所以刪除子窗口時,屏幕不會發(fā)生任何變化。 子窗口主要的用途是提供了一種簡潔的方式來卷動另一窗口里的部分內(nèi)容。在編寫 curses 程序時,經(jīng)常需要卷動屏幕的某個小區(qū)域,將這個小區(qū)域定義為一個子窗口,然后對其卷動,就能達(dá)到我們想要的效果。 注意 :使用子窗口有個強加的限制:在應(yīng)用程序刷新屏幕之
52、前必須先對其父窗口調(diào)用 touchwin 函數(shù)。 keypad 模式 curses 函數(shù)庫提供了一個精巧的用于管理功能鍵的功能。對每個終端來說,它的每個功能鍵所對應(yīng)的轉(zhuǎn)義序列都被保存,通常是保存在一個 terminfo 結(jié)構(gòu)中,而頭文件 curses.h 通過一組以 KEY_ 為前綴的定義來管理邏輯鍵。 curses 在啟動時會關(guān)閉轉(zhuǎn)義序列與邏輯鍵之間的轉(zhuǎn)換功能,這功能需要通過調(diào)用 keypad 函數(shù)來啟用。 實際上,使用 keypad 模式還是有一定的限制的: 1 )識別 escape 轉(zhuǎn)義序列的過程是與時間相關(guān)的。在處理許多網(wǎng)絡(luò)協(xié)議時這個問題會變得很突出,唯一解決辦法是設(shè)法對終端進行編程,讓它針對用戶希望使用的每個功能鍵只發(fā)送一個單獨的、唯一的字符,但這將限制可使用的控制字符的數(shù)目。 2 )為了讓 curses 能夠區(qū)分“單獨按下 Escapce 鍵”和“一個以 Escape 字符開頭的鍵盤轉(zhuǎn)義字符”,它必須等待一小段時間。 3 ) curses 不能處理二義性的 Escape 轉(zhuǎn)義序列。如果你的終端上兩個不同的按鍵會產(chǎn)生完全相同的轉(zhuǎn)義序列,就回導(dǎo)致 curses 不知該返回哪個邏輯按鍵。 Curses 對這一問題的處理方式是簡單地放棄對這個轉(zhuǎn)義序列的處理。 彩色顯示 鑒于歷史性原因, curses 只能以一種非常受限的方式
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2026北京中國氣象局地球系統(tǒng)數(shù)值預(yù)報中心博士后科研工作站招收7人備考題庫完整答案詳解
- 2025 小學(xué)四年級科學(xué)下冊熱氣球上升高度影響因素實驗課件
- 2025 小學(xué)四年級科學(xué)下冊植物葉的蒸騰作用觀察課件
- 2026年投資分析基礎(chǔ)股票與債券分析實操試題
- 2026年高級會計師職稱考試財務(wù)決策能力測試題
- 2026年CFA金融知識備考模擬試題集
- 2026年生物技術(shù)與生物安全知識題庫
- 2026年建筑工程施工安全標(biāo)準(zhǔn)建造師必考題
- 2026年網(wǎng)絡(luò)工程師網(wǎng)絡(luò)架構(gòu)設(shè)計方向筆試全解模擬題
- 2026年醫(yī)療電子與智慧醫(yī)療系統(tǒng)考試題目
- 《養(yǎng)老服務(wù)政策法規(guī)與標(biāo)準(zhǔn)》智慧健康養(yǎng)老服務(wù)專業(yè)全套教學(xué)課件
- 知識付費商業(yè)模式設(shè)計
- 無錫車聯(lián)天下信息技術(shù)有限公司智能網(wǎng)聯(lián)汽車車載顯示模組研發(fā)及智能化生產(chǎn)項目環(huán)評資料環(huán)境影響
- 抹灰層陰陽角方正度控制技術(shù)
- 【SA8000標(biāo)準(zhǔn)(社會責(zé)任標(biāo)準(zhǔn))對我國勞動密集型產(chǎn)業(yè)的影響及應(yīng)對措施研究12000字(論文)】
- 醫(yī)療行業(yè)知識產(chǎn)權(quán)教育的必要性
- 2024-2025學(xué)年滬教版(五四學(xué)制)(2024)初中英語六年級下冊(全冊)知識點歸納
- 五年級數(shù)學(xué)下冊寒假作業(yè)每日一練
- 傳染病院感防控課件
- 實習(xí)生醫(yī)德醫(yī)風(fēng)培訓(xùn)
- 橫穿公路管道施工方案
評論
0/150
提交評論