版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
Linux環(huán)境下的
C語言編程第一部分:Linux下的C編程實戰(zhàn)之開發(fā)平臺搭建準(zhǔn)備工作建議在PC內(nèi)存足夠大的情況下,不要直接安裝Linux操作系統(tǒng),最好把它安裝在運行VMWare虛擬機(jī)軟件的Windows平臺上,如下圖:1.Vim和Emacs編輯器在Linux平臺下,可用任意一個文本編輯工具編輯源代碼。Vim(viimprove)是Linux下功能強大的編輯器,是由UNIX系統(tǒng)下的傳統(tǒng)文本編輯器vi發(fā)展而來,是vi的一個增強版本,有彩色和高亮等特性,對編程有很大幫助。主菜單-編程-viImproved命令來運行x-windows下的vim。Emacs即EditorMACroS(編輯器宏)的縮寫,是一種強大的文本編輯器,在程序員和其他以技術(shù)工作為主的計算機(jī)用戶中廣受歡迎。使用vim編輯helloworld程序使用emacs編輯helloworld程序2.GCC編譯器GCC是Linux平臺下最重要的開發(fā)工具,它是GNU的C和C++編譯器,其基本用法為:gcc[options][filenames]
該命令按編譯選項(參數(shù)options)指定的操作對給定的文件進(jìn)行編譯處理。編譯一輸出“HelloWorld”的程序:
main()
{
printf("HelloWorld\n");
}2.GCC編譯器最簡單的編譯方法是不指定任何編譯選項,它會為目標(biāo)程序生成默認(rèn)的文件名a.outgcc
helloworld.co選項:編譯來為將產(chǎn)生的可執(zhí)行文件指定一個文件名。例如,將上述名為helloworld.c的C程序編譯為名叫helloworld的可執(zhí)行文件,輸入如下命令:gcc-ohelloworld
helloworld.c
2.GCC編譯器-常用選項c選項:告訴GCC僅把源代碼(.c文件)編譯為目標(biāo)代碼(.o文件)而跳過匯編和連接的步驟;它能使編譯多個C程序時的速度更快且容易管理。例如用戶將已編輯好的test.c文件編譯成名為test.o的目標(biāo)文件??梢允褂妹頶cc-ctest.cs選項:告訴GCC在為C代碼產(chǎn)生了匯編語言文件后停止編譯。GCC產(chǎn)生的匯編語言文件的缺省擴(kuò)展名是.s。將生成helloworld.c的匯編代碼,使用的是AT&T匯編。用emacs打開匯編代碼如下圖。用emacs打開的Hello.c的匯編代碼2.GCC編譯器-常用選項E選項:指示編譯器僅對輸入文件進(jìn)行預(yù)處理,但不匯編和連接O(-O1)選項:告訴GCC對源代碼進(jìn)行基本優(yōu)化從而使得程序執(zhí)行地更快;而-O2選項告訴GCC產(chǎn)生盡可能小和盡可能快的代碼。使用-O2選項編譯的速度比使用-O時慢,但產(chǎn)生的代碼執(zhí)行速度會更快。Wall選項:顯示附加的警告信息。例如在上述程序中去掉return0;語句,之后重新編譯gcc–Wall–ohellohello.c將得到的警告信息:hello.c:5:warning:controlreachesendofnon-voidfunction3.GDB調(diào)試器GCC用于編譯程序,而Linux的另一個GNU工具gdb則用于調(diào)試程序。gdb是一個用來調(diào)試C和C++程序的強力調(diào)試器,通過它進(jìn)行一系列調(diào)試工作。gdb主要提供一下功能:監(jiān)視程序中變量的值得變化設(shè)置斷點,使程序在指定的代碼上暫停執(zhí)行,便于觀察單步執(zhí)行代碼分析崩潰程序產(chǎn)生的core文件3.GDB調(diào)試器★gdb最常用的命令如下file:裝入想要調(diào)試的可執(zhí)行文件。kill:終止正在調(diào)試的程序。list:列表顯示源代碼。next:執(zhí)行一行源代碼但不進(jìn)入函數(shù)內(nèi)部。step:執(zhí)行一行源代碼而且進(jìn)入函數(shù)內(nèi)部。run:執(zhí)行當(dāng)前被調(diào)試的程序quit:終止gdbwatch:監(jiān)視一個變量的值break:在代碼里設(shè)置斷點,程序執(zhí)行到這里時掛起3.GDB調(diào)試器舉例說明怎樣用GDB調(diào)試一個求0+1+2+3+…+99的程序:
/*Filename:sum.c*/
main()
{
inti,sum;
sum=0;
for(i=0;i<100;i++)
{
sum+=i;
}
printf("thesumof1+2+...+is%d",sum);
}3.GDB調(diào)試器3.GDB調(diào)試器執(zhí)行如下命令編譯sum.c(加-g選項產(chǎn)生debug信息):
gcc–g–osumsum.c在命令行上鍵入gdbsum并按回車鍵就可以開始調(diào)試sum了,再運行run命令執(zhí)行sum,屏幕上將看到如下內(nèi)容:3.GDB調(diào)試器list命令:list命令用于列出源代碼,對上述程序運行l(wèi)ist,將出現(xiàn)如下畫面(源代碼被標(biāo)行號):
3.GDB調(diào)試器根據(jù)列出的源程序,如果將斷點設(shè)置在第4行,只需在gdb
命令行提示符下鍵入如下命令設(shè)置斷點:(gdb)break4Breakpoint1at0x8048338:filesum.cline4這時再run,程序會停止在第4行:Startingprogram:/root/sumBreakpoint1,main()atsum.cline44sum=03.GDB調(diào)試器設(shè)置斷點的另一種語法是break<function>,它在進(jìn)入指定函數(shù)(function)時停住。相反的,clear用于清除所有的已定義的斷點clear<function>清除設(shè)置在函數(shù)上的斷點;clear<linenum>則清除設(shè)置在指定行上的斷點。3.GDB調(diào)試器watch命令:用于觀查變量或表達(dá)式的值watch命令觀查sum變量只需要運行:watchsumwatch命令觀查表達(dá)式:watch<expr>
為表達(dá)式(變量)expr設(shè)置一個觀察點,變量表達(dá)式值有變化時,程序會停止執(zhí)行。要觀查當(dāng)前設(shè)置的watch,可以使用infowatchpoints命令。3.GDB調(diào)試器next、step命令:next、step用于單步執(zhí)行,在執(zhí)行的過程中,被watch變量的變化情況將實時呈現(xiàn)(分別顯示Oldvalue和Newvalue),如下圖:next、step命令的區(qū)別在于step遇到函數(shù)調(diào)用,會跳轉(zhuǎn)到該函數(shù)定義的開始行去執(zhí)行,而next則不進(jìn)入到函數(shù)內(nèi)部,它把函數(shù)調(diào)用語句當(dāng)作一條普通語句執(zhí)行。4.Make編譯和連接的區(qū)別編譯器使用源碼文件來產(chǎn)生某種形式的目標(biāo)文件,在編譯過程中,外部的符號參考并沒有被解釋或替換(即外部全局變量和函數(shù)并沒有被找到)。因此,在編譯階段所報的錯誤一般都是語法錯誤。連接器則用于連接目標(biāo)文件和程序包,生成一個可執(zhí)行程序。在連接階段,一個目標(biāo)文件中對別的文件中的符號的參考被解釋,如果有符號不能找到,會報告連接錯誤。4.Make編譯和連接的一般步驟是:第一階段把源文件一個一個的編譯成目標(biāo)文件,第二階段把所有的目標(biāo)文件加上需要的程序包連接成一個可執(zhí)行文件。這樣的過程需要使用大量的gcc命令。而make則使從大量源文件的編譯和連接工作中解放出來,綜合為一步完成。4.MakeGNUMake的主要工作是讀進(jìn)一個文本文件,稱為makefile。makefile文件記錄了哪些文件(目的文件,目的文件不一定是最后的可執(zhí)行程序,它可以是任何一種文件)由哪些文件(依靠文件)產(chǎn)生,用什么命令來產(chǎn)生。Make依靠此makefile中的信息檢查磁盤上的文件,如果目的文件的創(chuàng)建或修改時間比它的一個依靠文件舊的話,make就執(zhí)行相應(yīng)的命令,以便更新目的文件。
4.Makemakefile文件的編寫makefile文件是一個文本文件,用于描述整個項目和各個文件之間的依賴關(guān)系。它由多個規(guī)則組成。makefile文件的規(guī)則遵循以下結(jié)構(gòu)#remark注釋行target:file1file2[…]二進(jìn)制文件或者目標(biāo)文件command1命令command2[…]4.Make例如:下面三個文件,add.h用于聲明add函數(shù),add.c提供兩個整數(shù)相加的函數(shù)體,而main.c中調(diào)用add函數(shù):
/*filename:add.h*/
externint
add(inti,intj);
/*filename:add.c*/
int
add(inti,intj)
{
returni+j;
}
/*filename:main.c*/
#include"add.h"
main()
{
inta,b;
a=2;
b=3;
printf("thesumofa+bis%d",add(a+b));}怎樣為上述三個文件產(chǎn)生makefile呢?4.Make為上述三個文件產(chǎn)生makefile的方法如下:
test:main.o
add.o
gcc
main.o
add.o-otest
main.o:main.c
add.h
gcc-cmain.c-omain.o
add.o:add.c
add.h
gcc-cadd.c-oadd.o
4.Make上述makefile文件的含義利用add.c和add.h文件執(zhí)行g(shù)cc-cadd.c-oadd.o命令產(chǎn)生add.o目標(biāo)代碼。利用main.c和add.h文件執(zhí)行g(shù)cc-cmain.c-omain.o命令產(chǎn)生main.o目標(biāo)代碼。最后利用main.o和add.o文件(兩個模塊的目標(biāo)代碼)執(zhí)行g(shù)cc
main.o
add.o-otest命令產(chǎn)生可執(zhí)行文件test??梢允褂胓cc-MMmain.c自動尋找源文件中的頭文件,并形成依賴關(guān)系。輸出為:main.o
main.c
add.h4.Make可在makefile中加入變量。另外,環(huán)境變量在make過程中也被解釋成make的變量。這些變量是大小寫敏感的,一般使用大寫字母。要定義一個變量,只需要在一行的開始寫下這個變量的名字,后面跟一個=號,再跟變量的值。引用變量的方法是寫一個$符號,后面跟(變量名)。4.Make把前面的makefile
利用變量重寫一遍(并假設(shè)使用-Wall-O–g編譯選項):OBJS=main.o
add.oCC=gccCFLAGS=-Wall-O-gtest:$(OBJS)$(CC)$(OBJS)-otestmain.o:main.c
add.h$(CC)$(CFLAGS)-cmain.c-omain.oadd.o:add.c
add.h$(CC)$(CFLAGS)-cadd.c-oadd.o
4.Makemakefile
中還可定義清除(clean)目標(biāo),可用來清除編譯過程中產(chǎn)生的中間文件,例如在上述makefile文件中添加下列代碼:clean:
rm-f*.o運行makeclean時,將執(zhí)行rm-f*.o命令,刪除所有編譯過程中產(chǎn)生的中間文件。
4.MakeMake的運行GUNmake默認(rèn)在當(dāng)前的目錄下一次查找GUNmake文件,Makefile文件和makefile文件,找到后讀取文件執(zhí)行。給make命令指定一個特殊名字的makefile文件make–fhchen.mk4.Make自己動手編寫makefile仍然是很復(fù)雜和煩瑣的,而且很容易出錯。因此,GNU也提供了Automake和Autoconf來輔助快速自動產(chǎn)生makefile。
4.Make使用autoconf和automake來進(jìn)行自動化配置和生成Makefile的流程可以概括如下:運行autoscan命令。將configure.scan文件重命名為configure.in,并修改configure.in文件。運行aclocal命令得到aclocal.m4文件。運行autoconf命令得到configure文件。在工程目錄下新建Makefile.am文件,如果存在子目錄,子目錄中也要創(chuàng)建此文件。將/usr/share/automake-1.X/目錄下的depcomp和compile文件復(fù)制到需要處理目錄下。運行automake-a命令得到Makefile.in文件。運行./configure腳本4.Make從例子程序helloworld開始。過程如下:新建三個文件:
helloworld.c
configure.in
Makefile.am然后執(zhí)行
aclocal;autoconf;automake--add-missing;./configure;make;./helloworld
Makefile被產(chǎn)生出來,而且可以將helloworld.c編譯通過。小結(jié)本部分主要闡述了Linux程序的編寫、編譯、調(diào)試方法及make,實際上是引導(dǎo)學(xué)習(xí)怎樣在Linux下編程,為后續(xù)章節(jié)做好準(zhǔn)備。
第二部分
Linux下的C編程實戰(zhàn)之文件系統(tǒng)編程
Linux平臺下文件編程在Linux平臺下對文件編程可以使用兩類函數(shù):Linux操作系統(tǒng)文件API;C語言I/O庫函數(shù)。前者依賴于Linux系統(tǒng)調(diào)用,后者實際上與操作系統(tǒng)是獨立的,因為在任何操作系統(tǒng)下,使用C語言I/O庫函數(shù)操作文件的方法都是相同的。1.Linux文件API-創(chuàng)建創(chuàng)建int
create(constchar*filename,mode_tmode);
參數(shù)mode指定新建文件的存取權(quán)限,它同umask一起決定文件的最終權(quán)限(mode&umask).umask代表了文件在創(chuàng)建時需要去掉的一些存取權(quán)限。umask可通過系統(tǒng)調(diào)用umask()來改變:
int
umask(int
newmask);
該調(diào)用將umask設(shè)置為newmask,然后返回舊的umask,它只影響讀、寫和執(zhí)行權(quán)限。1.Linux文件API-創(chuàng)建mode可以是以下情況的組合,可以通過上述宏進(jìn)行“或”邏輯產(chǎn)生標(biāo)志。
標(biāo)志含義S_IRUSR
用戶可以讀
S_IWUSR
用戶可以寫
S_IXUSR
用戶可以執(zhí)行
S_IRWXU
用戶可以讀、寫、執(zhí)行
S_IRGRP
組可以讀
S_IWGRP組可以寫S_IXGRP組可以執(zhí)行S_IRWXG組可以讀寫執(zhí)行1.Linux文件API-創(chuàng)建mode可以是以下情況的組合,可以通過上述宏進(jìn)行“或”邏輯產(chǎn)生標(biāo)志。標(biāo)志含義S_IROTH其他人可以讀S_IWOTH其他人可以寫S_IXOTH其他人可以執(zhí)行S_IRWXO其他人可以讀、寫、執(zhí)行S_ISUID設(shè)置用戶執(zhí)行IDS_ISGID設(shè)置組的執(zhí)行ID1.Linux文件API-創(chuàng)建用數(shù)字來表示:Linux總共用5個數(shù)字來表示文件的各種權(quán)限:第一位表示設(shè)置用戶ID;第二位表示設(shè)置組ID;第三位表示用戶自己的權(quán)限位;第四位表示組的權(quán)限;最后一位表示其他人的權(quán)限。每個數(shù)字可以取1(執(zhí)行權(quán)限)、2(寫權(quán)限)、4(讀權(quán)限)、0(無)或者是這些值的和。1.Linux文件API-創(chuàng)建★用數(shù)字來表示:例如,要創(chuàng)建一個用戶可讀、可寫、可執(zhí)行,但是組沒有權(quán)限,其他人可以讀、可以執(zhí)行的文件,并設(shè)置用戶ID位。應(yīng)該使用的模式是1(設(shè)置用戶ID)、0(不設(shè)置組ID)、7(1+2+4,讀、寫、執(zhí)行)、0(沒有權(quán)限)、5(1+4,讀、執(zhí)行)即10705
1.Linux文件API-打開打開int
open(constchar*pathname,intflags);int
open(constchar*pathname,intflags,mode_tmode);如果文件打開成功,open函數(shù)會返回一個文件描述符,以后對該文件的所有操作就可以通過對這個文件描述符進(jìn)行操作來實現(xiàn)。open函數(shù)有兩個形式,其中pathname是要打開的文件名(包含路徑名稱,缺省是認(rèn)為在當(dāng)前路徑下面)。
1.Linux文件API-打開打開flags可以是下面的一個值或者是幾個值的組合,O_RDONLY、O_WRONLY、O_RDWR三個標(biāo)志只能使用任意的一個。1.Linux文件API-打開打開如果使用了O_CREATE標(biāo)志,則使用的函數(shù)是int
open(constchar*pathname,int
flags,mode_tmode);這時要指定mode標(biāo)志,用來表示文件的訪問權(quán)限。以O(shè)_CREAT為標(biāo)志的open實際上實現(xiàn)了文件創(chuàng)建的功能。例如:
open("test",O_CREAT,10705);
open("test",O_CREAT,S_IRWXU|S_IROTH|S_IXOTH|S_ISUID);
1.Linux文件API-讀寫讀寫Linux中提供文件讀寫的系統(tǒng)調(diào)用是read、write函數(shù):
int
read(int
fd,constvoid*buf,size_tlength);
int
write(int
fd,constvoid*buf,size_tlength);參數(shù)buf為指向緩沖區(qū)的指針,length為緩沖區(qū)的大?。ㄒ宰止?jié)為單位)。1.Linux文件API-讀寫int
read(int
fd,constvoid*buf,size_tlength);函數(shù)read實現(xiàn)從文件描述符fd所指定的文件中讀取length個字節(jié)到buf所指向的緩沖區(qū)中,返回值為實際讀取的字節(jié)數(shù)。int
write(int
fd,constvoid*buf,size_tlength);函數(shù)write實現(xiàn)將把length個字節(jié)從buf指向的緩沖區(qū)中寫到文件描述符fd所指向的文件中,返回值為實際寫入的字節(jié)數(shù)。1.Linux文件API-定位定位:對于隨機(jī)文件,我們可以隨機(jī)的指定位置讀寫,使用如下函數(shù)進(jìn)行定位:
int
lseek(int
fd,offset_toffset,intwhence);lseek()將文件讀寫指針相對whence移動offset個字節(jié)。操作成功時,返回文件指針相對于文件頭的位置。參數(shù)whence可使用下述值:SEEK_SET:相對文件開頭SEEK_CUR:相對文件讀寫指針的當(dāng)前位置SEEK_END:相對文件末尾1.Linux文件API-定位定位:offset可取負(fù)值,例如下述調(diào)用可將文件指針相對當(dāng)前位置向前移動5個字節(jié):
lseek(fd,-5,SEEK_CUR);由于lseek函數(shù)的返回值為文件指針相對于文件頭的位置,因此下列調(diào)用的返回值就是文件的長度:
lseek(fd,0,SEEK_END);1.Linux文件API-關(guān)閉關(guān)閉當(dāng)操作完成以后,要關(guān)閉文件,只要調(diào)用close即可,其中fd是要關(guān)閉的文件描述符:
int
close(int
fd);1.Linux文件API-編程實例例程:編寫一個程序,在當(dāng)前目錄下創(chuàng)建用戶可讀寫文件“hello.txt”,在其中寫入“Hello,softwareweekly”,關(guān)閉該文件。再次打開該文件,讀取其中的內(nèi)容并輸出在屏幕上。
#include<sys/types.h>//類型#include<sys/stat.h>//獲取文件屬性#include<fcntl.h>//文件描述詞操作#include<stdio.h>
#defineLENGTH100
main()
{
int
fd,len;
charstr[LENGTH];
fd=open(“hello.txt”,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);/*創(chuàng)建并打開文件,以讀寫的方式打開,用戶可以讀、用戶可以寫*/
1.Linux文件API-編程實例if(fd)
{
write(fd,"Hello,SoftwareWeekly",strlen("Hello,softwareweekly"));/*寫入Hello,softwareweekly字符串*/
close(fd);
}
fd=open(“hello.txt”,O_RDWR);//以讀寫方式打開
len=read(fd,str,LENGTH);/*讀取文件內(nèi)容*/
str[len]='\0';
printf("%s\n",str);
close(fd);
}1.Linux文件API-編程實例1.Linux文件API-編程實例編譯并運行,執(zhí)行結(jié)果如下[root@dl
root]#gcc–g–ohello./hello.c[root@dlroot]#./helloHello,softwareweekly
3.C語言庫函數(shù)C庫函數(shù)的文件操作實際上是獨立于具體的操作系統(tǒng)平臺的,不管是在DOS、Windows、Linux還是在VxWorks中都是這些函數(shù).
3.C語言庫函數(shù)-創(chuàng)建和打開創(chuàng)建和打開FILE*fopen(constchar*path,constchar*mode);fopen()實現(xiàn)打開指定文件,參數(shù)path字符串包含欲打開的文件路徑及文件名
,mode為打開模式.返回值:文件指針名。必須被說明為FILE類型的指針變量。3.C語言庫函數(shù)-創(chuàng)建和打開C語言中支持的打開模式如下表,其中b用于區(qū)分二進(jìn)制文件和文本文件,在DOS、Windows系統(tǒng)中是有區(qū)分的,但Linux不區(qū)分二進(jìn)制文件和文本文件。
標(biāo)志含義r,rb以只讀方式打開w,wb以只寫方式打開。如果文件不存在,則創(chuàng)建該文件,否則文件被截斷a,ab以追加方式打開。如果文件不存在,則創(chuàng)建該文件r+,r+b,rb+以讀寫方式打開+,w+b,wh+以讀寫方式打開。如果文件不存在時,創(chuàng)建新文件,否則文件被截斷a+,a+b,ab+以讀和追加方式打開。如果文件不存在,創(chuàng)建新文件3.C語言庫函數(shù)-讀寫讀寫:C庫函數(shù)支持以字符、字符串等為單位,支持按照某中格式進(jìn)行文件的讀寫,這一組函數(shù)為:int
fgetc(FILE*stream);從流中讀一個字符int
fputc(intc,FILE*stream);送一個字符到流中char*fgets(char*s,intn,FILE*stream);從流中讀取一字符串
int
fputs(constchar*s,FILE*stream);送一個字符串到流中3.C語言庫函數(shù)-讀寫int
fprintf(FILE*stream,constchar*format,...);傳送格式化輸出到一個文件中,成功時返回轉(zhuǎn)換的字節(jié)數(shù),失敗時返回一個負(fù)數(shù)。
fprintf(stream,"%s%c",s,c);
fprintf(stream,"%d\n",i);int
fscanf(FILE*stream,constchar*format,...);從一個流中執(zhí)行格式化輸入
if(fscanf(stdin,"%d",&i))
printf("Theintegerreadwas:%d\n",i);
3.C語言庫函數(shù)-讀寫讀寫:size_t
fread(void*ptr,size_tsize,size_tn,FILE*stream);size_t
fwrite(constvoid*ptr,size_tsize,size_tn,FILE*stream);fread()實現(xiàn)從流stream中讀取n個字段,每個字段為size字節(jié),并將讀取的字段放入ptr所指的字符數(shù)組中,返回實際已讀取的字段數(shù)。write()實現(xiàn)從緩沖區(qū)ptr所指的數(shù)組中把n個字段寫到流stream中,每個字段長為size個字節(jié),返回實際寫入的字段數(shù)。3.C語言庫函數(shù)-定位定位:C庫函數(shù)還提供了讀寫過程中的定位能力,這些函數(shù)包括:int
fgetpos(FILE*stream,fpos_t*pos);//將文件流的文件位置指示符存儲在pos變量中int
fsetpos(FILE*stream,constfpos_t*pos);//將文件指針定位在pos指定的位置上返回值:成功返回0,否則返回非0。#include<stdio.h>voidmain(void){FILE*fp;
fpos_tpos;charbuffer[50];if((fp=fopen("test.txt","rb"))==NULL)/*以只讀方式打開名為test.txt的文件*/
printf("Troubleopeningfile\n");else{pos=10;/*設(shè)置pos值*/if(fsetpos(fp,&pos)!=0)/*應(yīng)用fsetpos函數(shù)將文件指針fp按照pos指定的位置在文件中定位*/
perror("fsetposerror");else{
/*從新定位的文件指針開始讀取16個字符到buffer緩沖區(qū)*/fread(buffer,sizeof(char),16,fp);printf("16bytesatbyte%ld:%.16s\n",pos,buffer);/*顯示結(jié)果*/}}
fclose(fp);}
3.C語言庫函數(shù)-定位首先,程序以只讀方式打開名為test.txt的文件。在這里,test.txt文件中已存入字符串Thisisatestfortestingthefunctionoffsetpos.將pos設(shè)置為10。應(yīng)用fsetpos函數(shù)將文件指針fp按照pos指定的位置在文件中定位。這樣文件指針fp指向字符串中test的字母t。再從新定位的文件指針開始讀取16個字符到buffer緩沖區(qū),也就是說讀取字符串"testfortesting"到緩沖區(qū)buffer。最后顯示結(jié)果:16bytesatbyte10:testfortesting
3.C語言庫函數(shù)-定位int
fseek(FILE*stream,longoffset,intwhence);stream為文件指針offset為偏移量,整數(shù)表示正向偏移,負(fù)數(shù)為負(fù)向偏移whence設(shè)定從文件的哪里開始偏移,可能取值為:SEEK_SET:文件開頭0SEEK_CUR:當(dāng)前位置1SEEK_END:文件結(jié)尾2fseek(fp,100L,0);把fp指針移動到離文件開頭100字節(jié)處;fseek(fp,100L,1);把fp指針移動到離文件當(dāng)前位置100字節(jié)處;fseek(fp,100L,2);把fp指針退回到離文件結(jié)尾100字節(jié)處。3.C語言庫函數(shù)-關(guān)閉關(guān)閉:利用C庫函數(shù)關(guān)閉文件的操作:
int
fclose(FILE*stream);3.C語言庫函數(shù)-編程實例例程:編寫一個程序,在當(dāng)前目錄下創(chuàng)建用戶可讀寫文件“hello.txt”,在其中寫入“Hello,softwareweekly”,關(guān)閉該文件。再次打開該文件,讀取其中的內(nèi)容并輸出在屏幕上。
#include<stdio.h>
#defineLENGTH100
main()
{
FILE*fd;
charstr[LENGTH];
fd=fopen("hello.txt","w+");/*創(chuàng)建并打開文件*/
if(fd)
{
fputs("Hello,SoftwareWeekly",fd);/*寫入Hello,softwareweekly字符串*/
fclose(fd);
}
fd=fopen("hello.txt","r");
fgets(str,LENGTH,fd);/*讀取文件內(nèi)容*/
printf("%s\n",str);
fclose(fd);
}3.小結(jié)Linux提供的虛擬文件系統(tǒng)為多種文件系統(tǒng)提供了統(tǒng)一的接口,Linux的文件編程有兩種途徑:基于Linux系統(tǒng)調(diào)用;基于C庫函數(shù)這兩種編程所涉及到文件操作有新建、打開、讀寫和關(guān)閉,對隨機(jī)文件還可以定位。
第二部分
Linux下的C編程實戰(zhàn)之進(jìn)程控制與進(jìn)程通信編程1.進(jìn)程的基本概念進(jìn)程是具有一定功能的程序,是關(guān)于一個數(shù)據(jù)集合的一次執(zhí)行過程。多個進(jìn)程可以同時運行。Linux進(jìn)程在內(nèi)存中包含三部分?jǐn)?shù)據(jù):代碼段:存放了程序的代碼,代碼段可以為機(jī)器中運行同一程序的數(shù)個進(jìn)程共享。堆棧段:存放的是子程序(函數(shù))的返回地址、子程序的參數(shù)及程序的局部變量。數(shù)據(jù)段:存放程序的全局變量、常數(shù)以及動態(tài)數(shù)據(jù)分配的數(shù)據(jù)空間。堆棧段和數(shù)據(jù)段不能為運行同一程序的數(shù)個進(jìn)程共享。2.進(jìn)程控制-進(jìn)程的創(chuàng)建(1)派生進(jìn)程系統(tǒng)調(diào)用fork用于派生一個進(jìn)程,其說明如下:
#include<unistd.h>
pid_t
fork(void);
pid_t
vfork(void);調(diào)用fork時,系統(tǒng)創(chuàng)建一個與當(dāng)前進(jìn)程(父進(jìn)程)相同的新進(jìn)程(子進(jìn)程)。子進(jìn)程是父進(jìn)程的一個復(fù)制,子進(jìn)程拷貝父進(jìn)程的數(shù)據(jù)段、代碼段2.進(jìn)程控制-進(jìn)程的創(chuàng)建fork調(diào)用將執(zhí)行兩次返回,它將從父進(jìn)程和子進(jìn)程中分別返回。從父進(jìn)程返回時的返回值為子進(jìn)程的PID,而從子進(jìn)程返回時的返回值為0,并且返回都將執(zhí)行fork之后的語句。調(diào)用出錯時,返回值為-1#include<sys/types.h>#include<stdio.h>#include<unistd.h>intmain(){
pid_t
pid;
pid=fork();
if((pid)<0){
printf(“forkerror!\n”);exit(1);}elseif(pid=0)
printf(“childprocessisprinting.IDis%d\n”,getpid());else
printf(“parentprocessisprinting.IDis%d\n”,getpid());}拷貝代碼段的實例運行結(jié)果:
[root@lwm
liweimeng]#gccfork1.c-ofork1
[root@lwm
liweimeng]#./fork1
Iamthechildprocess,IDis4238
Iamtheparentprocess,IDis4237因為fork()函數(shù)用于從已存在的進(jìn)程中創(chuàng)建一個新的子進(jìn)程,在pid=fork();語句之前只有父進(jìn)程在運行,而在之后,父進(jìn)程和新創(chuàng)建的子進(jìn)程都在運行,子進(jìn)程拷貝父進(jìn)程的代碼段,所以子進(jìn)程中同樣有
if(pid<0)printf("errorinfork!");
elseif(pid==0)
printf("Iamthechildprocess,IDis%d\n",getpid());
else
printf("Iamtheparentprocess,IDis%d\n",getpid());2.進(jìn)程控制-進(jìn)程的創(chuàng)建#include<unistd.h>#include<unistd.h>intmain(){
pid_t
pid;
Intcount=0;
pid=fork();
Count++;
Printf(“count=%d\n”,count);Return0;}輸出:Count=1Count=1拷貝數(shù)據(jù)段的實例將被父子進(jìn)程各執(zhí)行一次,但是子進(jìn)程執(zhí)行時使自己的數(shù)據(jù)段里面的(這個數(shù)據(jù)段是從父進(jìn)程那copy過來的一模一樣)count+1,同樣父進(jìn)程執(zhí)行時使自己的數(shù)據(jù)段里面count+1,互不影響2.進(jìn)程控制-進(jìn)程的創(chuàng)建vfork的作用和fork基本相同,區(qū)別在于:vfork并不完全復(fù)制父進(jìn)程的數(shù)據(jù)段,而是和父進(jìn)程共享數(shù)據(jù)段。調(diào)用vfork對于父子進(jìn)程的執(zhí)行次序有所限制。調(diào)用vfork函數(shù)將使父進(jìn)程掛起,直至子進(jìn)程返回。vfork
保證子進(jìn)程先運行,在她調(diào)用exec或exit之后父進(jìn)程才可能被調(diào)度運行。#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
int
main(void)
{
pid_t
pid;
intcount=0;
pid=vfork();
if(pid==0)
{
count++;
_exit(0);}
else
count++;
printf("count=%d\n",count);return0;
}2.進(jìn)程控制-進(jìn)程的創(chuàng)建在子進(jìn)程調(diào)用exec或exit之前與父進(jìn)程數(shù)據(jù)是共享的,所以子進(jìn)程退出后把父進(jìn)程的數(shù)據(jù)段count改成1了,子進(jìn)程退出后,父進(jìn)程又執(zhí)行,最終就將count變成了2運行結(jié)果:
[root@lwm
liweimeng]#gccfork2.c-ofork2
[root@lwm
liweimeng]#./fork2
count=22.進(jìn)程控制-進(jìn)程的創(chuàng)建(2)創(chuàng)建執(zhí)行其他程序的進(jìn)程使用exec族的函數(shù)執(zhí)行新的程序,以新的子進(jìn)程完全替代原有的進(jìn)程。int
execl(constchar*pathname,constchar*arg,…);int
execlp(constchar*filename,conxtchar*arg,…);int
execle(constchar*pathname,conxtchar*arg,…,char#constencp[]);int
execv(constchar*pathname,char*constargv[]);int
execvp(constchar*filename,char*constargv[]);int
execve(constchar*pathname,char*constargv[],char*constenvp[]);2.進(jìn)程控制-進(jìn)程的創(chuàng)建exec函數(shù)族的特點用于啟動一個指定路徑和文件名的進(jìn)程。某進(jìn)程一旦調(diào)用了exec類函數(shù),正在執(zhí)行的程序結(jié)束,系統(tǒng)把代碼段換成新的程序的代碼,原有的數(shù)據(jù)段和堆棧段也被放棄,新的數(shù)據(jù)段和堆棧段被分配,但是進(jìn)程號被保留。結(jié)果為:系統(tǒng)認(rèn)為正在執(zhí)行的還是原來的進(jìn)程,但是進(jìn)程對應(yīng)的程序被替換了。2.進(jìn)程控制-進(jìn)程的創(chuàng)建fork和exec搭配實現(xiàn)讓父進(jìn)程的代碼執(zhí)行又啟動一個新的指定的進(jìn)程。execl()使用范例#include<stdio.h>#include<unistd.h>#include<sys/types.h>main(){
pid_t
pid;
printf("Nowonlyoneprocess\n");
printf("Callingfork...\n");
pid=fork();
if(pid==0)/*進(jìn)程為子進(jìn)程*/{
printf("Iamthechild\n");
execl("/bin/ls","-l",NULL);
/*如果execl返回,說明調(diào)用失敗*/
perror("execlfailedtorunls");exit(1);}2.進(jìn)程控制-進(jìn)程的創(chuàng)建elseif(pid>0)/*進(jìn)程為父進(jìn)程*/ {
printf("I'mtheparent,mychild'spidis%d\n",pid);
execl("/bin/ps","-c",NULL);/*如果execl返回,說明調(diào)用失敗*/
perror("execlfailedtorunls");exit(1);}else
printf("Forkfall!\n");}2.進(jìn)程控制-進(jìn)程的創(chuàng)建Clone此函數(shù)是fork的變形,對父子進(jìn)程的共享資源提供了更多的控制??梢允沟脛?chuàng)建的子進(jìn)程共享父進(jìn)程的資源。函數(shù)原型:int
clone(int(*fn)(void),void*child_stack,int
flags,void*arg)fn是函數(shù)指針,指向要執(zhí)行的函數(shù)child_stack子進(jìn)程堆棧段的指針flags用于不同繼承內(nèi)容的標(biāo)識2.進(jìn)程控制-進(jìn)程的創(chuàng)建Flags標(biāo)識含義CLONE_VM繼承父進(jìn)程的虛擬存儲器屬性CLONE_FS繼承父進(jìn)程的chroot
chdir
和umaskCLONE_FILES繼承父進(jìn)程的文件描述符CLONE_PID繼承父進(jìn)程的文件鎖、進(jìn)程號及時間片CLONE_SIGHAND繼承父進(jìn)程的信號處理程序Flags標(biāo)識的選取2.進(jìn)程控制-進(jìn)程的創(chuàng)建intvariable,fd;
int
do_something(){
variable=42;
close(fd);
_exit(0);
}
2.進(jìn)程控制-進(jìn)程的創(chuàng)建int
main(int
argc,char*argv[]){
void**child_stack;
chartempch;
variable=9;
fd=open("test.file",O_RDONLY);
child_stack=(void**)malloc(16384);
printf("Thevariablewas%d\n",variable);
clone(do_something,child_stack,CLONE_VM|CLONE_FILES,NULL);
sleep(1);/*延時以便子進(jìn)程完成關(guān)閉文件操作、修改變量*/
2.進(jìn)程控制-進(jìn)程的創(chuàng)建printf("Thevariableisnow%d\n",variable);
if(read(fd,&tempch,1)<1){
perror("FileReadError");
exit(1);
}
printf("Wecouldreadfromthefile\n");
return0;
}2.進(jìn)程控制-進(jìn)程的創(chuàng)建運行輸出:
Thevariableisnow42
FileReadError
程序的輸出結(jié)果說明,子進(jìn)程將文件關(guān)閉并將變量修改(調(diào)用clone時用到的CLONE_VM、CLONE_FILES標(biāo)志將使得變量和文件描述符表被共享),父進(jìn)程隨即就感覺到了,這就是clone的特點。
2.進(jìn)程控制-進(jìn)程的掛起Sleep函數(shù)調(diào)用sleep可以用來使進(jìn)程掛起指定的秒數(shù)。如果指定的掛起的時間到了,該調(diào)用的返回值為0;如果該函數(shù)調(diào)用被信號打斷,則返回剩余掛起的時間數(shù)(指定的時間減去已經(jīng)掛起的時間)函數(shù)原型unsignedint
sleep(unsigned
intseconds)2.進(jìn)程控制-進(jìn)程的等待WaitWait的作用是為發(fā)出調(diào)用的進(jìn)程只要有子進(jìn)程,就睡眠到他們中的一個終止為止。函數(shù)原型pid_t
wait(int*status);
//子進(jìn)程的結(jié)束狀態(tài)值會由參數(shù)status返回
pid_t
waitpid(pid_t
溫馨提示
- 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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年度國際美食節(jié)場地租賃合同范本2篇
- 2024年度斜屋面掛瓦工程驗收合同3篇
- 2024版企業(yè)信息化管理系統(tǒng)開發(fā)與實施合同2篇
- 2024全新綠色建筑場地租用合同范本2篇
- 2024年度委托房產(chǎn)中介購房代理及房產(chǎn)法律咨詢服務(wù)合同3篇
- 2024版冰箱品牌授權(quán)區(qū)域保護(hù)合同2篇
- 2024年特種物品冷凍運輸合同
- 2024版養(yǎng)老院家屬溝通與探視權(quán)益合同3篇
- 2024版夫妻共同債務(wù)處理與個人財產(chǎn)界定合同樣本3篇
- 2024版企業(yè)級saas系統(tǒng)定制開發(fā)與銷售合同3篇
- 《基于Halbach分布的初級永磁直線電機(jī)的電磁設(shè)計與分析》
- 光伏發(fā)電項目管理述職報告
- 2024年辦公室檔案管理工作總結(jié)模版(3篇)
- 2024-2025學(xué)年高一【數(shù)學(xué)(人教A版)】數(shù)學(xué)建模活動(1)-教學(xué)設(shè)計
- 2025年小學(xué)五年級數(shù)學(xué)(北京版)-分?jǐn)?shù)的意義(三)-3學(xué)習(xí)任務(wù)單
- 生物人教版(2024版)生物七年級上冊復(fù)習(xí)材料
- 中華人民共和國野生動物保護(hù)法
- 數(shù)字化轉(zhuǎn)型成熟度模型與評估(DTMM)國家標(biāo)準(zhǔn)解讀 2024
- 河南省名校八校聯(lián)考2024-2025學(xué)年高二上學(xué)期期中模擬考試語文試題(含答案解析)
- 第五單元觀察物體(一) (單元測試)-2024-2025學(xué)年二年級上冊數(shù)學(xué) 人教版
- 【初中生物】脊椎動物(魚)課件-2024-2025學(xué)年人教版(2024)生物七年級上冊
評論
0/150
提交評論