版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度醫(yī)療器械出口合同執(zhí)行與質(zhì)量監(jiān)管協(xié)議3篇
- 年薪制勞動(dòng)合同2025版:高科技企業(yè)人才激勵(lì)范本4篇
- 專用會(huì)議接待:2024酒店住宿與餐飲服務(wù)合同2
- 二零二五年度出軌男方離婚財(cái)產(chǎn)分割及子女未來(lái)職業(yè)規(guī)劃合同4篇
- 2025年度抵押借款房屋維修保養(yǎng)合同模板4篇
- 二零二五年度智能制造生產(chǎn)線派遣服務(wù)合同4篇
- 二零二五年度大棚蔬菜病蟲害防治與生物防治服務(wù)合同3篇
- 2025年度建筑質(zhì)量一次性終結(jié)賠償協(xié)議4篇
- 2025年度苗圃定向育苗與循環(huán)農(nóng)業(yè)合同范本4篇
- 2025年度知識(shí)產(chǎn)權(quán)法律服務(wù)勞動(dòng)合同范本4篇
- 江蘇省蘇州市2024-2025學(xué)年高三上學(xué)期1月期末生物試題(有答案)
- 銷售與銷售目標(biāo)管理制度
- 人教版(2025新版)七年級(jí)下冊(cè)英語(yǔ):寒假課內(nèi)預(yù)習(xí)重點(diǎn)知識(shí)默寫練習(xí)
- 2024年食品行業(yè)員工勞動(dòng)合同標(biāo)準(zhǔn)文本
- 2025年第一次工地開工會(huì)議主要議程開工大吉模板
- 全屋整裝售后保修合同模板
- 高中生物學(xué)科學(xué)推理能力測(cè)試
- GB/T 44423-2024近紅外腦功能康復(fù)評(píng)估設(shè)備通用要求
- 2024-2030年中國(guó)減肥行業(yè)市場(chǎng)發(fā)展分析及發(fā)展趨勢(shì)與投資研究報(bào)告
- 運(yùn)動(dòng)技能學(xué)習(xí)
- 單側(cè)雙通道內(nèi)鏡下腰椎間盤摘除術(shù)手術(shù)護(hù)理配合1
評(píng)論
0/150
提交評(píng)論