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

下載本文檔

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

文檔簡(jiǎn)介

linux操作系統(tǒng)下c語(yǔ)言編程入門[轉(zhuǎn)]-----非常經(jīng)典的文章linux操作系統(tǒng)下c語(yǔ)言編程入門

(一)目錄介紹

1)Linux程序設(shè)計(jì)入門--基礎(chǔ)知識(shí)

2)Linux程序設(shè)計(jì)入門--進(jìn)程介紹

3)Linux程序設(shè)計(jì)入門--文件操作

4)Linux程序設(shè)計(jì)入門--時(shí)間概念

5)Linux程序設(shè)計(jì)入門--信號(hào)處理

6)Linux程序設(shè)計(jì)入門--消息管理

7)Linux程序設(shè)計(jì)入門--線程操作

8)Linux程序設(shè)計(jì)入門--網(wǎng)絡(luò)編程

9)Linux下C開發(fā)工具介紹

(二)具體內(nèi)容

1)Linux程序設(shè)計(jì)入門--基礎(chǔ)知識(shí)

Linux下C語(yǔ)言編程基礎(chǔ)知識(shí)

前言:

這篇文章介紹在LINUX下進(jìn)行C語(yǔ)言編程所需要的基礎(chǔ)知識(shí).在這篇文章當(dāng)中,我們將

會(huì)學(xué)到以下內(nèi)容:

源程序編譯

Makefile的編寫

程序庫(kù)的鏈接

程序的調(diào)試

頭文件和系統(tǒng)求助

1.源程序的編譯

在Linux下面,如果要編譯一個(gè)C語(yǔ)言源程序,我們要使用GNU的gcc編譯器.下面我們

以一個(gè)實(shí)例來(lái)說(shuō)明如何使用gcc編譯器.

假設(shè)我們有下面一個(gè)非常簡(jiǎn)單的源程序(hello.c):

intmain(intargc,char**argv)

{

printf("HelloLinux/n");

}

要編譯這個(gè)程序,我們只要在命令行下執(zhí)行:

gcc-ohellohello.c

gcc編譯器就會(huì)為我們生成一個(gè)hello的可執(zhí)行文件.執(zhí)行./hello就可以看到程序的輸出結(jié)果了.命令行中g(shù)cc表示我們是用gcc來(lái)編譯我們的源程序,-o選項(xiàng)表示我們要求編譯器給我們輸出的可執(zhí)行文件名為hello而hello.c是我們的源程序文件.

gcc編譯器有許多選項(xiàng),一般來(lái)說(shuō)我們只要知道其中的幾個(gè)就夠了.-o選項(xiàng)我們已經(jīng)知道了,表示我們要求輸出的可執(zhí)行文件名.-c選項(xiàng)表示我們只要求編譯器輸出目標(biāo)代碼,而不必要輸出可執(zhí)行文件.-g選項(xiàng)表示我們要求編譯器在編譯的時(shí)候提供我們以后對(duì)程序進(jìn)行調(diào)試的信息.

知道了這三個(gè)選項(xiàng),我們就可以編譯我們自己所寫的簡(jiǎn)單的源程序了,如果你想要知道更多的選項(xiàng),可以查看gcc的幫助文檔,那里有著許多對(duì)其它選項(xiàng)的詳細(xì)說(shuō)明.

2.Makefile的編寫

假設(shè)我們有下面這樣的一個(gè)程序,源代碼如下:

/*main.c*/

#include"mytool1.h"

#include"mytool2.h"

intmain(intargc,char**argv)

{

mytool1_print("hello");

mytool2_print("hello");

}

/*mytool1.h*/

#ifndef_MYTOOL_1_H

#define_MYTOOL_1_H

voidmytool1_print(char*print_str);

#endif

/*mytool1.c*/

#include"mytool1.h"

voidmytool1_print(char*print_str)

{

printf("Thisismytool1print%s/n",print_str);

}

/*mytool2.h*/

#ifndef_MYTOOL_2_H

#define_MYTOOL_2_H

voidmytool2_print(char*print_str);

#endif

/*mytool2.c*/

#include"mytool2.h"

voidmytool2_print(char*print_str)

{

printf("Thisismytool2print%s/n",print_str);

}

當(dāng)然由于這個(gè)程序是很短的我們可以這樣來(lái)編譯

gcc-cmain.c

gcc-cmytool1.c

gcc-cmytool2.c

gcc-omainmain.omytool1.omytool2.o

這樣的話我們也可以產(chǎn)生main程序,而且也不是很麻煩.但是如果我們考慮一下如果有一天我們修改了其中的一個(gè)文件(比如說(shuō)mytool1.c)那么我們難道還要重新輸入上面的命令?也許你會(huì)說(shuō),這個(gè)很容易解決啊,我寫一個(gè)SHELL腳本,讓她幫我去完成不就可以了.是的

對(duì)于這個(gè)程序來(lái)說(shuō),是可以起到作用的.但是當(dāng)我們把事情想的更復(fù)雜一點(diǎn),如果我們的程

序有幾百個(gè)源程序的時(shí)候,難道也要編譯器重新一個(gè)一個(gè)的去編譯?

為此,聰明的程序員們想出了一個(gè)很好的工具來(lái)做這件事情,這就是make.我們只要執(zhí)行以

下make,就可以把上面的問(wèn)題解決掉.在我們執(zhí)行make之前,我們要先編寫一個(gè)非常重要的

文件.--Makefile.對(duì)于上面的那個(gè)程序來(lái)說(shuō),可能的一個(gè)Makefile的文件是:

#這是上面那個(gè)程序的Makefile文件

main:main.omytool1.omytool2.o

gcc-omainmain.omytool1.omytool2.o

main.o:main.cmytool1.hmytool2.h

gcc-cmain.c

mytool1.o:mytool1.cmytool1.h

gcc-cmytool1.c

mytool2.o:mytool2.cmytool2.h

gcc-cmytool2.c

有了這個(gè)Makefile文件,不過(guò)我們什么時(shí)候修改了源程序當(dāng)中的什么文件,我們只要執(zhí)行

make命令,我們的編譯器都只會(huì)去編譯和我們修改的文件有關(guān)的文件,其它的文件她連理

都不想去理的.

下面我們學(xué)習(xí)Makefile是如何編寫的.

在Makefile中也#開始的行都是注釋行.Makefile中最重要的是描述文件的依賴關(guān)系的說(shuō)

明.一般的格式是:

target:components

TABrule

第一行表示的是依賴關(guān)系.第二行是規(guī)則.

比如說(shuō)我們上面的那個(gè)Makefile文件的第二行

main:main.omytool1.omytool2.o

表示我們的目標(biāo)(target)main的依賴對(duì)象(components)是main.omytool1.omytool2.o

當(dāng)倚賴的對(duì)象在目標(biāo)修改后修改的話,就要去執(zhí)行規(guī)則一行所指定的命令.就象我們的上

面那個(gè)Makefile第三行所說(shuō)的一樣要執(zhí)行g(shù)cc-omainmain.omytool1.omytool2.o

注意規(guī)則一行中的TAB表示那里是一個(gè)TAB鍵

Makefile有三個(gè)非常有用的變量.分別是$@,___FCKpd___0gt;,___FCKpd___0lt;代表的意義分別是:

$@--目標(biāo)文件,$^--所有的依賴文件,___FCKpd___0lt;--第一個(gè)依賴文件.

如果我們使用上面三個(gè)變量,那么我們可以簡(jiǎn)化我們的Makefile文件為:

#這是簡(jiǎn)化后的Makefile

main:main.omytool1.omytool2.o

gcc-o$@___FCKpd___0gt;

main.o:main.cmytool1.hmytool2.h

gcc-c___FCKpd___0lt;

mytool1.o:mytool1.cmytool1.h

gcc-c___FCKpd___0lt;

mytool2.o:mytool2.cmytool2.h

gcc-c___FCKpd___0lt;

經(jīng)過(guò)簡(jiǎn)化后我們的Makefile是簡(jiǎn)單了一點(diǎn),不過(guò)人們有時(shí)候還想簡(jiǎn)單一點(diǎn).這里我們學(xué)習(xí)

一個(gè)Makefile的缺省規(guī)則

..c.o:

gcc-c___FCKpd___0lt;

這個(gè)規(guī)則表示所有的.o文件都是依賴與相應(yīng)的.c文件的.例如mytool.o依賴于mytool.c

這樣Makefile還可以變?yōu)?

#這是再一次簡(jiǎn)化后的Makefile

main:main.omytool1.omytool2.o

gcc-o$@$^

..c.o:

gcc-c___FCKpd___0lt;

好了,我們的Makefile也差不多了,如果想知道更多的關(guān)于Makefile規(guī)則可以查看相應(yīng)的

文檔.

3.程序庫(kù)的鏈接

試著編譯下面這個(gè)程序

/*temp.c*/

#include<math.h>

intmain(intargc,char**argv)

{

doublevalue;

printf("Value:%f/n",value);

}

這個(gè)程序相當(dāng)簡(jiǎn)單,但是當(dāng)我們用gcc-otemptemp.c編譯時(shí)會(huì)出現(xiàn)下面所示的錯(cuò)誤.

/tmp/cc33Kydu.o:Infunction`main':

/tmp/cc33Kydu.o(.text+0xe):undefinedreferenceto`log'

collect2:ldreturned1exitstatus

出現(xiàn)這個(gè)錯(cuò)誤是因?yàn)榫幾g器找不到log的具體實(shí)現(xiàn).雖然我們包括了正確的頭文件,但是我

們?cè)诰幾g的時(shí)候還是要連接確定的庫(kù).在Linux下,為了使用數(shù)學(xué)函數(shù),我們必須和數(shù)學(xué)庫(kù)連接,為此我們要加入-lm選項(xiàng).gcc-otemptemp.c-lm這樣才能夠正確的編譯.也許

有人要問(wèn),前面我們用printf函數(shù)的時(shí)候怎么沒有連接庫(kù)呢?是這樣的,對(duì)于一些常用的函

數(shù)的實(shí)現(xiàn),gcc編譯器會(huì)自動(dòng)去連接一些常用庫(kù),這樣我們就沒有必要自己去指定了.有時(shí)

候我們?cè)诰幾g程序的時(shí)候還要指定庫(kù)的路徑,這個(gè)時(shí)候我們要用到編譯器的-L選項(xiàng)指定

路徑.比如說(shuō)我們有一個(gè)庫(kù)在/home/hoyt/mylib下,這樣我們編譯的時(shí)候還要加上-L/home/hoyt/mylib.對(duì)于一些標(biāo)準(zhǔn)庫(kù)來(lái)說(shuō),我們沒有必要指出路徑.只要它們?cè)谄鹑笔?kù)的路

徑下就可以了.系統(tǒng)的缺省庫(kù)的路徑/lib/usr/lib/usr/local/lib在這三個(gè)路徑下面

的庫(kù),我們可以不指定路徑.

還有一個(gè)問(wèn)題,有時(shí)候我們使用了某個(gè)函數(shù),但是我們不知道庫(kù)的名字,這個(gè)時(shí)候怎么辦呢

?很抱歉,對(duì)于這個(gè)問(wèn)題我也不知道答案,我只有一個(gè)傻辦法.首先,我到標(biāo)準(zhǔn)庫(kù)路徑下面去

找看看有沒有和我用的函數(shù)相關(guān)的庫(kù),我就這樣找到了線程(thread)函數(shù)的庫(kù)文件(libpthread.a).當(dāng)然,如果找不到,只有一個(gè)笨方法.比如我要找sin這個(gè)函數(shù)所在的庫(kù).就只

好用nm-o/lib/*.so|grepsin>~/sin命令,然后看~/sin文件,到那里面去找了.在sin文件當(dāng)中,我會(huì)找到這樣的一行l(wèi)ibm-2.1.2.so:00009fa0Wsin這樣我就知道了sin在

libm-2.1.2.so庫(kù)里面,我用-lm選項(xiàng)就可以了(去掉前面的lib和后面的版本標(biāo)志,就剩

下m了所以是-lm).如果你知道怎么找,請(qǐng)趕快告訴我,我回非常感激的.謝謝!

4.程序的調(diào)試

我們編寫的程序不太可能一次性就會(huì)成功的,在我們的程序當(dāng)中,會(huì)出現(xiàn)許許多多我

們想不到的錯(cuò)誤,這個(gè)時(shí)候我們就要對(duì)我們的程序進(jìn)行調(diào)試了.

最常用的調(diào)試軟件是gdb.如果你想在圖形界面下調(diào)試程序,那么你現(xiàn)在可以選擇xxgdb.記

得要在編譯的時(shí)候加入-g選項(xiàng).關(guān)于gdb的使用可以看gdb的幫助文件.由于我沒有用過(guò)這

個(gè)軟件,所以我也不能夠說(shuō)出如何使用.不過(guò)我不喜歡用gdb.跟蹤一個(gè)程序是很煩的事情

,我一般用在程序當(dāng)中輸出中間變量的值來(lái)調(diào)試程序的.當(dāng)然你可以選擇自己的辦法,沒有

必要去學(xué)別人的.現(xiàn)在有了許多IDE環(huán)境,里面已經(jīng)自己帶了調(diào)試器了.你可以選擇幾個(gè)試

一試找出自己喜歡的一個(gè)用.

5.頭文件和系統(tǒng)求助

有時(shí)候我們只知道一個(gè)函數(shù)的大概形式,不記得確切的表達(dá)式,或者是不記得著函數(shù)

在那個(gè)頭文件進(jìn)行了說(shuō)明.這個(gè)時(shí)候我們可以求助系統(tǒng).

比如說(shuō)我們想知道fread這個(gè)函數(shù)的確切形式,我們只要執(zhí)行manfread系統(tǒng)就會(huì)輸出著

函數(shù)的詳細(xì)解釋的.和這個(gè)函數(shù)所在的頭文件<stdio.h>說(shuō)明了.如果我們要write這個(gè)函

數(shù)的說(shuō)明,當(dāng)我們執(zhí)行manwrite時(shí),輸出的結(jié)果卻不是我們所需要的.因?yàn)槲覀円氖莣rite這個(gè)函數(shù)的說(shuō)明,可是出來(lái)的卻是write這個(gè)命令的說(shuō)明.為了得到write的函數(shù)說(shuō)明

我們要用man2write.2表示我們用的write這個(gè)函數(shù)是系統(tǒng)調(diào)用函數(shù),還有一個(gè)我們常

用的是3表示函數(shù)是C的庫(kù)函數(shù).

記住不管什么時(shí)候,man都是我們的最好助手.

------------------------------------------------------------------------

好了,這一章就講這么多了,有了這些知識(shí)我們就可以進(jìn)入激動(dòng)人心的Linux下的C程序探險(xiǎn)活動(dòng).

2)Linux程序設(shè)計(jì)入門--進(jìn)程介紹

Linux下進(jìn)程的創(chuàng)建

前言:

這篇文章是用來(lái)介紹在Linux下和進(jìn)程相關(guān)的各個(gè)概念.我們將會(huì)學(xué)到:

進(jìn)程的概念

進(jìn)程的身份

進(jìn)程的創(chuàng)建

守護(hù)進(jìn)程的創(chuàng)建

1。進(jìn)程的概念

Linux操作系統(tǒng)是面向多用戶的.在同一時(shí)間可以有許多用戶向操作系統(tǒng)發(fā)出各種命令.那么操作系統(tǒng)是怎么實(shí)現(xiàn)多用戶的環(huán)境呢?在現(xiàn)代的操作系統(tǒng)里面,都有程序和進(jìn)程的概念.那么什么是程序,什么是進(jìn)程呢?通俗的講程序是一個(gè)包含可以執(zhí)行代碼的文件,是一個(gè)靜態(tài)的文件.而進(jìn)程是一個(gè)開始執(zhí)行但是還沒有結(jié)束的程序的實(shí)例.就是可執(zhí)行文件的具體實(shí)現(xiàn).一個(gè)程序可能有許多進(jìn)程,而每一個(gè)進(jìn)程又可以有許多子進(jìn)程.依次循環(huán)下去,而產(chǎn)生子孫進(jìn)程.當(dāng)程序被系統(tǒng)調(diào)用到內(nèi)存以后,系統(tǒng)會(huì)給程序分配一定的資源(內(nèi)存,設(shè)備等等)然后進(jìn)行一系列的復(fù)雜操作,使程序變成進(jìn)程以供系統(tǒng)調(diào)用.在系統(tǒng)里面只有進(jìn)程沒有程序,為了區(qū)分各個(gè)不同的進(jìn)程,系統(tǒng)給每一個(gè)進(jìn)程分配了一個(gè)ID(就象我們的身份證)以便識(shí)別.為了充分的利用資源,系統(tǒng)還對(duì)進(jìn)程區(qū)分了不同的狀態(tài).將進(jìn)程分為新

建,運(yùn)行,阻塞,就緒和完成五個(gè)狀態(tài).新建表示進(jìn)程正在被創(chuàng)建,運(yùn)行是進(jìn)程正在運(yùn)行,阻塞是進(jìn)程正在等待某一個(gè)事件發(fā)生,就緒是表示系統(tǒng)正在等待CPU來(lái)執(zhí)行命令,而完成表示進(jìn)程已經(jīng)結(jié)束了系統(tǒng)正在回收資源.關(guān)于進(jìn)程五個(gè)狀態(tài)的詳細(xì)解說(shuō)我們可以看《操作系統(tǒng)》上面有詳細(xì)的解說(shuō)。

2。進(jìn)程的標(biāo)志

上面我們知道了進(jìn)程都有一個(gè)ID,那么我們?cè)趺吹玫竭M(jìn)程的ID呢?系統(tǒng)調(diào)用getpid可以得到進(jìn)程的ID,而getppid可以得到父進(jìn)程(創(chuàng)建調(diào)用該函數(shù)進(jìn)程的進(jìn)程)的ID.

#include<unistd>

pid_tgetpid(void);

pid_tgetppid(void);

進(jìn)程是為程序服務(wù)的,而程序是為了用戶服務(wù)的.系統(tǒng)為了找到進(jìn)程的用戶名,還為進(jìn)程和用戶建立聯(lián)系.這個(gè)用戶稱為進(jìn)程的所有者.相應(yīng)的每一個(gè)用戶也有一個(gè)用戶ID.通過(guò)系統(tǒng)調(diào)用getuid可以得到進(jìn)程的所有者的ID.由于進(jìn)程要用到一些資源,而Linux對(duì)系統(tǒng)資源是進(jìn)行保護(hù)的,為了獲取一定資源進(jìn)程還有一個(gè)有效用戶ID.這個(gè)ID和系統(tǒng)的資源使用有關(guān),涉及到進(jìn)程的權(quán)限.通過(guò)系統(tǒng)調(diào)用geteuid我們可以得到進(jìn)程的有效用戶ID.和用戶ID相對(duì)應(yīng)進(jìn)程還有一個(gè)組ID和有效組ID系統(tǒng)調(diào)用getgid和getegid可以分別得到組ID和有效組ID

#include<unistd>

#include<sys/types.h>

uid_tgetuid(void);

uid_tgeteuid(void);

gid_tgetgid(void);

git_tgetegid(void);

有時(shí)候我們還會(huì)對(duì)用戶的其他信息感興趣(登錄名等等),這個(gè)時(shí)候我們可以調(diào)用getpwuid來(lái)得到.

structpasswd{

char*pw_name;/*登錄名稱*/

char*pw_passwd;/*登錄口令*/

uid_tpw_uid;/*用戶ID*/

gid_tpw_gid;/*用戶組ID*/

char*pw_gecos;/*用戶的真名*/

char*pw_dir;/*用戶的目錄*/

char*pw_shell;/*用戶的SHELL*/

};

#include<pwd.h>

#include<sys/types.h>

structpasswd*getpwuid(uid_tuid);

下面我們學(xué)習(xí)一個(gè)實(shí)例來(lái)實(shí)踐一下上面我們所學(xué)習(xí)的幾個(gè)函數(shù):

#include<unistd.h>

#include<pwd.h>

#include<sys/types.h>

#include<stdio.h>

intmain(intargc,char**argv)

{

pid_tmy_pid,parent_pid;

uid_tmy_uid,my_euid;

gid_tmy_gid,my_egid;

structpasswd*my_info;

my_pid=getpid();

parent_pid=getppid();

my_uid=getuid();

my_euid=geteuid();

my_gid=getgid();

my_egid=getegid();

my_info=getpwuid(my_uid);

printf("ProcessID:%ld/n",my_pid);

printf("ParentID:%ld/n",parent_pid);

printf("UserID:%ld/n",my_uid);

printf("EffectiveUserID:%ld/n",my_euid);

printf("GroupID:%ld/n",my_gid);

printf("EffectiveGroupID:%ld/n",my_egid);

if(my_info)

{

printf("MyLoginName:%s/n",my_info->pw_name);

printf("MyPassword:%s/n",my_info->pw_passwd);

printf("MyUserID:%ld/n",my_info->pw_uid);

printf("MyGroupID:%ld/n",my_info->pw_gid);

printf("MyRealName:%s/n",my_info->pw_gecos);

printf("MyHomeDir:%s/n",my_info->pw_dir);

printf("MyWorkShell:%s/n",my_info->pw_shell);

}

}

3。進(jìn)程的創(chuàng)建

創(chuàng)建一個(gè)進(jìn)程的系統(tǒng)調(diào)用很簡(jiǎn)單.我們只要調(diào)用fork函數(shù)就可以了.

#include<unistd.h>

pid_tfork();

當(dāng)一個(gè)進(jìn)程調(diào)用了fork以后,系統(tǒng)會(huì)創(chuàng)建一個(gè)子進(jìn)程.這個(gè)子進(jìn)程和父進(jìn)程不同的地方只有他的進(jìn)程ID和父進(jìn)程ID,其他的都是一樣.就象符進(jìn)程克隆(clone)自己一樣.當(dāng)然創(chuàng)建兩個(gè)一模一樣的進(jìn)程是沒有意義的.為了區(qū)分父進(jìn)程和子進(jìn)程,我們必須跟蹤fork的返回值.當(dāng)fork掉用失敗的時(shí)候(內(nèi)存不足或者是用戶的最大進(jìn)程數(shù)已到)fork返回-1,否則fork的返回值有重要的作用.對(duì)于父進(jìn)程fork返回子進(jìn)程的ID,而對(duì)于fork子進(jìn)程返回0.我們就是根據(jù)這個(gè)返回值來(lái)區(qū)分父子進(jìn)程的.父進(jìn)程為什么要?jiǎng)?chuàng)建子進(jìn)程呢?前面我們已經(jīng)說(shuō)過(guò)了Linux是一個(gè)多用戶操作系統(tǒng),在同一時(shí)間會(huì)有許多的用戶在爭(zhēng)奪系統(tǒng)的資源.有時(shí)進(jìn)程為了早一點(diǎn)完成任務(wù)就創(chuàng)建子進(jìn)程來(lái)爭(zhēng)奪資源.一旦子進(jìn)程被創(chuàng)建,父子進(jìn)程一起從fork處繼續(xù)執(zhí)行,相互競(jìng)爭(zhēng)系統(tǒng)的資源.有時(shí)候我們希望子進(jìn)程繼續(xù)執(zhí)行,而父進(jìn)程阻塞直到子進(jìn)程完成任務(wù).這個(gè)時(shí)候我們可以調(diào)用wait或者waitpid系統(tǒng)調(diào)用.

#include<sys/types.h>

#include<sys/wait.h>

pid_twait(int*stat_loc);

pid_twaitpid(pid_tpid,int*stat_loc,intoptions);

wait系統(tǒng)調(diào)用會(huì)使父進(jìn)程阻塞直到一個(gè)子進(jìn)程結(jié)束或者是父進(jìn)程接受到了一個(gè)信號(hào).如果沒有父進(jìn)程沒有子進(jìn)程或者他的子進(jìn)程已經(jīng)結(jié)束了wait回立即返回.成功時(shí)(因一個(gè)子進(jìn)程結(jié)束)wait將返回子進(jìn)程的ID,否則返回-1,并設(shè)置全局變量errno.stat_loc是子進(jìn)程的退出狀態(tài).子進(jìn)程調(diào)用exit,_exit或者是return來(lái)設(shè)置這個(gè)值.為了得到這個(gè)值Linux定義了幾個(gè)宏來(lái)測(cè)試這個(gè)返回值.

WIFEXITED:判斷子進(jìn)程退出值是非0

WEXITSTATUS:判斷子進(jìn)程的退出值(當(dāng)子進(jìn)程退出時(shí)非0).

WIFSIGNALED:子進(jìn)程由于有沒有獲得的信號(hào)而退出.

WTERMSIG:子進(jìn)程沒有獲得的信號(hào)號(hào)(在WIFSIGNALED為真時(shí)才有意義).

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

#include<unistd.h>

intexecl(constchar*path,constchar*arg,...);

intexeclp(constchar*file,constchar*arg,...);

intexecle(constchar*path,constchar*arg,...);

intexecv(constchar*path,char*constargv[]);

intexecvp(constchar*file,char*constargv[]):

exec族調(diào)用可以執(zhí)行給定程序.關(guān)于exec族調(diào)用的詳細(xì)解說(shuō)可以參考系統(tǒng)手冊(cè)(manexec

l).下面我們來(lái)學(xué)習(xí)一個(gè)實(shí)例.注意編譯的時(shí)候要加-lm以便連接數(shù)學(xué)函數(shù)庫(kù).

#include<unistd.h>

#include<sys/types.h>

#include<sys/wait.h>

#include<stdio.h>

#include<errno.h>

#include<math.h>

voidmain(void)

{

pid_tchild;

intstatus;

printf("Thiswilldemostratehowtogetchildstatus/n");

if((child=fork())==-1)

{

printf("ForkError:%s/n",strerror(errno));

exit(1);

}

elseif(child==0)

{

inti;

printf("Iamthechild:%ld/n",getpid());

for(i=0;i<1000000;i++)sin(i);

i=5;

printf("Iexitwith%d/n",i);

exit(i);

}

while(((child=wait(&status))==-1)&(errno==EINTR));

if(child==-1)

printf("WaitError:%s/n",strerror(errno));

elseif(!status)

printf("Child%ldterminatednormallyreturnstatusiszero/n",

child);

elseif(WIFEXITED(status))

printf("Child%ldterminatednormallyreturnstatusis%d/n",

child,WEXITSTATUS(status));

elseif(WIFSIGNALED(status))

printf("Child%ldterminatedduetosignal%dznotcaught/n",

child,WTERMSIG(status));

}

strerror函數(shù)會(huì)返回一個(gè)指定的錯(cuò)誤號(hào)的錯(cuò)誤信息的字符串.

4。守護(hù)進(jìn)程的創(chuàng)建

如果你在DOS時(shí)代編寫過(guò)程序,那么你也許知道在DOS下為了編寫一個(gè)常駐內(nèi)存的程序我們要編寫多少代碼了.相反如果在Linux下編寫一個(gè)"常駐內(nèi)存"的程序卻是很容易的.我們只要幾行代碼就可以做到.實(shí)際上由于Linux是多任務(wù)操作系統(tǒng),我們就是不編寫代碼也可以把一個(gè)程序放到后臺(tái)去執(zhí)行的.我們只要在命令后面加上&符號(hào),SHELL就會(huì)把我們的程序放到后臺(tái)去運(yùn)行的.這里我們"開發(fā)"一個(gè)后臺(tái)檢查郵件的程序.這個(gè)程序每過(guò)一個(gè)指定的時(shí)間回去檢查我們的郵箱,如果發(fā)現(xiàn)我們有郵件了,會(huì)不斷的報(bào)警(通過(guò)機(jī)箱上的小喇叭來(lái)發(fā)出聲音).后面有這個(gè)函數(shù)的加強(qiáng)版本,加強(qiáng)版本后臺(tái)進(jìn)程的創(chuàng)建思想:首先父進(jìn)程創(chuàng)建一個(gè)子進(jìn)程.然后子進(jìn)程殺死父進(jìn)程(是不是很無(wú)情?).信號(hào)處理所有的工作由子進(jìn)程來(lái)處理.

#include<unistd.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<stdio.h>

#include<errno.h>

#include<fcntl.h>

#include<signal.h>

/*Linux的默任個(gè)人的郵箱地址是/var/spool/mail/用戶的登錄名*/

#defineMAIL"/var/spool/mail/hoyt"

/*睡眠10秒鐘*/

#defineSLEEP_TIME10

main(void)

{

pid_tchild;

if((child=fork())==-1)

{

printf("ForkError:%s/n",strerror(errno));

exit(1);

}

elseif(child>0)

while(1);

if(kill(getppid(),SIGTERM)==-1)

{

printf("KillParentError:%s/n",strerror(errno));

exit(1);

}

{

intmailfd;

while(1)

{

if((mailfd=open(MAIL,O_RDONLY))!=-1)

{

fprintf(stderr,"%s","/007");

close(mailfd);

}

sleep(SLEEP_TIME);

}

}

}

你可以在默認(rèn)的路徑下創(chuàng)建你的郵箱文件,然后測(cè)試一下這個(gè)程序.當(dāng)然這個(gè)程序還有很多地方要改善的.我們后面會(huì)對(duì)這個(gè)小程序改善的,再看我的改善之前你可以嘗試自己改善一下.比如讓用戶指定郵相的路徑和睡眠時(shí)間等等.相信自己可以做到的.動(dòng)手吧,勇敢的探險(xiǎn)者.

好了進(jìn)程一節(jié)的內(nèi)容我們就先學(xué)到這里了.進(jìn)程是一個(gè)非常重要的概念,許多的程序都會(huì)用子進(jìn)程.創(chuàng)建一個(gè)子進(jìn)程是每一個(gè)程序員的基本要求!

3)Linux程序設(shè)計(jì)入門--文件操作

Linux下文件的操作

前言:

我們?cè)谶@一節(jié)將要討論linux下文件操作的各個(gè)函數(shù).

文件的創(chuàng)建和讀寫

文件的各個(gè)屬性

目錄文件的操作

管道文件

1。文件的創(chuàng)建和讀寫

我假設(shè)你已經(jīng)知道了標(biāo)準(zhǔn)級(jí)的文件操作的各個(gè)函數(shù)(fopen,fread,fwrite等等).當(dāng)然如果你不清楚的話也不要著急.我們討論的系統(tǒng)級(jí)的文件操作實(shí)際上是為標(biāo)準(zhǔn)級(jí)文件操作服務(wù)的.

當(dāng)我們需要打開一個(gè)文件進(jìn)行讀寫操作的時(shí)候,我們可以使用系統(tǒng)調(diào)用函數(shù)open.使用完成以后我們調(diào)用另外一個(gè)close函數(shù)進(jìn)行關(guān)閉操作.

#include<fcntl.h>

#include<unistd.h>

#include<sys/types.h>

#include<sys/stat.h>

intopen(constchar*pathname,intflags);

intopen(constchar*pathname,intflags,mode_tmode);

intclose(intfd);

open函數(shù)有兩個(gè)形式.其中pathname是我們要打開的文件名(包含路徑名稱,缺省是認(rèn)為在當(dāng)前路徑下面).flags可以去下面的一個(gè)值或者是幾個(gè)值的組合.

O_RDONLY:以只讀的方式打開文件.

O_WRONLY:以只寫的方式打開文件.

O_RDWR:以讀寫的方式打開文件.

O_APPEND:以追加的方式打開文件.

O_CREAT:創(chuàng)建一個(gè)文件.

O_EXEC:如果使用了O_CREAT而且文件已經(jīng)存在,就會(huì)發(fā)生一個(gè)錯(cuò)誤.

O_NOBLOCK:以非阻塞的方式打開一個(gè)文件.

O_TRUNC:如果文件已經(jīng)存在,則刪除文件的內(nèi)容.

前面三個(gè)標(biāo)志只能使用任意的一個(gè).如果使用了O_CREATE標(biāo)志,那么我們要使用open的第二種形式.還要指定mode標(biāo)志,用來(lái)表示文件的訪問(wèn)權(quán)限.mode可以是以下情況的組合.

-----------------------------------------------------------------

S_IRUSR用戶可以讀S_IWUSR用戶可以寫

S_IXUSR用戶可以執(zhí)行S_IRWXU用戶可以讀寫執(zhí)行

-----------------------------------------------------------------

S_IRGRP組可以讀S_IWGRP組可以寫

S_IXGRP組可以執(zhí)行S_IRWXG組可以讀寫執(zhí)行

-----------------------------------------------------------------

S_IROTH其他人可以讀S_IWOTH其他人可以寫

S_IXOTH其他人可以執(zhí)行S_IRWXO其他人可以讀寫執(zhí)行

-----------------------------------------------------------------

S_ISUID設(shè)置用戶執(zhí)行IDS_ISGID設(shè)置組的執(zhí)行ID

-----------------------------------------------------------------

我們也可以用數(shù)字來(lái)代表各個(gè)位的標(biāo)志.Linux總共用5個(gè)數(shù)字來(lái)表示文件的各種權(quán)限.

00000.第一位表示設(shè)置用戶ID.第二位表示設(shè)置組ID,第三位表示用戶自己的權(quán)限位,第四位表示組的權(quán)限,最后一位表示其他人的權(quán)限.

每個(gè)數(shù)字可以取1(執(zhí)行權(quán)限),2(寫權(quán)限),4(讀權(quán)限),0(什么也沒有)或者是這幾個(gè)值的和

..

比如我們要?jiǎng)?chuàng)建一個(gè)用戶讀寫執(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會(huì)返回一個(gè)文件描述符.我們以后對(duì)文件的所有操作就可以對(duì)這個(gè)文件描述符進(jìn)行操作了.

當(dāng)我們操作完成以后,我們要關(guān)閉文件了,只要調(diào)用close就可以了,其中fd是我們要關(guān)閉的文件描述符.

文件打開了以后,我們就要對(duì)文件進(jìn)行讀寫了.我們可以調(diào)用函數(shù)read和write進(jìn)行文件的讀寫.

#include<unistd.h>

ssize_tread(intfd,void*buffer,size_tcount);

ssize_twrite(intfd,constvoid*buffer,size_tcount);

fd是我們要進(jìn)行讀寫操作的文件描述符,buffer是我們要寫入文件內(nèi)容或讀出文件內(nèi)容的內(nèi)存地址.count是我們要讀寫的字節(jié)數(shù).

對(duì)于普通的文件read從指定的文件(fd)中讀取count字節(jié)到buffer緩沖區(qū)中(記住我們必須提供一個(gè)足夠大的緩沖區(qū)),同時(shí)返回count.

如果read讀到了文件的結(jié)尾或者被一個(gè)信號(hào)所中斷,返回值會(huì)小于count.如果是由信號(hào)中斷引起返回,而且沒有返回?cái)?shù)據(jù),read會(huì)返回-1,且設(shè)置errno為EINTR.當(dāng)程序讀到了文件結(jié)尾的時(shí)候,read會(huì)返回0.

write從buffer中寫count字節(jié)到文件fd中,成功時(shí)返回實(shí)際所寫的字節(jié)數(shù).

下面我們學(xué)習(xí)一個(gè)實(shí)例,這個(gè)實(shí)例用來(lái)拷貝文件.

#include<unistd.h>

#include<fcntl.h>

#include<stdio.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<errno.h>

#include<string.h>

#defineBUFFER_SIZE1024

intmain(intargc,char**argv)

{

intfrom_fd,to_fd;

intbytes_read,bytes_write;

charbuffer[BUFFER_SIZE];

char*ptr;

if(argc!=3)

{

fprintf(stderr,"Usage:%sfromfiletofile/n/a",argv[0]);

exit(1);

}

/*打開源文件*/

if((from_fd=open(argv[1],O_RDONLY))==-1)

{

fprintf(stderr,"Open%sError:%s/n",argv[1],strerror(errno));

exit(1);

}

/*創(chuàng)建目的文件*/

if((to_fd=open(argv[2],O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1)

{

fprintf(stderr,"Open%sError:%s/n",argv[2],strerror(errno));

exit(1);

}

/*以下代碼是一個(gè)經(jīng)典的拷貝文件的代碼*/

while(bytes_read=read(from_fd,buffer,BUFFER_SIZE))

{

/*一個(gè)致命的錯(cuò)誤發(fā)生了*/

if((bytes_read==-1)&&(errno!=EINTR))break;

elseif(bytes_read>0)

{

ptr=buffer;

while(bytes_write=write(to_fd,ptr,bytes_read))

{

/*一個(gè)致命錯(cuò)誤發(fā)生了*/

if((bytes_write==-1)&&(errno!=EINTR))break;

/*寫完了所有讀的字節(jié)*/

elseif(bytes_write==bytes_read)break;

/*只寫了一部分,繼續(xù)寫*/

elseif(bytes_write>0)

{

ptr+=bytes_write;

bytes_read-=bytes_write;

}

}

/*寫的時(shí)候發(fā)生的致命錯(cuò)誤*/

if(bytes_write==-1)break;

}

}

close(from_fd);

close(to_fd);

exit(0);

}

2。文件的各個(gè)屬性

文件具有各種各樣的屬性,除了我們上面所知道的文件權(quán)限以外,文件還有創(chuàng)建時(shí)間,大小等等屬性.

有時(shí)侯我們要判斷文件是否可以進(jìn)行某種操作(讀、寫等等).這個(gè)時(shí)候我們可以使用access函數(shù).

#include<unistd.h>

intaccess(constchar*pathname,intmode);

pathname:是文件名稱,mode是我們要判斷的屬性.可以取以下值或者是他們的組合.

R_OK文件可以讀,W_OK文件可以寫,X_OK文件可以執(zhí)行,F_OK文件存在.當(dāng)我們測(cè)試成功時(shí),函數(shù)返回0,否則如果有一個(gè)條件不符時(shí),返回-1.

如果我們要獲得文件的其他屬性,我們可以使用函數(shù)stat或者fstat.

#include<sys/stat.h>

#include<unistd.h>

intstat(constchar*file_name,structstat*buf);

intfstat(intfiledes,structstat*buf);

structstat{

dev_tst_dev;/*設(shè)備*/

ino_tst_ino;/*節(jié)點(diǎn)*/

mode_tst_mode;/*模式*/

nlink_tst_nlink;/*硬連接*/

uid_tst_uid;/*用戶ID*/

gid_tst_gid;/*組ID*/

dev_tst_rdev;/*設(shè)備類型*/

off_tst_off;/*文件字節(jié)數(shù)*/

unsignedlongst_blksize;/*塊大小*/

unsignedlongst_blocks;/*塊數(shù)*/

time_tst_atime;/*最后一次訪問(wèn)時(shí)間*/

time_tst_mtime;/*最后一次修改時(shí)間*/

time_tst_ctime;/*最后一次改變時(shí)間(指屬性)*/

};

stat用來(lái)判斷沒有打開的文件,而fstat用來(lái)判斷打開的文件.我們使用最多的屬性是st_mode.通過(guò)著屬性我們可以判斷給定的文件是一個(gè)普通文件還是一個(gè)目錄,連接等等.可以使用下面幾個(gè)宏來(lái)判斷.

S_ISLNK(st_mode):是否是一個(gè)連接.S_ISREG是否是一個(gè)常規(guī)文件.S_ISDIR是否是一個(gè)目錄S_ISCHR是否是一個(gè)字符設(shè)備.S_ISBLK是否是一個(gè)塊設(shè)備S_ISFIFO是否是一個(gè)FIFO文件.S_ISSOCK是否是一個(gè)SOCKET文件.我們會(huì)在下面說(shuō)明如何使用這幾個(gè)宏的.

3。目錄文件的操作

在我們編寫程序的時(shí)候,有時(shí)候會(huì)要得到我們當(dāng)前的工作路徑。C庫(kù)函數(shù)提供了getcwd來(lái)解決這個(gè)問(wèn)題。

#include<unistd.h>

char*getcwd(char*buffer,size_tsize);

我們提供一個(gè)size大小的buffer,getcwd會(huì)把我們當(dāng)前的路徑考到buffer中.如果buffer太小,函數(shù)會(huì)返回-1和一個(gè)錯(cuò)誤號(hào).

Linux提供了大量的目錄操作函數(shù),我們學(xué)習(xí)幾個(gè)比較簡(jiǎn)單和常用的函數(shù).

#include<dirent.h>

#include<unistd.h>

#include<fcntl.h>

#include<sys/types.h>

#include<sys/stat.h>

intmkdir(constchar*path,mode_tmode);

DIR*opendir(constchar*path);

structdirent*readdir(DIR*dir);

voidrewinddir(DIR*dir);

off_ttelldir(DIR*dir);

voidseekdir(DIR*dir,off_toff);

intclosedir(DIR*dir);

structdirent{

longd_ino;

off_td_off;

unsignedshortd_reclen;

chard_name[NAME_MAX+1];/*文件名稱*/

mkdir很容易就是我們創(chuàng)建一個(gè)目錄,opendir打開一個(gè)目錄為以后讀做準(zhǔn)備.readdir讀一個(gè)打開的目錄.rewinddir是用來(lái)重讀目錄的和我們學(xué)的rewind函數(shù)一樣.closedir是關(guān)閉一個(gè)目錄.telldir和seekdir類似與ftee和fseek函數(shù).

下面我們開發(fā)一個(gè)小程序,這個(gè)程序有一個(gè)參數(shù).如果這個(gè)參數(shù)是一個(gè)文件名,我們輸出這個(gè)文件的大小和最后修改的時(shí)間,如果是一個(gè)目錄我們輸出這個(gè)目錄下所有文件的大小和修改時(shí)間.

#include<unistd.h>

#include<stdio.h>

#include<errno.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<dirent.h>

#include<time.h>

staticintget_file_size_time(constchar*filename)

{

structstatstatbuf;

if(stat(filename,&statbuf)==-1)

{

printf("Getstaton%sError:%s/n",

filename,strerror(errno));

return(-1);

}

if(S_ISDIR(statbuf.st_mode))return(1);

if(S_ISREG(statbuf.st_mode))

printf("%ssize:%ldbytes/tmodifiedat%s",

filename,statbuf.st_size,ctime(&statbuf.st_mtime));

return(0);

}

intmain(intargc,char**argv)

{

DIR*dirp;

structdirent*direntp;

intstats;

if(argc!=2)

{

printf("Usage:%sfilename/n/a",argv[0]);

exit(1);

}

if(((stats=get_file_size_time(argv[1]))==0)||(stats==-1))exit(1);

if((dirp=opendir(argv[1]))==NULL)

{

printf("OpenDirectory%sError:%s/n",

argv[1],strerror(errno));

exit(1);

}

while((direntp=readdir(dirp))!=NULL)

if(get_file_size_time(direntp-<d_name)==-1)break;

closedir(dirp);

exit(1);

}

4。管道文件

Linux提供了許多的過(guò)濾和重定向程序,比如morecat

等等.還提供了<>|<<等等重定向操作符.在這些過(guò)濾和重定向程序當(dāng)中,都用到了管

道這種特殊的文件.系統(tǒng)調(diào)用pipe可以創(chuàng)建一個(gè)管道.

#include<unistd.h>

intpipe(intfildes[2]);

pipe調(diào)用可以創(chuàng)建一個(gè)管道(通信緩沖區(qū)).當(dāng)調(diào)用成功時(shí),我們可以訪問(wèn)文件描述符fildes[0],fildes[1].其中fildes[0]是用來(lái)讀的文件描述符,而fildes[1]是用來(lái)寫的文件描述符.

在實(shí)際使用中我們是通過(guò)創(chuàng)建一個(gè)子進(jìn)程,然后一個(gè)進(jìn)程寫,一個(gè)進(jìn)程讀來(lái)使用的.

關(guān)于進(jìn)程通信的詳細(xì)情況請(qǐng)查看進(jìn)程通信

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<string.h>

#include<errno.h>

#include<sys/types.h>

#include<sys/wait.h>

#defineBUFFER255

intmain(intargc,char**argv)

{

charbuffer[BUFFER+1];

intfd[2];

if(argc!=2)

{

fprintf(stderr,"Usage:%sstring/n/a",argv[0]);

exit(1);

}

if(pipe(fd)!=0)

{

fprintf(stderr,"PipeError:%s/n/a",strerror(errno));

exit(1);

}

if(fork()==0)

{

close(fd[0]);

printf("Child[%d]Writetopipe/n/a",getpid());

snprintf(buffer,BUFFER,"%s",argv[1]);

write(fd[1],buffer,strlen(buffer));

printf("Child[%d]Quit/n/a",getpid());

exit(0);

}

else

{

close(fd[1]);

printf("Parent[%d]Readfrompipe/n/a",getpid());

memset(buffer,'/0',BUFFER+1);

read(fd[0],buffer,BUFFER);

printf("Parent[%d]Read:%s/n",getpid(),buffer);

exit(1);

}

}

為了實(shí)現(xiàn)重定向操作,我們需要調(diào)用另外一個(gè)函數(shù)dup2.

#include<unistd.h>

intdup2(intoldfd,intnewfd);

dup2將用oldfd文件描述符來(lái)代替newfd文件描述符,同時(shí)關(guān)閉newfd文件描述符.也就是說(shuō),所有向newfd操作都轉(zhuǎn)到oldfd上面.下面我們學(xué)習(xí)一個(gè)例子,這個(gè)例子將標(biāo)準(zhǔn)輸出重定向到一個(gè)文件.

#include<unistd.h>

#include<stdio.h>

#include<errno.h>

#include<fcntl.h>

#include<string.h>

#include<sys/types.h>

#include<sys/stat.h>

#defineBUFFER_SIZE1024

intmain(intargc,char**argv)

{

intfd;

charbuffer[BUFFER_SIZE];

if(argc!=2)

{

fprintf(stderr,"Usage:%soutfilename/n/a",argv[0]);

exit(1);

}

if((fd=open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR))==-1)

{

fprintf(stderr,"Open%sError:%s/n/a",argv[1],strerror(errno));

exit(1);

}

if(dup2(fd,STDOUT_FILENO)==-1)

{

fprintf(stderr,"RedirectStandardOutError:%s/n/a",strerror(errno));

exit(1);

}

fprintf(stderr,"Now,pleaseinputstring");

fprintf(stderr,"(ToquituseCTRL+D)/n");

while(1)

{

fgets(buffer,BUFFER_SIZE,stdin);

if(feof(stdin))break;

write(STDOUT_FILENO,buffer,strlen(buffer));

}

exit(0);

}

好了,文件一章我們就暫時(shí)先討論到這里,學(xué)習(xí)好了文件的操作我們其實(shí)已經(jīng)可以寫出一些比較有用的程序了.我們可以編寫一個(gè)實(shí)現(xiàn)例如dir,mkdir,cp,mv等等常用的文件操作命令了.

想不想自己寫幾個(gè)試一試呢?

4)程序設(shè)計(jì)入門--時(shí)間概念

前言:Linux下的時(shí)間概念

這一章我們學(xué)習(xí)Linux的時(shí)間表示和計(jì)算函數(shù)時(shí)間的表示時(shí)間的測(cè)量計(jì)時(shí)器的使用

1。時(shí)間表示在程序當(dāng)中,我們經(jīng)常要輸出系統(tǒng)當(dāng)前的時(shí)間,比如我們使用date命令的輸出結(jié)果.這個(gè)時(shí)候我們可以使用下面兩個(gè)函數(shù)

#include<time.h>

time_ttime(time_t*tloc);

char*ctime(consttime_t*clock);

time函數(shù)返回從1970年1月1日0點(diǎn)以來(lái)的秒數(shù).存儲(chǔ)在time_t結(jié)構(gòu)之中.不過(guò)這個(gè)函數(shù)的返回值對(duì)于我們來(lái)說(shuō)沒有什么實(shí)際意義.這個(gè)時(shí)候我們使用第二個(gè)函數(shù)將秒數(shù)轉(zhuǎn)化為字符串

..這個(gè)函數(shù)的返回類型是固定的:一個(gè)可能值為.ThuDec714:58:592000這個(gè)字符串的長(zhǎng)度是固定的為262。時(shí)間的測(cè)量有時(shí)候我們要計(jì)算程序執(zhí)行的時(shí)間.比如我們要對(duì)算法進(jìn)行時(shí)間分析

..這個(gè)時(shí)候可以使用下面這個(gè)函數(shù).

#include<sys/time.h>

intgettimeofday(structtimeval*tv,structtimezone*tz);

struttimeval{

longtv_sec;/*秒數(shù)*/

longtv_usec;/*微秒數(shù)*/

};

gettimeofday將時(shí)間保存在結(jié)構(gòu)tv之中.tz一般我們使用NULL來(lái)代替.

#include<sys/time.h<

#include<stdio.h<

#include<math.h<

voidfunction()

{

unsignedinti,j;

doubley;

for(i=0;i<1000;i++)

for(j=0;j<1000;j++)

y=sin((double)i);

}

main()

{

structtimevaltpstart,tpend;

floattimeuse;

gettimeofday(&tpstart,NULL);

function();

gettimeofday(&tpend,NULL);

timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+

tpend.tv_usec-tpstart.tv_usec;

timeuse/=1000000;

printf("UsedTime:%f/n",timeuse);

exit(0);

}

這個(gè)程序輸出函數(shù)的執(zhí)行時(shí)間,我們可以使用這個(gè)來(lái)進(jìn)行系統(tǒng)性能的測(cè)試,或者是函數(shù)算法的效率分析.在我機(jī)器上的一個(gè)輸出結(jié)果是:UsedTime:0.556070

3。計(jì)時(shí)器的使用Linux操作系統(tǒng)為每一個(gè)進(jìn)程提供了3個(gè)內(nèi)部間隔計(jì)時(shí)器.

ITIMER_REAL:減少實(shí)際時(shí)間.到時(shí)的時(shí)候發(fā)出SIGALRM信號(hào).

ITIMER_VIRTUAL:減少有效時(shí)間(進(jìn)程執(zhí)行的時(shí)間).產(chǎn)生SIGVTALRM信號(hào).

ITIMER_PROF:減少進(jìn)程的有效時(shí)間和系統(tǒng)時(shí)間(為進(jìn)程調(diào)度用的時(shí)間).這個(gè)經(jīng)常和上面一個(gè)使用用來(lái)計(jì)算系統(tǒng)內(nèi)核時(shí)間和用戶時(shí)間.產(chǎn)生SIGPROF信號(hào).

具體的操作函數(shù)是:

#include<sys/time.h>

intgetitimer(intwhich,structitimerval*value);

intsetitimer(intwhich,structitimerval*newval,structitimerval*oldval);

structitimerval{

structtimevalit_interval;

structtimevalit_value;

}

getitimer函數(shù)得到間隔計(jì)時(shí)器的時(shí)間值.保存在value中setitimer函數(shù)設(shè)置間隔計(jì)時(shí)器的時(shí)間值為newval.并將舊值保存在oldval中.which表示使用三個(gè)計(jì)時(shí)器中的哪一個(gè).itimerval結(jié)構(gòu)中的it_value是減少的時(shí)間,當(dāng)這個(gè)值為0的時(shí)候就發(fā)出相應(yīng)的信號(hào)了.然后設(shè)置為it_interval值.

#include<sys/time.h>

#include<stdio.h>

#include<unistd.h>

#include<signal.h>

#include<string.h>

#definePROMPT"時(shí)間已經(jīng)過(guò)去了兩秒鐘/n/a"

char*prompt=PROMPT;

unsignedintlen;

voidprompt_info(intsigno)

{

write(STDERR_FILENO,prompt,len);

}

voidinit_sigaction(void)

{

structsigactionact;

act.sa_handler=prompt_info;

act.sa_flags=0;

sigemptyset(&act.sa_mask);//清空此信號(hào)集

sigaction(SIGPROF,&act,NULL);

}

voidinit_time()

{

structitimervalvalue;

value.it_value.tv_sec=2;

value.it_value.tv_usec=0;

value.it_interval=value.it_value;

setitimer(ITIMER_PROF,&value,NULL);

}

intmain()

{

len=strlen(prompt);

init_sigaction();

init_time();

while(1);

exit(0);

}

這個(gè)程序每執(zhí)行兩秒中之后會(huì)輸出一個(gè)提示.

5)Linux程序設(shè)計(jì)入門--信號(hào)處理

Linux下的信號(hào)事件

前言:這一章我們討論一下Linux下的信號(hào)處理函數(shù).

Linux下的信號(hào)處理函數(shù):

信號(hào)的產(chǎn)生

信號(hào)的處理

其它信號(hào)函數(shù)

一個(gè)實(shí)例

1。信號(hào)的產(chǎn)生

Linux下的信號(hào)可以類比于DOS下的INT或者是Windows下的事件.在有一個(gè)信號(hào)發(fā)生時(shí)候相應(yīng)的信號(hào)就會(huì)發(fā)送給相應(yīng)的進(jìn)程.在Linux下的信號(hào)有以下幾個(gè).我們使用kill-l命令可以得到以下的輸出結(jié)果:

1)SIGHUP2)SIGINT3)SIGQUIT4)SIGILL

5)SIGTRAP6)SIGABRT7)SIGBUS8)SIGFPE

9)SIGKILL10)SIGUSR111)SIGSEGV12)SIGUSR2

13)SIGPIPE14)SIGALRM15)SIGTERM17)SIGCHLD

18)SIGCONT19)SIGSTOP20)SIGTSTP21)SIGTTIN

22)SIGTTOU23)SIGURG24)SIGXCPU25)SIGXFSZ

26)SIGVTALRM27)SIGPROF28)SIGWINCH29)SIGIO

30)SIGPWR

關(guān)于這些信號(hào)的詳細(xì)解釋請(qǐng)查看man7signal的輸出結(jié)果.信號(hào)事件的發(fā)生有兩個(gè)來(lái)源:一個(gè)是硬件的原因(比如我們按下了鍵盤),一個(gè)是軟件的原因(比如我們使用系統(tǒng)函數(shù)或者是命令發(fā)出信號(hào)).最常用的四個(gè)發(fā)出信號(hào)的系統(tǒng)函數(shù)是kill,raise,alarm和setitimer函數(shù).setitimer函數(shù)我們?cè)谟?jì)時(shí)器的使用那一章再學(xué)習(xí).

#include<sys/types.h>

#include<signal.h>

#include<unistd.h>

intkill(pid_tpid,intsig);

intraise(intsig);

unisignedintalarm(unsignedintseconds);

kill系統(tǒng)調(diào)用負(fù)責(zé)向進(jìn)程發(fā)送信號(hào)sig.

如果pid是正數(shù),那么向信號(hào)sig被發(fā)送到進(jìn)程pid.

如果pid等于0,那么信號(hào)sig被發(fā)送到所以和pid進(jìn)程在同一個(gè)進(jìn)程組的進(jìn)程

如果pid等于-1,那么信號(hào)發(fā)給所有的進(jìn)程表中的進(jìn)程,除了最大的哪個(gè)進(jìn)程號(hào).

如果pid小于0,將信號(hào)傳給進(jìn)程組識(shí)別碼為pid絕對(duì)值的所有進(jìn)程.

我們用最多的是第一個(gè)情況.還記得我們?cè)谑刈o(hù)進(jìn)程那一節(jié)的例子嗎?我們那個(gè)時(shí)候用這個(gè)函數(shù)殺死了父進(jìn)程守護(hù)進(jìn)程的創(chuàng)建

raise系統(tǒng)調(diào)用向自己發(fā)送一個(gè)sig信號(hào).我們可以用上面那個(gè)函數(shù)來(lái)實(shí)現(xiàn)這個(gè)功能的.

alarm函數(shù)和時(shí)間有點(diǎn)關(guān)系了,這個(gè)函數(shù)可以在seconds秒后向自己發(fā)送一個(gè)SIGALRM信號(hào)

..下面這個(gè)函數(shù)會(huì)有什么結(jié)果呢?

#include<unistd.h>

main()

{

unsignedinti;

alarm(1);

for(i=0;1;i++)

printf("I=%d",i);

}

SIGALRM的缺省操作是結(jié)束進(jìn)程,所以程序在1秒之后結(jié)束,你可以看看你的最后I值為多少,來(lái)比較一下大家的系統(tǒng)性能差異(我的是2232).

2。信號(hào)操作有時(shí)候我們希望進(jìn)程正確的執(zhí)行,而不想進(jìn)程受到信號(hào)的影響,比如我們希望上面那個(gè)程序在1秒鐘之后不結(jié)束.這個(gè)時(shí)候我們就要進(jìn)行信號(hào)的操作了.

信號(hào)操作最常用的方法是信號(hào)屏蔽.信號(hào)屏蔽要用到下面的幾個(gè)函數(shù).

#include<signal.h>

intsigemptyset(sigset_t*set);

intsigfillset(sigset_t*set);

intsigaddset(sigset_t*set,intsigno);

intsigdelset(sigset_t*set,intsigno);

intsigismember(sigset_t*set,intsigno);

intsigprocmask(inthow,constsigset_t*set,sigset_t*oset);

sigemptyset函數(shù)初始化信號(hào)集合set,將set設(shè)置為空.sigfillset也初始化信號(hào)集合,只是將信號(hào)集合設(shè)置為所有信號(hào)的集合.sigaddset將信號(hào)signo加入到信號(hào)集合之中,sigdelset將信號(hào)從信號(hào)集合中刪除.sigismember查詢信號(hào)是否在信號(hào)集合之中.sigprocmask是最為關(guān)鍵的一個(gè)函數(shù).在使用之前要先設(shè)置好信號(hào)集合set.這個(gè)函數(shù)的作用是將指定的信號(hào)集合set加入到進(jìn)程的信號(hào)阻塞集合之中去,如果提供了oset那么當(dāng)前的進(jìn)程信號(hào)阻塞集合將會(huì)保存在oset里面.參數(shù)how決定函數(shù)的操作方式.

SIG_BLOCK:增加一個(gè)信號(hào)集合到當(dāng)前進(jìn)程的阻塞集合之中.

SIG_UNBLOCK:從當(dāng)前的阻塞集合之中刪除一個(gè)信號(hào)集合.

SIG_SETMASK:將當(dāng)前的信號(hào)集合設(shè)置為信號(hào)阻塞集合.

以一個(gè)實(shí)例來(lái)解釋使用這幾個(gè)函數(shù).

#include<signal.h>

#include<stdio.h>

#include<math.h>

#include<stdlib.h>

intmain(intargc,char**argv)

{

doubley;

sigset_tintmask;

inti,repeat_factor;

if(argc!=2)

{

fprintf(stderr,"Usage:%srepeat_factor/n/a",argv[0]);

exit(1);

}

if((repeat_factor=atoi(argv[1]))<1)repeat_factor=10;

sigemptyset(&intmask);/*將信號(hào)集合設(shè)置為空*/

sigaddset(&intmask,SIGINT);/*加入中斷Ctrl+C信號(hào)*/

while(1)

{

/*阻塞信號(hào),我們不希望保存原來(lái)的集合所以參數(shù)為NULL*/

sigprocmask(SIG_BLOCK,&intmask,NULL);

fprintf(stderr,"SIGINTsignalblocked/n");

for(i=0;i<repeat_factor;i++)y=sin((double)i);

fprintf(stderr,"Blockedcalculationisfinished/n");

/*取消阻塞*/

sigprocmask(SIG_UNBLOCK,&intmask,NULL);

fprintf(stderr,"SIGINTsignalunblocked/n");

for(i=0;i<repeat_factor;i++)y=sin((double)i);

fprintf(stderr,"Unblockedcalculationisfinished/n");

}

exit(0);

}

程序在運(yùn)行的時(shí)候我們要使用Ctrl+C來(lái)結(jié)束.如果我們?cè)诘谝挥?jì)算的時(shí)候發(fā)出SIGINT信號(hào),由于信號(hào)已經(jīng)屏蔽了,所以程序沒有反映.只有到信號(hào)被取消阻塞的時(shí)候程序才會(huì)結(jié)束.注意我們只要發(fā)出一次SIGINT信號(hào)就可以了,因?yàn)樾盘?hào)屏蔽只是將信號(hào)加入到信號(hào)阻塞集合之中,并沒有丟棄這個(gè)信號(hào).一旦信號(hào)屏蔽取消了,這個(gè)信號(hào)就會(huì)發(fā)生作用.

有時(shí)候我們希望對(duì)信號(hào)作出及時(shí)的反映的,比如當(dāng)擁護(hù)按下Ctrl+C時(shí),我們不想什么事情也不做,我們想告訴用戶你的這個(gè)操作不好,請(qǐng)不要重試,而不是什么反映也沒有的.這個(gè)時(shí)候我們要用到sigaction函數(shù).

#include<signal.h>

intsigaction(intsigno,conststructsigaction*act,structsigaction*oact);

structsigaction{

void(*sa_handler)(intsigno);

void(*sa_sigaction)(intsiginfo_t*info,void

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論