Linux 環(huán)境下的C語言編程_第1頁
Linux 環(huán)境下的C語言編程_第2頁
Linux 環(huán)境下的C語言編程_第3頁
Linux 環(huán)境下的C語言編程_第4頁
Linux 環(huán)境下的C語言編程_第5頁
已閱讀5頁,還剩98頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論