linux操作系統(tǒng)下c語言編程入門95124_第1頁
linux操作系統(tǒng)下c語言編程入門95124_第2頁
linux操作系統(tǒng)下c語言編程入門95124_第3頁
linux操作系統(tǒng)下c語言編程入門95124_第4頁
linux操作系統(tǒng)下c語言編程入門95124_第5頁
已閱讀5頁,還剩58頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

1、linux操作系統(tǒng)下c語言編程入門 (一)目錄介紹 1)Linux程序設(shè)計入門-基礎(chǔ)知識 2)Linux程序設(shè)計入門-進程介紹 3)Linux程序設(shè)計入門-文件操作 4)Linux程序設(shè)計入門-時間概念 5)Linux程序設(shè)計入門-信號處理 6)Linux程序設(shè)計入門-消息管理 7)Linux程序設(shè)計入門-線程操作 8)Linux程序設(shè)計入門-網(wǎng)絡(luò)編程 9)Linux下C開發(fā)工具介紹 (二)具體內(nèi)容 1)Linux程序設(shè)計入門-基礎(chǔ)知識 Linux下C語言編程基礎(chǔ)知識 前言: 這篇文章介紹在LINUX下進行C語言編程所需要的基礎(chǔ)知識.在這篇文章當(dāng)中,我們將 會學(xué)到以下內(nèi)容: 源程序編譯 Mak

2、efile的編寫 程序庫的鏈接 程序的調(diào)試 頭文件和系統(tǒng)求助 - - 1.源程序的編譯 在Linux下面,如果要編譯一個C語言源程序,我們要使用GNU的gcc編譯器. 下面我們 以一個實例來說明如何使用gcc編譯器. 假設(shè)我們有下面一個非常簡單的源程序(hello.c): int main(int argc,char *argv) printf(Hello Linuxn); 要編譯這個程序,我們只要在命令行下執(zhí)行: gcc -o hello hello.c gcc 編譯器就會為我們生成一個hello的可執(zhí)行文件.執(zhí)行./hello就可以看到程序的輸出 結(jié)果了.命令行中 gcc表示我們是用gcc

3、來編譯我們的源程序,-o 選項表示我們要求編譯 器給我們輸出的可執(zhí)行文件名為hello 而hello.c是我們的源程序文件. gcc編譯器有許多選項,一般來說我們只要知道其中的幾個就夠了. -o選項我們已經(jīng)知道 了,表示我們要求輸出的可執(zhí)行文件名. -c選項表示我們只要求編譯器輸出目標(biāo)代碼,而 不必要輸出可執(zhí)行文件. -g選項表示我們要求編譯器在編譯的時候提供我們以后對程序 進行調(diào)試的信息. 知道了這三個選項,我們就可以編譯我們自己所寫的簡單的源程序了,如果你想要知道更 多的選項,可以查看gcc的幫助文檔,那里有著許多對其它選項的詳細說明. 2.Makefile的編寫 假設(shè)我們有下面這樣的一個

4、程序,源代碼如下: /* main.c */ #include mytool1.h #include mytool2.h int main(int argc,char *argv) mytool1_print(hello); mytool2_print(hello); /* mytool1.h */ #ifndef _MYTOOL_1_H #define _MYTOOL_1_H void mytool1_print(char *print_str); #endif /* mytool1.c */ #include mytool1.h void mytool1_print(char *print

5、_str) printf(This is mytool1 print %sn,print_str); /* mytool2.h */ #ifndef _MYTOOL_2_H #define _MYTOOL_2_H void mytool2_print(char *print_str); #endif /* mytool2.c */ #include mytool2.h void mytool2_print(char *print_str) printf(This is mytool2 print %sn,print_str); 當(dāng)然由于這個程序是很短的我們可以這樣來編譯 gcc -c main

6、.c gcc -c mytool1.c gcc -c mytool2.c gcc -o main main.o mytool1.o mytool2.o 這樣的話我們也可以產(chǎn)生main程序,而且也不時很麻煩.但是如果我們考慮一下如果有一 天我們修改了其中的一個文件(比如說mytool1.c)那么我們難道還要重新輸入上面的命令 ?也許你會說,這個很容易解決啊,我寫一個SHELL腳本,讓她幫我去完成不就可以了.是的 對于這個程序來說,是可以起到作用的.但是當(dāng)我們把事情想的更復(fù)雜一點,如果我們的程 序有幾百個源程序的時候,難道也要編譯器重新一個一個的去編譯? 為此,聰明的程序員們想出了一個很好的工具來

7、做這件事情,這就是make.我們只要執(zhí)行以 下make,就可以把上面的問題解決掉.在我們執(zhí)行make之前,我們要先編寫一個非常重要的 文件.-Makefile.對于上面的那個程序來說,可能的一個Makefile的文件是: # 這是上面那個程序的Makefile文件 main:main.o mytool1.o mytool2.o gcc -o main main.o mytool1.o mytool2.o main.o:main.c mytool1.h mytool2.h gcc -c main.c mytool1.o:mytool1.c mytool1.h gcc -c mytool1.c m

8、ytool2.o:mytool2.c mytool2.h gcc -c mytool2.c 有了這個Makefile文件,不過我們什么時候修改了源程序當(dāng)中的什么文件,我們只要執(zhí)行 make命令,我們的編譯器都只會去編譯和我們修改的文件有關(guān)的文件,其它的文件她連理 都不想去理的. 下面我們學(xué)習(xí)Makefile是如何編寫的. 在Makefile中也#開始的行都是注釋行.Makefile中最重要的是描述文件的依賴關(guān)系的說 明.一般的格式是: target: components TAB rule 第一行表示的是依賴關(guān)系.第二行是規(guī)則. 比如說我們上面的那個Makefile文件的第二行 main:ma

9、in.o mytool1.o mytool2.o 表示我們的目標(biāo)(target)main的依賴對象(components)是main.o mytool1.o mytool2.o 當(dāng)倚賴的對象在目標(biāo)修改后修改的話,就要去執(zhí)行規(guī)則一行所指定的命令.就象我們的上 面那個Makefile第三行所說的一樣要執(zhí)行 gcc -o main main.o mytool1.o mytool2.o 注意規(guī)則一行中的TAB表示那里是一個TAB鍵 Makefile有三個非常有用的變量.分別是$,$,$代表的意義分別是: $-目標(biāo)文件,$-所有的依賴文件,$ main.o:main.c mytool1.h mytool

10、2.h gcc -c $ mytool1.o:mytool1.c mytool1.h gcc -c $ mytool2.o:mytool2.c mytool2.h gcc -c $ 經(jīng)過簡化后我們的Makefile是簡單了一點,不過人們有時候還想簡單一點.這里我們學(xué)習(xí) 一個Makefile的缺省規(guī)則 .c.o: gcc -c $ 這個規(guī)則表示所有的 .o文件都是依賴與相應(yīng)的.c文件的.例如mytool.o依賴于mytool.c 這樣Makefile還可以變?yōu)? # 這是再一次簡化后的Makefile main:main.o mytool1.o mytool2.o gcc -o $ $ .c.o

11、: gcc -c $ 好了,我們的Makefile 也差不多了,如果想知道更多的關(guān)于Makefile規(guī)則可以查看相應(yīng)的 文檔. 3.程序庫的鏈接 試著編譯下面這個程序 /* temp.c */ #include int main(int argc,char *argv) double value; printf(Value:%fn,value); 這個程序相當(dāng)簡單,但是當(dāng)我們用 gcc -o temp temp.c 編譯時會出現(xiàn)下面所示的錯誤. /tmp/cc33Kydu.o: In function main: /tmp/cc33Kydu.o(.text+0xe): undefined re

12、ference to log collect2: ld returned 1 exit status 出現(xiàn)這個錯誤是因為編譯器找不到log的具體實現(xiàn).雖然我們包括了正確的頭文件,但是我 們在編譯的時候還是要連接確定的庫.在Linux下,為了使用數(shù)學(xué)函數(shù),我們必須和數(shù)學(xué)庫 連接,為此我們要加入 -lm 選項. gcc -o temp temp.c -lm這樣才能夠正確的編譯.也許 有人要問,前面我們用printf函數(shù)的時候怎么沒有連接庫呢?是這樣的,對于一些常用的函 數(shù)的實現(xiàn),gcc編譯器會自動去連接一些常用庫,這樣我們就沒有必要自己去指定了. 有時 候我們在編譯程序的時候還要指定庫的路徑,這個

13、時候我們要用到編譯器的 -L選項指定 路徑.比如說我們有一個庫在 /home/hoyt/mylib下,這樣我們編譯的時候還要加上 -L/h ome/hoyt/mylib.對于一些標(biāo)準(zhǔn)庫來說,我們沒有必要指出路徑.只要它們在起缺省庫的路 徑下就可以了.系統(tǒng)的缺省庫的路徑/lib /usr/lib /usr/local/lib 在這三個路徑下面 的庫,我們可以不指定路徑. 還有一個問題,有時候我們使用了某個函數(shù),但是我們不知道庫的名字,這個時候怎么辦呢 ?很抱歉,對于這個問題我也不知道答案,我只有一個傻辦法.首先,我到標(biāo)準(zhǔn)庫路徑下面去 找看看有沒有和我用的函數(shù)相關(guān)的庫,我就這樣找到了線程(thre

14、ad)函數(shù)的庫文件(libp thread.a). 當(dāng)然,如果找不到,只有一個笨方法.比如我要找sin這個函數(shù)所在的庫. 就只 好用 nm -o /lib/*.so|grep sin/sin 命令,然后看/sin文件,到那里面去找了. 在s in文件當(dāng)中,我會找到這樣的一行l(wèi)ibm-2.1.2.so:00009fa0 W sin 這樣我就知道了sin在 libm-2.1.2.so庫里面,我用 -lm選項就可以了(去掉前面的lib和后面的版本標(biāo)志,就剩 下m了所以是 -lm). 如果你知道怎么找,請趕快告訴我,我回非常感激的.謝謝! 4.程序的調(diào)試 我們編寫的程序不太可能一次性就會成功的,在我們

15、的程序當(dāng)中,會出現(xiàn)許許多多我 們想不到的錯誤,這個時候我們就要對我們的程序進行調(diào)試了. 最常用的調(diào)試軟件是gdb.如果你想在圖形界面下調(diào)試程序,那么你現(xiàn)在可以選擇xxgdb.記 得要在編譯的時候加入 -g選項.關(guān)于gdb的使用可以看gdb的幫助文件.由于我沒有用過這 個軟件,所以我也不能夠說出如何使用. 不過我不喜歡用gdb.跟蹤一個程序是很煩的事情 ,我一般用在程序當(dāng)中輸出中間變量的值來調(diào)試程序的.當(dāng)然你可以選擇自己的辦法,沒有 必要去學(xué)別人的.現(xiàn)在有了許多IDE環(huán)境,里面已經(jīng)自己帶了調(diào)試器了.你可以選擇幾個試 一試找出自己喜歡的一個用. 5.頭文件和系統(tǒng)求助 有時候我們只知道一個函數(shù)的大概

16、形式,不記得確切的表達式,或者是不記得著函數(shù) 在那個頭文件進行了說明.這個時候我們可以求助系統(tǒng). 比如說我們想知道fread這個函數(shù)的確切形式,我們只要執(zhí)行 man fread 系統(tǒng)就會輸出著 函數(shù)的詳細解釋的.和這個函數(shù)所在的頭文件說明了. 如果我們要write這個函 數(shù)的說明,當(dāng)我們執(zhí)行man write時,輸出的結(jié)果卻不是我們所需要的. 因為我們要的是w rite這個函數(shù)的說明,可是出來的卻是write這個命令的說明.為了得到write的函數(shù)說明 我們要用 man 2 write. 2表示我們用的write這個函數(shù)是系統(tǒng)調(diào)用函數(shù),還有一個我們常 用的是3表示函數(shù)是C的庫函數(shù). 記住不管什

17、么時候,man都是我們的最好助手. - 好了,這一章就講這么多了,有了這些知識我們就可以進入激動人心的Linux下的C程序探 險活動. 2)Linux程序設(shè)計入門-進程介紹 Linux下進程的創(chuàng)建 前言: 這篇文章是用來介紹在Linux下和進程相關(guān)的各個概念.我們將會學(xué)到: 進程的概念 進程的身份 進程的創(chuàng)建 守護進程的創(chuàng)建 - - 1。進程的概念 Linux操作系統(tǒng)是面向多用戶的.在同一時間可以有許多用戶向操作系統(tǒng)發(fā)出各種命 令.那么操作系統(tǒng)是怎么實現(xiàn)多用戶的環(huán)境呢? 在現(xiàn)代的操作系統(tǒng)里面,都有程序和進程 的概念.那么什么是程序,什么是進程呢? 通俗的講程序是一個包含可以執(zhí)行代碼的文件 ,是

18、一個靜態(tài)的文件.而進程是一個開始執(zhí)行但是還沒有結(jié)束的程序的實例.就是可執(zhí)行文 件的具體實現(xiàn). 一個程序可能有許多進程,而每一個進程又可以有許多子進程.依次循環(huán) 下去,而產(chǎn)生子孫進程. 當(dāng)程序被系統(tǒng)調(diào)用到內(nèi)存以后,系統(tǒng)會給程序分配一定的資源(內(nèi) 存,設(shè)備等等)然后進行一系列的復(fù)雜操作,使程序變成進程以供系統(tǒng)調(diào)用.在系統(tǒng)里面只 有進程沒有程序,為了區(qū)分各個不同的進程,系統(tǒng)給每一個進程分配了一個ID(就象我們的 身份證)以便識別. 為了充分的利用資源,系統(tǒng)還對進程區(qū)分了不同的狀態(tài).將進程分為新 建,運行,阻塞,就緒和完成五個狀態(tài). 新建表示進程正在被創(chuàng)建,運行是進程正在運行,阻 塞是進程正在等待某一

19、個事件發(fā)生,就緒是表示系統(tǒng)正在等待CPU來執(zhí)行命令,而完成表示 進程已經(jīng)結(jié)束了系統(tǒng)正在回收資源. 關(guān)于進程五個狀態(tài)的詳細解說我們可以看操作系 統(tǒng)上面有詳細的解說。 2。進程的標(biāo)志 上面我們知道了進程都有一個ID,那么我們怎么得到進程的ID呢?系統(tǒng)調(diào)用getpid可 以得到進程的ID,而getppid可以得到父進程(創(chuàng)建調(diào)用該函數(shù)進程的進程)的ID. #include pid_t getpid(void); pid_t getppid(void); 進程是為程序服務(wù)的,而程序是為了用戶服務(wù)的.系統(tǒng)為了找到進程的用戶名,還為進程和 用戶建立聯(lián)系.這個用戶稱為進程的所有者.相應(yīng)的每一個用戶也有一個用

20、戶ID.通過系統(tǒng) 調(diào)用getuid可以得到進程的所有者的ID.由于進程要用到一些資源,而Linux對系統(tǒng)資源是 進行保護的,為了獲取一定資源進程還有一個有效用戶ID.這個ID和系統(tǒng)的資源使用有關(guān) ,涉及到進程的權(quán)限. 通過系統(tǒng)調(diào)用geteuid我們可以得到進程的有效用戶ID. 和用戶ID 相對應(yīng)進程還有一個組ID和有效組ID系統(tǒng)調(diào)用getgid和getegid可以分別得到組ID和有效 組ID #include #include uid_t getuid(void); uid_t geteuid(void); gid_t getgid(void); git_t getegid(void); 有時

21、候我們還會對用戶的其他信息感興趣(登錄名等等),這個時候我們可以調(diào)用getpwui d來得到. struct passwd char *pw_name; /* 登錄名稱 */ char *pw_passwd; /* 登錄口令 */ uid_t pw_uid; /* 用戶ID */ gid_t pw_gid; /* 用戶組ID */ char *pw_gecos; /* 用戶的真名 */ char *pw_dir; /* 用戶的目錄 */ char *pw_shell; /* 用戶的SHELL */ ; #include #include struct passwd *getpwuid(uid_

22、t uid); 下面我們學(xué)習(xí)一個實例來實踐一下上面我們所學(xué)習(xí)的幾個函數(shù): #include #include #include #include int main(int argc,char *argv) pid_t my_pid,parent_pid; uid_t my_uid,my_euid; gid_t my_gid,my_egid; struct passwd *my_info; my_pid=getpid(); parent_pid=getppid(); my_uid=getuid(); my_euid=geteuid(); my_gid=getgid(); my_egid=gete

23、gid(); my_info=getpwuid(my_uid); printf(Process ID:%ldn,my_pid); printf(Parent ID:%ldn,parent_pid); printf(User ID:%ldn,my_uid); printf(Effective User ID:%ldn,my_euid); printf(Group ID:%ldn,my_gid); printf(Effective Group ID:%ldn,my_egid); if(my_info) printf(My Login Name:%sn ,my_info-pw_name); prin

24、tf(My Password :%sn ,my_info-pw_passwd); printf(My User ID :%ldn,my_info-pw_uid); printf(My Group ID :%ldn,my_info-pw_gid); printf(My Real Name:%sn ,my_info-pw_gecos); printf(My Home Dir :%sn, my_info-pw_dir); printf(My Work Shell:%sn, my_info-pw_shell); 3。進程的創(chuàng)建 創(chuàng)建一個進程的系統(tǒng)調(diào)用很簡單.我們只要調(diào)用fork函數(shù)就可以了. #inc

25、lude pid_t fork(); 當(dāng)一個進程調(diào)用了fork以后,系統(tǒng)會創(chuàng)建一個子進程.這個子進程和父進程不同的地方只 有他的進程ID和父進程ID,其他的都是一樣.就象符進程克隆(clone)自己一樣.當(dāng)然創(chuàng)建 兩個一模一樣的進程是沒有意義的.為了區(qū)分父進程和子進程,我們必須跟蹤fork的返回 值. 當(dāng)fork掉用失敗的時候(內(nèi)存不足或者是用戶的最大進程數(shù)已到)fork返回-1,否則f ork的返回值有重要的作用.對于父進程fork返回子進程的ID,而對于fork子進程返回0.我 們就是根據(jù)這個返回值來區(qū)分父子進程的. 父進程為什么要創(chuàng)建子進程呢?前面我們已經(jīng) 說過了Linux是一個多用戶操

26、作系統(tǒng),在同一時間會有許多的用戶在爭奪系統(tǒng)的資源.有時 進程為了早一點完成任務(wù)就創(chuàng)建子進程來爭奪資源. 一旦子進程被創(chuàng)建,父子進程一起從 fork處繼續(xù)執(zhí)行,相互競爭系統(tǒng)的資源.有時候我們希望子進程繼續(xù)執(zhí)行,而父進程阻塞直 到子進程完成任務(wù).這個時候我們可以調(diào)用wait或者waitpid系統(tǒng)調(diào)用. #include #include pid_t wait(int *stat_loc); pid_t waitpid(pid_t pid,int *stat_loc,int options); wait系統(tǒng)調(diào)用會使父進程阻塞直到一個子進程結(jié)束或者是父進程接受到了一個信號.如果 沒有父進程沒有子進程或

27、者他的子進程已經(jīng)結(jié)束了wait回立即返回.成功時(因一個子進 程結(jié)束)wait將返回子進程的ID,否則返回-1,并設(shè)置全局變量errno.stat_loc是子進程的 退出狀態(tài).子進程調(diào)用exit,_exit 或者是return來設(shè)置這個值. 為了得到這個值Linux定 義了幾個宏來測試這個返回值. WIFEXITED:判斷子進程退出值是非0 WEXITSTATUS:判斷子進程的退出值(當(dāng)子進程退出時非0). WIFSIGNALED:子進程由于有沒有獲得的信號而退出. WTERMSIG:子進程沒有獲得的信號號(在WIFSIGNALED為真時才有意義). waitpid等待指定的子進程直到子進程返

28、回.如果pid為正值則等待指定的進程(pid).如果 為0則等待任何一個組ID和調(diào)用者的組ID相同的進程.為-1時等同于wait調(diào)用.小于-1時等 待任何一個組ID等于pid絕對值的進程. stat_loc和wait的意義一樣. options可以決定 父進程的狀態(tài).可以取兩個值 WNOHANG:父進程立即返回當(dāng)沒有子進程存在時. WUNTACHE D:當(dāng)子進程結(jié)束時waitpid返回,但是子進程的退出狀態(tài)不可得到. 父進程創(chuàng)建子進程后,子進程一般要執(zhí)行不同的程序.為了調(diào)用系統(tǒng)程序,我們可以使用系 統(tǒng)調(diào)用exec族調(diào)用.exec族調(diào)用有著5個函數(shù). #include int execl(con

29、st char *path,const char *arg,.); int execlp(const char *file,const char *arg,.); int execle(const char *path,const char *arg,.); int execv(const char *path,char *const argv); int execvp(const char *file,char *const argv): exec族調(diào)用可以執(zhí)行給定程序.關(guān)于exec族調(diào)用的詳細解說可以參考系統(tǒng)手冊(man exec l). 下面我們來學(xué)習(xí)一個實例.注意編譯的時候要加 -lm

30、以便連接數(shù)學(xué)函數(shù)庫. #include #include #include #include #include #include void main(void) pid_t child; int status; printf(This will demostrate how to get child statusn); if(child=fork()=-1) printf(Fork Error :%sn,strerror(errno); exit(1); else if(child=0) int i; printf(I am the child:%ldn,getpid(); for(i=0;i

31、1000000;i+) sin(i); i=5; printf(I exit with %dn,i); exit(i); while(child=wait(&status)=-1)&(errno=EINTR); if(child=-1) printf(Wait Error:%sn,strerror(errno); else if(!status) printf(Child %ld terminated normally return status is zeron, child); else if(WIFEXITED(status) printf(Child %ld terminated no

32、rmally return status is %dn, child,WEXITSTATUS(status); else if(WIFSIGNALED(status) printf(Child %ld terminated due to signal %d znot caughtn, child,WTERMSIG(status); strerror函數(shù)會返回一個指定的錯誤號的錯誤信息的字符串. 4。守護進程的創(chuàng)建 如果你在DOS時代編寫過程序,那么你也許知道在DOS下為了編寫一個常駐內(nèi)存的程序 我們要編寫多少代碼了.相反如果在Linux下編寫一個常駐內(nèi)存的程序卻是很容易的.我 們只要幾行代碼就

33、可以做到. 實際上由于Linux是多任務(wù)操作系統(tǒng),我們就是不編寫代碼 也可以把一個程序放到后臺去執(zhí)行的.我們只要在命令后面加上&符號SHELL就會把我們的 程序放到后臺去運行的. 這里我們開發(fā)一個后臺檢查郵件的程序.這個程序每個一個指 定的時間回去檢查我們的郵箱,如果發(fā)現(xiàn)我們有郵件了,會不斷的報警(通過機箱上的小喇 叭來發(fā)出聲音). 后面有這個函數(shù)的加強版本加強版本 后臺進程的創(chuàng)建思想: 首先父進程創(chuàng)建一個子進程.然后子進程殺死父進程(是不是很無 情?). 信號處理所有的工作由子進程來處理. #include #include #include #include #include #inclu

34、de #include /* Linux 的默任個人的郵箱地址是 /var/spool/mail/用戶的登錄名 */ #define MAIL /var/spool/mail/hoyt /* 睡眠10秒鐘 */ #define SLEEP_TIME 10 main(void) pid_t child; if(child=fork()=-1) printf(Fork Error:%sn,strerror(errno); exit(1); else if(child0) while(1); if(kill(getppid(),SIGTERM)=-1) printf(Kill Parent Erro

35、r:%sn,strerror(errno); exit(1); int mailfd; while(1) if(mailfd=open(MAIL,O_RDONLY)!=-1) fprintf(stderr,%s,007); close(mailfd); sleep(SLEEP_TIME); 你可以在默認(rèn)的路徑下創(chuàng)建你的郵箱文件,然后測試一下這個程序.當(dāng)然這個程序還有很 多地方要改善的.我們后面會對這個小程序改善的,再看我的改善之前你可以嘗試自己改 善一下.比如讓用戶指定郵相的路徑和睡眠時間等等.相信自己可以做到的.動手吧,勇敢 的探險者. 好了進程一節(jié)的內(nèi)容我們就先學(xué)到這里了.進程是一個非常重

36、要的概念,許多的程序都會 用子進程.創(chuàng)建一個子進程是每一個程序員的基本要求! 3)Linux程序設(shè)計入門-文件操作 Linux下文件的操作 前言: 我們在這一節(jié)將要討論linux下文件操作的各個函數(shù). 文件的創(chuàng)建和讀寫 文件的各個屬性 目錄文件的操作 管道文件 - - 1。文件的創(chuàng)建和讀寫 我假設(shè)你已經(jīng)知道了標(biāo)準(zhǔn)級的文件操作的各個函數(shù)(fopen,fread,fwrite等等).當(dāng)然 如果你不清楚的話也不要著急.我們討論的系統(tǒng)級的文件操作實際上是為標(biāo)準(zhǔn)級文件操作 服務(wù)的. 當(dāng)我們需要打開一個文件進行讀寫操作的時候,我們可以使用系統(tǒng)調(diào)用函數(shù)open.使用完 成以后我們調(diào)用另外一個close函數(shù)進

37、行關(guān)閉操作. #include #include #include #include int open(const char *pathname,int flags); int open(const char *pathname,int flags,mode_t mode); int close(int fd); open函數(shù)有兩個形式.其中pathname是我們要打開的文件名(包含路徑名稱,缺省是認(rèn)為在 當(dāng)前路徑下面).flags可以去下面的一個值或者是幾個值的組合. O_RDONLY:以只讀的方式打開文件. O_WRONLY:以只寫的方式打開文件. O_RDWR:以讀寫的方式打開文件. O

38、_APPEND:以追加的方式打開文件. O_CREAT:創(chuàng)建一個文件. O_EXEC:如果使用了O_CREAT而且文件已經(jīng)存在,就會發(fā)生一個錯誤. O_NOBLOCK:以非阻塞的方式打開一個文件. O_TRUNC:如果文件已經(jīng)存在,則刪除文件的內(nèi)容. 前面三個標(biāo)志只能使用任意的一個.如果使用了O_CREATE標(biāo)志,那么我們要使用open的第 二種形式.還要指定mode標(biāo)志,用來表示文件的訪問權(quán)限.mode可以是以下情況的組合. - S_IRUSR 用戶可以讀 S_IWUSR 用戶可以寫 S_IXUSR 用戶可以執(zhí)行 S_IRWXU 用戶可以讀寫執(zhí)行 - S_IRGRP 組可以讀 S_IWGRP

39、 組可以寫 S_IXGRP 組可以執(zhí)行 S_IRWXG 組可以讀寫執(zhí)行 - S_IROTH 其他人可以讀 S_IWOTH 其他人可以寫 S_IXOTH 其他人可以執(zhí)行 S_IRWXO 其他人可以讀寫執(zhí)行 - S_ISUID 設(shè)置用戶執(zhí)行ID S_ISGID 設(shè)置組的執(zhí)行ID - 我們也可以用數(shù)字來代表各個位的標(biāo)志.Linux總共用5個數(shù)字來表示文件的各種權(quán)限. 00000.第一位表示設(shè)置用戶ID.第二位表示設(shè)置組ID,第三位表示用戶自己的權(quán)限位,第四 位表示組的權(quán)限,最后一位表示其他人的權(quán)限. 每個數(shù)字可以取1(執(zhí)行權(quán)限),2(寫權(quán)限),4(讀權(quán)限),0(什么也沒有)或者是這幾個值的和 . 比

40、如我們要創(chuàng)建一個用戶讀寫執(zhí)行,組沒有權(quán)限,其他人讀執(zhí)行的文件.設(shè)置用戶ID位那么 我們可以使用的模式是-1(設(shè)置用戶ID)0(組沒有設(shè)置)7(1+2+4)0(沒有權(quán)限,使用缺省) 5(1+4)即10705: open(temp,O_CREAT,10705); 如果我們打開文件成功,open會返回一個文件描述符.我們以后對文件的所有操作就可以 對這個文件描述符進行操作了. 當(dāng)我們操作完成以后,我們要關(guān)閉文件了,只要調(diào)用close就可以了,其中fd是我們要關(guān)閉 的文件描述符. 文件打開了以后,我們就要對文件進行讀寫了.我們可以調(diào)用函數(shù)read和write進行文件的 讀寫. #include ssi

41、ze_t read(int fd, void *buffer,size_t count); ssize_t write(int fd, const void *buffer,size_t count); fd是我們要進行讀寫操作的文件描述符,buffer是我們要寫入文件內(nèi)容或讀出文件內(nèi)容的 內(nèi)存地址.count是我們要讀寫的字節(jié)數(shù). 對于普通的文件read從指定的文件(fd)中讀取count字節(jié)到buffer緩沖區(qū)中(記住我們必 須提供一個足夠大的緩沖區(qū)),同時返回count. 如果read讀到了文件的結(jié)尾或者被一個信號所中斷,返回值會小于count.如果是由信號中 斷引起返回,而且沒有返回數(shù)

42、據(jù),read會返回-1,且設(shè)置errno為EINTR.當(dāng)程序讀到了文件 結(jié)尾的時候,read會返回0. write從buffer中寫count字節(jié)到文件fd中,成功時返回實際所寫的字節(jié)數(shù). 下面我們學(xué)習(xí)一個實例,這個實例用來拷貝文件. #include #include #include #include #include #include #include #define BUFFER_SIZE 1024 int main(int argc,char *argv) int from_fd,to_fd; int bytes_read,bytes_write; char bufferBUFFER

43、_SIZE; char *ptr; if(argc!=3) fprintf(stderr,Usage:%s fromfile tofilena,argv0); exit(1); /* 打開源文件 */ if(from_fd=open(argv1,O_RDONLY)=-1) fprintf(stderr,Open %s Error:%sn,argv1,strerror(errno); exit(1); /* 創(chuàng)建目的文件 */ if(to_fd=open(argv2,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR)=-1) fprintf(stderr,Open %s Erro

44、r:%sn,argv2,strerror(errno); exit(1); /* 以下代碼是一個經(jīng)典的拷貝文件的代碼 */ while(bytes_read=read(from_fd,buffer,BUFFER_SIZE) /* 一個致命的錯誤發(fā)生了 */ if(bytes_read=-1)&(errno!=EINTR) break; else if(bytes_read0) ptr=buffer; while(bytes_write=write(to_fd,ptr,bytes_read) /* 一個致命錯誤發(fā)生了 */ if(bytes_write=-1)&(errno!=EINTR)bre

45、ak; /* 寫完了所有讀的字節(jié) */ else if(bytes_write=bytes_read) break; /* 只寫了一部分,繼續(xù)寫 */ else if(bytes_write0) ptr+=bytes_write; bytes_read-=bytes_write; /* 寫的時候發(fā)生的致命錯誤 */ if(bytes_write=-1)break; close(from_fd); close(to_fd); exit(0); 2。文件的各個屬性 文件具有各種各樣的屬性,除了我們上面所知道的文件權(quán)限以外,文件還有創(chuàng)建時間 ,大小等等屬性. 有時侯我們要判斷文件是否可以進行某種操作

46、(讀,寫等等).這個時候我們可以使用acce ss函數(shù). #include int access(const char *pathname,int mode); pathname:是文件名稱,mode是我們要判斷的屬性.可以取以下值或者是他們的組合. R_OK文件可以讀,W_OK文件可以寫,X_OK文件可以執(zhí)行,F_OK文件存在.當(dāng)我們測試成功時 ,函數(shù)返回0,否則如果有一個條件不符時,返回-1. 如果我們要獲得文件的其他屬性,我們可以使用函數(shù)stat或者fstat. #include #include int stat(const char *file_name,struct stat *b

47、uf); int fstat(int filedes,struct stat *buf); struct stat dev_t st_dev; /* 設(shè)備 */ ino_t st_ino; /* 節(jié)點 */ mode_t st_mode; /* 模式 */ nlink_t st_nlink; /* 硬連接 */ uid_t st_uid; /* 用戶ID */ gid_t st_gid; /* 組ID */ dev_t st_rdev; /* 設(shè)備類型 */ off_t st_off; /* 文件字節(jié)數(shù) */ unsigned long st_blksize; /* 塊大小 */ unsign

48、ed long st_blocks; /* 塊數(shù) */ time_t st_atime; /* 最后一次訪問時間 */ time_t st_mtime; /* 最后一次修改時間 */ time_t st_ctime; /* 最后一次改變時間(指屬性) */ ; stat用來判斷沒有打開的文件,而fstat用來判斷打開的文件.我們使用最多的屬性是st_ mode.通過著屬性我們可以判斷給定的文件是一個普通文件還是一個目錄,連接等等.可以 使用下面幾個宏來判斷. S_ISLNK(st_mode):是否是一個連接.S_ISREG是否是一個常規(guī)文件.S_ISDIR是否是一個目 錄S_ISCHR是否是一

49、個字符設(shè)備.S_ISBLK是否是一個塊設(shè)備S_ISFIFO是否 是一個FIFO文 件.S_ISSOCK是否是一個SOCKET文件. 我們會在下面說明如何使用這幾個宏的. 3。目錄文件的操作 在我們編寫程序的時候,有時候會要得到我們當(dāng)前的工作路徑。C庫函數(shù)提供了get cwd來解決這個問題。 #include char *getcwd(char *buffer,size_t size); 我們提供一個size大小的buffer,getcwd會把我們當(dāng)前的路徑考到buffer中.如果buffer 太小,函數(shù)會返回-1和一個錯誤號. Linux提供了大量的目錄操作函數(shù),我們學(xué)習(xí)幾個比較簡單和常用的函

50、數(shù). #include #include #include #include #include int mkdir(const char *path,mode_t mode); DIR *opendir(const char *path); struct dirent *readdir(DIR *dir); void rewinddir(DIR *dir); off_t telldir(DIR *dir); void seekdir(DIR *dir,off_t off); int closedir(DIR *dir); struct dirent long d_ino; off_t d_off;

溫馨提示

  • 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)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論