- 論壇徽章:
- 0
|
UNIX 編程資料
第一章 概述
1.1UNIX的版本
UNIX操作系統(tǒng)是貝爾實(shí)驗(yàn)室于六十年代末用C語言研制開發(fā)的。經(jīng)過幾十年的發(fā)展,已經(jīng)成為流行于從大型機(jī)、小型機(jī)到工作站甚至微機(jī)等多種平臺(tái)的操作系統(tǒng)。UNIX的成功同時(shí)也推動(dòng)了C語言的普及。本教材的目的是講解UNIX系統(tǒng)下的C程序設(shè)計(jì),使C程序員快速掌握UNIX系統(tǒng)下的編程開發(fā)。作者在進(jìn)行UNIX編程開發(fā)的實(shí)踐過程中,深感實(shí)例的重要性-一個(gè)簡(jiǎn)短的C語言實(shí)例往往勝過長(zhǎng)篇累牘的文字說明,當(dāng)然了,文字說明也是必不可少的。本教材將本著實(shí)例優(yōu)先的原則,使您能夠?qū)NIX編程開發(fā)快速入門。
UNIX的版本不統(tǒng)一是出了名的,從UNIX的發(fā)展歷史來看,主要有兩大流派:AT&T的UNIX系統(tǒng)V版本和加州大學(xué)伯克利分校的BSD版本,在此基礎(chǔ)上,各家UNIX廠商均開發(fā)了各自的UNIX操作系統(tǒng)。如:工作站廠商中有HP的hpux、SUN的solaris、SGI的irix、IBM的AIX等,小型機(jī)有VAX上的Ultrix,微機(jī)上有SCO
UNIX、微軟的Xenix以及隨著Internet而風(fēng)靡全球的Linux等。由于Windows
NT的異軍突起,對(duì)UNIX的市場(chǎng)形成巨大的威脅,各大UNIX廠商不得不聯(lián)合起來,在工作站市場(chǎng)上,統(tǒng)一以系統(tǒng)V版作為標(biāo)準(zhǔn),加入BSD版本中的一些優(yōu)點(diǎn),支持統(tǒng)一的CDE(Common
Desktop Environment)窗口環(huán)境,以與Windows NT進(jìn)行對(duì)抗。
1.2 UNIX編程環(huán)境
UNIX操作系統(tǒng)通過Shell程序?qū)崿F(xiàn)系統(tǒng)與用戶的交互,在Shell提示符下,用戶鍵入U(xiǎn)NIX命令,即可得到操作系統(tǒng)的輸出結(jié)果。BSD系統(tǒng)的常用Shell是C Shell,缺省提示符是"%",系統(tǒng)V的常用Shell是Bourne Shell(現(xiàn)在多為Korn
Shell),缺省提示符是"$",有關(guān)Shell的編程,我們?cè)诤竺娴恼鹿?jié)中進(jìn)行介紹。
UNIX上的標(biāo)準(zhǔn)編譯器是cc。在Shell提示符下(以C Shell為例)鍵入下列命令:
%cc -o hello hello.c
即將C文件hello.c編譯為可執(zhí)行文件hello。在編譯多個(gè)文件生成一個(gè)可執(zhí)行文件時(shí),UNIX提供命令make。用戶需要針對(duì)多個(gè)C文件,按照一定的格式編寫一個(gè)叫做Makefile的文本文件。下面是SGI上的一個(gè)Makefile的例子:
CC = cc
CFLAGS = $(DEBUG) -cckr -I$(INC)/X11 -DSYSV
DEBUG = -g
INC = /usr/include
LDFLAGS = -lXext -lXm -lXt -lX11 -lPW -lc
OBJS = initx.o windowx.o
TGTS = showxwin
all:: $(TGTS)
showxwin: $(OBJS)
$(CC) -o $@ $(OBJS) $(CFLAGS) $(LDFLAGS)
大寫字母的字串是一些宏,CC是編譯器的名字、CFLAGS定義cc的編譯開關(guān)、DEBUG是調(diào)試宏、INC是頭文件所在目錄、LDFLAGS定義了編譯連接庫、OBJ定義了目標(biāo)文件名、TGTS定義了可執(zhí)行文件名。在Shell提示符下直接鍵入:
%make
即可將Makefile中指定的所有C文件進(jìn)行編譯并生成可執(zhí)行文件。
1.3 UNIX編程中的基本概念
在討論UNIX編程開發(fā)前,首先需要闡明系統(tǒng)調(diào)用和庫函數(shù)這兩個(gè)概念。
一個(gè)系統(tǒng)調(diào)用指一個(gè)需要操作系統(tǒng)代表用戶程序來執(zhí)行某些任務(wù)的請(qǐng)求。例如:read是一個(gè)系統(tǒng)調(diào)用,它請(qǐng)求操作系統(tǒng)存儲(chǔ)在一個(gè)磁盤設(shè)備(或其他設(shè)備)上的數(shù)據(jù)去填充一個(gè)緩沖區(qū)。如果任何人在他們想執(zhí)行任務(wù)的時(shí)候都能隨便訪問設(shè)備,那么后果將是不可預(yù)測(cè)的。所以,這種服務(wù)必須請(qǐng)求操作系統(tǒng)來做,它(經(jīng)常是透明地)記錄所有處理每個(gè)設(shè)備的請(qǐng)求。
而一個(gè)庫函數(shù),并不經(jīng)常需要操作系統(tǒng)來執(zhí)行其任務(wù)。例如數(shù)學(xué)庫函數(shù)中的sin(),cos()等,這些計(jì)算只需要簡(jiǎn)單地對(duì)一個(gè)有限序列求和,所以并不需要操作系統(tǒng)干預(yù)。
在UNIX操作系統(tǒng)中,有一個(gè)常用的命令man,可用來查閱命令、庫函數(shù)和系統(tǒng)調(diào)用等的具體使用方法。傳統(tǒng) Unix 聯(lián)機(jī)幫助手冊(cè)的分節(jié)法為:
1 用戶級(jí)命令(User-level commands)
2 系統(tǒng)調(diào)用(System calls)
3 庫函數(shù)(Library functions)
4 設(shè)備及驅(qū)動(dòng)程序(Devices and device drivers)
5 文件格式(File formats)
6 游戲(Games)
7 雜項(xiàng)(Various miscellaneous stuff - macro packages etc.)
8 系統(tǒng)維護(hù)及操作命令(System maintenance and operation commands)
第二章 標(biāo)準(zhǔn)輸入/輸出庫
2.1 概述
本章介紹UNIX的標(biāo)準(zhǔn)輸入/輸出庫,UNIX提供一些庫函數(shù)完成高級(jí)輸入/輸出,為程序員提供了三方面的主要功能:
·自動(dòng)開辟緩沖區(qū)。即使一次讀或?qū)懙臄?shù)據(jù)只有幾個(gè)字節(jié),庫函數(shù)仍然在大到由數(shù)千個(gè)字節(jié)組成的"塊"中執(zhí)行實(shí)際輸入或輸出(緩沖區(qū)大小通常由頭文件stdio.h中的常量BUFSIZ定義)。這個(gè)緩沖區(qū)在內(nèi)部開辟給庫函數(shù)使用,對(duì)于程序員來說是透明的;
·自動(dòng)執(zhí)行輸入和輸出轉(zhuǎn)換。
·輸入輸出被自動(dòng)格式化。以上兩點(diǎn)在C語言的教程中一般均以講到。
在標(biāo)準(zhǔn)輸入/輸出庫中,一個(gè)文件被稱為一串字符流,并且被一個(gè)指向類型為FILE的目標(biāo)指針?biāo)枋,該指針被稱為文件指針。在UNIX中文件指針stdin、stdout、stderr是預(yù)先定義好的,分別對(duì)應(yīng)標(biāo)準(zhǔn)輸入(鍵盤)、標(biāo)準(zhǔn)輸出(終端屏幕)和標(biāo)準(zhǔn)錯(cuò)誤輸出。
2.2 庫函數(shù)介紹
·文件創(chuàng)建和關(guān)閉
fopen()用于打開已存在的文件或創(chuàng)建新文件
·文件讀寫
1、 一次處理一個(gè)字符 getc(), putc()
2、 一次處理多個(gè)字符 fgets(), fputs()
3、 文件的二進(jìn)制讀寫 fread(), fwrite()
4、 文件的格式化輸入/輸出 fscanf(), fprintf()
5、 字符串的格式化輸入/輸出 sscanf(), sprintf()
·文件移動(dòng)定位
用于在文件中移動(dòng)的標(biāo)準(zhǔn)輸入/輸出庫函數(shù)是fseek(),它接收三個(gè)參數(shù):一個(gè)文件指針指向一個(gè)打開的字符流;一個(gè)整數(shù)指明要移動(dòng)的字節(jié)數(shù),稱為offset;一個(gè)整數(shù)指明從文件中什么位置移動(dòng)。
第三章 低級(jí)輸入/輸出
3.1 概述
與第二章內(nèi)容相對(duì)應(yīng),本章介紹UNIX系統(tǒng)中通過系統(tǒng)調(diào)用來實(shí)現(xiàn)的輸入/輸出,通常稱之為低級(jí)輸入/輸出。這些系統(tǒng)調(diào)用能夠直接實(shí)現(xiàn)對(duì)設(shè)備(如磁帶驅(qū)動(dòng)器等)的輸入和輸出,程序員能夠決定要使用的緩沖區(qū)的大小,而不象標(biāo)準(zhǔn)輸入/輸出庫函數(shù)那樣透明設(shè)定緩沖區(qū)大小。
在標(biāo)準(zhǔn)輸入/輸出庫中,一個(gè)文件是由一個(gè)文件指針來對(duì)應(yīng)的。當(dāng)使用低級(jí)界面時(shí),則用一個(gè)文件描述字對(duì)應(yīng)一個(gè)文件。文件描述字是一個(gè)小的整數(shù)。有3個(gè)事先定義的文件描述字0、1和2,分別對(duì)應(yīng)標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出。一般說來,文件描述字都是作為系統(tǒng)調(diào)用的第一個(gè)參數(shù)給出的。
3.2 相關(guān)系統(tǒng)調(diào)用介紹
·文件創(chuàng)建和關(guān)閉
open()用于為讀寫而打開一個(gè)文件,或用它來創(chuàng)建新文件。
int open (const char *path, int oflag, ... /* mode_t mode */);
open使用三個(gè)參數(shù):一個(gè)字符串path包含要打開的文件名;一個(gè)整數(shù)oflag指明文件將被如何打開;整數(shù)mode在創(chuàng)建文件時(shí)使用。常用的oflag包括:
O_RDONLY 打開文件僅用于讀。
O_WRONLY 打開文件僅用于寫。
O_RDWR 打開文件用于讀寫。
O_CREAT 如果文件不存在,則創(chuàng)建,此時(shí)mode作為第三個(gè)參數(shù)給出。
close()用于關(guān)閉一個(gè)已經(jīng)打開的文件。
·文件讀寫
read()用于讀文件,格式為:
read(int fildes, void *buf, size_t nbyte);
三個(gè)參數(shù)說明如下:filedes是文件描述字;指針buf指向一個(gè)數(shù)據(jù)將被讀入的緩沖區(qū);整數(shù)nbytes指明要讀的字節(jié)個(gè)數(shù)。成功時(shí)返回實(shí)際讀入的字節(jié)數(shù),出錯(cuò)則返回-1。
write()用于寫文件,與read類似,格式為:
write(int fildes, void *buf, size_t nbyte);
三個(gè)參數(shù)說明如下:filedes是文件描述字;指針buf指向一個(gè)數(shù)據(jù)將被寫入的緩沖區(qū);整數(shù)nbytes指明要寫的字節(jié)個(gè)數(shù)。成功時(shí)返回實(shí)際寫入的字節(jié)數(shù),出錯(cuò)則返回-1。
·文件移動(dòng)定位
用于在文件中移動(dòng)的低級(jí)輸入/輸出系統(tǒng)調(diào)用是lseek(),與fseek()類似,它也接收三個(gè)參數(shù):一個(gè)文件描述字對(duì)應(yīng)一個(gè)打開的文件;一個(gè)整數(shù)指明要移動(dòng)的字節(jié)數(shù),稱為offset;一個(gè)整數(shù)指明從文件中什么位置移動(dòng)。
·復(fù)制文件描述字
有時(shí)候有不只一個(gè)文件描述字對(duì)應(yīng)一個(gè)文件。當(dāng)創(chuàng)建子進(jìn)程時(shí)(參加后面關(guān)于進(jìn)程開發(fā)的章節(jié)),這一點(diǎn)很常用。為了獲得一個(gè)新的文件描述字,并保證其與fd對(duì)應(yīng)同一個(gè)文件,應(yīng)調(diào)用
fd2 = dup(fd)
fd2現(xiàn)在和fd對(duì)應(yīng)同一個(gè)文件,并且和fd一樣在文件中有相同的位置。
第四章 文件與目錄編程
4.1 基本概念
·文件目錄概述
文件系統(tǒng)是UNIX對(duì)計(jì)算機(jī)技術(shù)的一大貢獻(xiàn)!UNIX系統(tǒng)的文件管理十分靈活、功能強(qiáng)大,許多首次在UNIX系統(tǒng)中出現(xiàn)的概念被其他操作系統(tǒng)所采用,如MS-DOS等。
UNIX系統(tǒng)提供了一種層次目錄方案。目錄就象存放一組文件的柜子一樣,目錄也可以包括在其他目錄中,這樣就形成了一種龐大的、具有分支的組織方式,這種結(jié)構(gòu)通常被稱為樹狀結(jié)構(gòu)。目錄實(shí)際上也是一種特殊的文件。命令、數(shù)據(jù)文件、其他命令甚至設(shè)備(特別文件)都可以作為目錄中的項(xiàng)(文件)。
·I標(biāo)識(shí)號(hào)、I列表和I節(jié)點(diǎn)
一個(gè)目錄是由一系列結(jié)構(gòu)組成的;每個(gè)結(jié)構(gòu)包含一個(gè)文件名和一個(gè)指向文件自身的指針,該指針是一個(gè)整數(shù),稱為文件的I標(biāo)識(shí)號(hào)。當(dāng)文件被訪問時(shí),它的I標(biāo)識(shí)號(hào)用來作為索引打開一個(gè)系統(tǒng)表(I列表),系統(tǒng)中存放著文件(I節(jié)點(diǎn))的實(shí)體。I節(jié)點(diǎn)包含了對(duì)文件的描述:
·文件自身的用戶和用戶組ID
·文件的保護(hù)碼
·文件內(nèi)容所在的物理磁盤地址
·文件的大小
·最后一次I節(jié)點(diǎn)改變的時(shí)間,最后一次使用和修改的時(shí)間
·連接該文件的次數(shù),即它出現(xiàn)在其他目錄中的次數(shù)
·一個(gè)指明文件類型的標(biāo)記(目錄、普通文件、特別文件)
·文件的三級(jí)保護(hù)
UNIX把使用文件的用戶分成三個(gè)等級(jí):文件所有者、同組用戶和其他用戶。文件所有者也稱文件主,是文件的創(chuàng)建者,對(duì)該文件擁有所有權(quán)限;同組用戶是具有相同組標(biāo)識(shí)號(hào)的所有用戶,文件主可以決定一個(gè)文件屬于哪個(gè)組以及該組用戶對(duì)文件的存取權(quán);其他用戶是指與文件主無關(guān)的用戶,他們與文件主不屬于同一個(gè)用戶組,其他用戶對(duì)一個(gè)文件的訪問權(quán)限也由該文件主決定的。
一個(gè)文件的訪問權(quán)限存放在該文件I節(jié)點(diǎn)的di_mode域中,di_mode的0-8位表示文件主、文件組用戶和其他用戶對(duì)該文件的存取權(quán)限。舉個(gè)例子,用"ls
-l"命令可列出文件hello.c的模式和屬性:
-rwxr-xr-x 1 yds user 58 9月 25日 10時(shí)54分 hello.c
最左面一欄顯示了該文件的模式:文件主對(duì)該文件可讀(r)、可寫(w)、可執(zhí)行(x),同組用戶對(duì)該文件可讀、可執(zhí)行,其他用戶對(duì)該文件可讀、可執(zhí)行。相應(yīng)的,di_mode的0-8位為111101101(0755)。
4.2 文件編程介紹
·檢查訪問權(quán)限-access系統(tǒng)調(diào)用
access系統(tǒng)調(diào)用的格式為:
#include
int access(const char *path, int amode);
其中:參數(shù)path指出被檢查文件的路徑,參數(shù)amode指出訪問權(quán)限。Access判斷調(diào)用進(jìn)程的實(shí)際用戶對(duì)文件path是否具有amode所指定的訪問權(quán)限,若有相應(yīng)權(quán)限,access返回0,否則返回-1。
參數(shù)amode可取以下值或它們的邏輯"或":
R_OK 檢查讀權(quán)限
W_OK 檢查寫權(quán)限
X_OK 檢查執(zhí)行(搜索)權(quán)限
F_OK 檢查文件是否存在
例如:access("hello.c", R_OK|W_OK),
用來檢查實(shí)際用戶對(duì)文件hello.c是否具有讀/寫權(quán);access("hello.c",F_OK) 判斷文件hello.c是否存在。
·鏈接與刪除文件-link和unlink系統(tǒng)調(diào)用
link和unlink系統(tǒng)調(diào)用的格式為:
#include
int link (const char *path1, const char *path2);
int unlink(const char *path);
其中:參數(shù)path1指出已經(jīng)存在的要被鏈接的文件路徑名,path2指出要建立的鏈接文件。link實(shí)現(xiàn)path2到path1的鏈接,相當(dāng)于給path1起了一個(gè)別名,同時(shí)文件path1的鏈接計(jì)數(shù)加1。若成功則返回0,否則返回-1。
參數(shù)path指出要被刪除的文件路徑名。Unlink刪除由path指出的文件,若成功則返回0,否則返回-1。
·從I節(jié)點(diǎn)上獲取信息-stat與fstat系統(tǒng)調(diào)用
stat與fstat的調(diào)用格式為:
#include
#include
int stat(const char *path, struct stat *buf);
int fstat(int fildes, struct stat *buf);
說明:stat和fstat都用于獲取文件I節(jié)點(diǎn)中有關(guān)狀態(tài)信息。stat根據(jù)參數(shù)path給出的文件路徑名,搜索它對(duì)應(yīng)的盤I節(jié)點(diǎn),而fstat則根據(jù)參數(shù)fildes給出的文件描述字去查找對(duì)應(yīng)的I節(jié)點(diǎn)。這兩個(gè)調(diào)用都把從I節(jié)點(diǎn)中獲取到的信息重組后放入?yún)?shù)buf指向的stat結(jié)構(gòu)中(stat結(jié)構(gòu)的說明在文件/usr/include/sys/stat.h中)。這兩個(gè)調(diào)用成功時(shí)均返回0,否則返回-1。
stat與fstat調(diào)用無論在使用上還是在功能上都是非常類似的,在參數(shù)上有一點(diǎn)區(qū)別。下面我們來看一個(gè)例子。
/* statfile.c */
#include
#include
#include
#include
main(argc,argv)
int argc;
char *argv[];
{
int fd;
struct stat statbuf;
if (argc!=2){
printf("usage: statfile filename!\n");
exit(1);
}
if ((fd = fopen(argv[1], O_RDONLY)) == -1)
fprintf(stderr, "Cannot open %s!\n", argv[1]);
if (unlink(argv[1]) == -1)
fprintf(stderr,"Cannot unlink %s!\n", argv[1]);
if (stat(argv[1], &statbuf) == -1) /* by file name */
fprintf(stderr, "stat %s fails as it should !\n");
else
printf("stat %s succeed!\n", argv[1]);
if (fstat(fd, &statbuf) == -1) /* by file descriptor */
fprintf(stderr, "fstat %s fails!\n", argv[1]);
else
printf("fstat %s succeeds as it should!\n", argv[1]);
}
程序首先打開命令行中指定的文件,然后用unlink將該文件刪除,接著分別用stat與fstat系統(tǒng)調(diào)用獲取該文件的信息。假設(shè)當(dāng)前命令下有一個(gè)名為xxx.c的文件,運(yùn)行
%statfile xxx.c
后,將會(huì)輸出如下結(jié)果:
stat xxx.c fails as it should!
fstat xxx.c succeeds as it should!
從中可知,當(dāng)一個(gè)打開文件被刪除后,用stat無法獲取該文件的信息。而fstat就可獲取該文件的信息。這是由于文件名在unlink之后已從目錄中除去,無法找到該文件名,而文件描述字則因文件仍打開而保存下來。因此stat不能成功返回,但fstat仍可成功返回。
使用stat調(diào)用來判定一個(gè)文件為何種文件類型時(shí)相當(dāng)有用。例如下列代碼:
stat("hello", &statbuf);
if (statbuf.st_mod & S_IFMT) == S_IFDIR)
printf("This is a directory file!\n");
else if (statbuf.st_mod & S_IFMT) == S_IFREG)
printf("This is a regular file !\n");
else …
以上代碼可判定當(dāng)前目錄下的hello文件是否為目錄文件或其他類型的文件。
4.3 目錄編程介紹
UNIX把目錄也視為一種文件,稱為目錄文件,并同普通文件一樣進(jìn)行管理和保護(hù)。如open、close、read、lseek等文件操作對(duì)目錄文件都是有效的。前面講到的文件編程中的各個(gè)系統(tǒng)調(diào)用對(duì)于目錄來說也同樣有效。但是與普通文件相比目錄文件又具有自身的一些特點(diǎn):
目錄文件的讀/寫/執(zhí)行訪問權(quán)限有特殊的含義:讀權(quán)限允許用戶讀取目錄項(xiàng)的內(nèi)容;寫權(quán)限允許用戶創(chuàng)建或刪除一個(gè)文件;執(zhí)行權(quán)限則允許用戶檢索目錄(此時(shí)通常稱為目錄搜索權(quán)限)。
目錄的創(chuàng)建、刪除與普通文件也不同,另外,任何用戶都不能對(duì)目錄文件以寫方式打開進(jìn)行文件寫操作。
·目錄的創(chuàng)建和刪除-mkdir和rmdir系統(tǒng)調(diào)用
mkdir 和rmdir系統(tǒng)調(diào)用的格式為:
#include
#include
int mkdir (const char *path, mode_t mode);
#include
int rmdir(const char *path);
其中:參數(shù)path分別指出要?jiǎng)?chuàng)建和刪除的目錄文件的文件名。mkdir調(diào)用中的參數(shù)mode指出新創(chuàng)建目錄文件的文件模式。新創(chuàng)建目錄后,除"."和".."兩項(xiàng)外,無別的目錄項(xiàng);刪除目錄時(shí),要求目錄中除"."和".."兩項(xiàng)外,也無別的目錄項(xiàng)。這兩個(gè)系統(tǒng)調(diào)用Access成功時(shí)都返回0,否則返回-1。
·目錄的讀。璷pendir/readdir/closedir庫函數(shù)
目錄文件可以像普通文件一樣,先用系統(tǒng)調(diào)用open以讀方式打開,再用read調(diào)用讀取其中的內(nèi)容。同時(shí),由于目錄文件是由具有目錄結(jié)構(gòu)的目錄項(xiàng)組成的,用read讀取其內(nèi)容有些不方便。UNIX提供的庫函數(shù)opendir/readdir/closedir等可以方便地實(shí)現(xiàn)目錄讀取。函數(shù)說明如下:
#include
#include
DIR *opendir(char *filename);
struct direct *readdir(DIR *dirp);
void closedir(DIR *dirp);
說明:參數(shù)filename指出要打開的目錄路徑名,庫函數(shù)opendir返回一個(gè)指向結(jié)構(gòu)DIR(在文件/usr/include/sys/dir.h中定義)的指針。庫函數(shù)readdir和closedir均以這個(gè)指針作為參數(shù),其中readdir返回一個(gè)指向結(jié)構(gòu)direct的指針。有關(guān)目錄的操作均可基于這個(gè)指針。下面是一個(gè)例子,查找當(dāng)前目錄下文件名為"name"的文件。
len = strlen(name);
dirp = opendir(".");
if (dirp == NULL) {
return NOT_FOUND;
}
while ((dp = readdir(dirp)) != NULL) {
if (dp->d_namlen == len && !strcmp(dp->d_name, name)) {
closedir(dirp);
return FOUND;
}
}
closedir(dirp);
return NOT_FOUND;
庫函數(shù)closedir關(guān)閉打開的目錄。值得注意的是,上面這一小段代碼在編程中很實(shí)用,稍加修改即可實(shí)現(xiàn)UNIX下類似"ls"的簡(jiǎn)單命令。
第五章 基本進(jìn)程編程
5.1 概述
UNIX系統(tǒng)為程序員提供了一個(gè)強(qiáng)有力的工具:在一個(gè)程序中執(zhí)行另一個(gè)程序。執(zhí)行一個(gè)程序最簡(jiǎn)單的途徑就是使用庫函數(shù)system。該函數(shù)使用一個(gè)參數(shù):一個(gè)包含要被執(zhí)行的命令的字符串。這一庫函數(shù)的特點(diǎn)是用法簡(jiǎn)單,在程序中調(diào)用簡(jiǎn)單的UNIX命令時(shí)很有用。但是由于它的調(diào)用要由SHELL進(jìn)程來實(shí)現(xiàn),故效率并不高,在實(shí)際的編程中應(yīng)用并不廣泛。本章主要介紹在實(shí)際編程中經(jīng)常使用的有關(guān)進(jìn)程控制和管理方面的系統(tǒng)調(diào)用,它們包括:
fork - 創(chuàng)建一子進(jìn)程
exec - 執(zhí)行子進(jìn)程
exit - 終止進(jìn)程執(zhí)行
wait - 等待子進(jìn)程暫;蚪K止
setpgrp - 設(shè)置進(jìn)程標(biāo)識(shí)符
getpid、getppid - 獲取進(jìn)程標(biāo)識(shí)符
setuid、setgid - 設(shè)置進(jìn)程的用戶標(biāo)識(shí)符
getuid、geteuid、getgid、getegid - 獲取進(jìn)程的用戶標(biāo)識(shí)符
5.2 進(jìn)程控制
1. fork系統(tǒng)調(diào)用
系統(tǒng)調(diào)用fork是UNIX操作系統(tǒng)創(chuàng)建新進(jìn)程的唯一手段,習(xí)慣上將新創(chuàng)建的進(jìn)程稱為子進(jìn)程,調(diào)用fork的進(jìn)程稱為父進(jìn)程。
fork系統(tǒng)調(diào)用的格式為:
int fork()
fork系統(tǒng)調(diào)用沒有參數(shù),如執(zhí)行成功,則創(chuàng)建一子進(jìn)程,子進(jìn)程繼承了父進(jìn)程的某些屬性。當(dāng)從該系統(tǒng)調(diào)用返回時(shí),系統(tǒng)中已有兩個(gè)用戶級(jí)環(huán)境完全相同的進(jìn)程存在。這兩個(gè)進(jìn)程從fork調(diào)用中得到的返回值不同,其中子進(jìn)程得到的返回值為零,父進(jìn)程得到的返回值是最新創(chuàng)建的子進(jìn)程的進(jìn)程標(biāo)識(shí)符。
2. exec系統(tǒng)調(diào)用
fork系統(tǒng)調(diào)用只是將父進(jìn)程的環(huán)境拷貝到新進(jìn)程中,而沒有啟動(dòng)執(zhí)行一個(gè)新的目標(biāo)程序。UNIX系統(tǒng)提供了exec系統(tǒng)調(diào)用,用它更換進(jìn)程的執(zhí)行映象,啟動(dòng)新的目標(biāo)程序。例如:UNIX系統(tǒng)中的所有命令都是通過exec來執(zhí)行的。
exec系統(tǒng)調(diào)用有六種不同的使用格式,但在核心中只對(duì)應(yīng)一個(gè)調(diào)用入口。它們有不同的調(diào)用格式和調(diào)用參數(shù)。這六種調(diào)用格式分別為:
#include
int execl (const char *path, const char *arg0, ..., const char
*argn, (char *)0);
int execv (const char *path, char *const *argv);
int execle (const char *path, const char *arg0, ..., const char
*argn,
(char *0), const char *envp[]);
int execve (const char *path, char *const *argv, char *const *envp);
int execlp (const char *file, const char *arg0, ..., const char
*argn, (char *)0);
int execvp (const char *file, char *const *argv);
說明:參數(shù)path指出一個(gè)可執(zhí)行目標(biāo)文件的路徑名;參數(shù)file指出可執(zhí)行目標(biāo)文件的文件名。arg0作為約定同path一樣指出目標(biāo)文件的路徑名;參數(shù)arg1到argn分別是該目標(biāo)文件執(zhí)行時(shí)所帶的命令行參數(shù);參數(shù)argv是一個(gè)字符串指針數(shù)組,由它指出該目標(biāo)程序使用的命令行參數(shù)表,按約定第一個(gè)字符指針指向與path
或file相同的字符串;最后一個(gè)指針指向一個(gè)空字符串,其余的指向該程序執(zhí)行時(shí)所帶的命令行參數(shù);參數(shù)envp同argv一樣也是一個(gè)字符指針數(shù)組,由它指出該目標(biāo)程序執(zhí)行時(shí)的進(jìn)程環(huán)境,它也以一個(gè)空指針結(jié)束。
exec的六種格式在以下三點(diǎn)上有所不同:
(1) path是一個(gè)目標(biāo)文件的完整路徑名,而file是目標(biāo)文件名,它是可以通過環(huán)境變量PATH來搜索的;
(2) 由path或file指定的目標(biāo)文件的命令行參數(shù)是完整的參數(shù)列表或是通過一指針數(shù)組argv來給出的;
(3) 環(huán)境變量是系統(tǒng)自動(dòng)傳遞或者通過envp來給出的。
下圖說明了exec系統(tǒng)調(diào)用的六種不同格式對(duì)以上三點(diǎn)的支持。
系統(tǒng)調(diào)用 參數(shù)形式 環(huán)境傳送 路徑搜索
Execl 全部列表 自動(dòng) 否
Execv 指針數(shù)組 自動(dòng) 否
Execle 全部列表 不自動(dòng) 否
Execve 指針數(shù)組 不自動(dòng) 否
Execlp 全部列表 自動(dòng) 是
Execvp 指針數(shù)組 自動(dòng) 是
3. exit、wait系統(tǒng)調(diào)用
(1) exit系統(tǒng)調(diào)用格式如下:
#include
void exit(int status);
#include
void _exit(int status);
說明:exit的功能是終止進(jìn)程的執(zhí)行,并釋放該進(jìn)程所占用的某些系統(tǒng)資源。參數(shù)status是調(diào)用進(jìn)程終止時(shí)傳遞給其父進(jìn)程的值。如果調(diào)用進(jìn)程執(zhí)行exit系統(tǒng)調(diào)用時(shí),其父進(jìn)程正在等待子進(jìn)程暫;蚪K止(使用wait系統(tǒng)調(diào)用),則父進(jìn)程可立刻得到該值;如果此時(shí)父進(jìn)程并不處在等待狀態(tài),那么一旦父進(jìn)程使用wait調(diào)用,便可立刻得到子進(jìn)程傳過來的status值,注意:只有status的低八位才傳遞給其父進(jìn)程。
系統(tǒng)調(diào)用_exit與exit之間的差異是_exit只做部分的清除,因此建議不要輕易地使用這種調(diào)用形式。
每個(gè)進(jìn)程在消亡前都要調(diào)用該系統(tǒng)調(diào)用,沒有顯示地使用該系統(tǒng)調(diào)用,則生成目標(biāo)文件的裝載程序?yàn)樵撨M(jìn)程隱含地做這一工作。
(2) wait系統(tǒng)調(diào)用格式如下:
#include
#include
pid_t wait (int *statptr);
說明:wait系統(tǒng)調(diào)用將調(diào)用進(jìn)程掛起,直到該進(jìn)程收到一個(gè)被其捕獲的信號(hào)或者它的任何一個(gè)子進(jìn)程暫;蚪K止為止。如果在wait調(diào)用之前已有子進(jìn)程暫;蚪K止,則該調(diào)用立刻返回。格式wait((int
*)0) 的功能是等待所有子進(jìn)程終止。Wait返回時(shí),其返回值為該子進(jìn)程的進(jìn)程號(hào)。參數(shù)statptr的值為該子進(jìn)程的終止原因:
1、 如果子進(jìn)程暫停,statptr目的高八位存放使該子進(jìn)程暫停的信號(hào)值(在第七章中介紹信號(hào)),低八位為0177
2、 如果子進(jìn)程由于調(diào)用exit終止,則該值的低八位為0,高八位為子進(jìn)程終止時(shí),exit系統(tǒng)調(diào)用中參數(shù)status值的低八位;
3、 如果子進(jìn)程因信號(hào)終止,該值的高八位為0,低八位為引起終止的信號(hào)值。此外如低七位為1,則表示產(chǎn)生了一個(gè)core文件。
下面我們來看一個(gè)例子,該例是一個(gè)fork、exec、exit和wait聯(lián)合使用的一個(gè)實(shí)例程序,我們稱之為feew.c:
/* feew.c */
main(argc, argv)
int argc;
char *argv[];
{
int pid, stat;
if (argc != 1){
if ((pid = fork()) == 0){
printf("Child pid = %d\n", getpid());
execl(argv[1], argv[1], 0);
exit(5);
}
}
pid = wait(&stat);
printf("pid=%d, H_stat=%d, L_stat=%d\n", pid, stat>>8, stat&0xff);
}
當(dāng)命令行參數(shù)的個(gè)數(shù)不為1時(shí),程序使用fork系統(tǒng)調(diào)用產(chǎn)生一個(gè)子進(jìn)程。子進(jìn)程通過系統(tǒng)調(diào)用getpid獲得自己的進(jìn)程標(biāo)識(shí)符,然后調(diào)用exec執(zhí)行命令行中用戶提交的命令,如果exec執(zhí)行失敗,則子進(jìn)程調(diào)用exit(5)終止。父進(jìn)程使用wait系統(tǒng)調(diào)用等待子進(jìn)程暫;蚪K止,然后輸出從wait中返回的信息。下面以三種方式執(zhí)行該程序:
1〕 不帶命令行參數(shù)
% ./feew
pid=-1, H_stat=0, L_stat=0
%
不產(chǎn)生子進(jìn)程,從運(yùn)行結(jié)果來看,當(dāng)無子進(jìn)程時(shí),wait的返回值為-1。
2〕 帶命令行參數(shù),參數(shù)為合法的可執(zhí)行命令
% ./feew /bin/date
Child pid = 1725
1998年 2月16日(星期一) 15時(shí)59分14秒 CST
pid=1725, H_stat=0, L_stat=0
%
產(chǎn)生子進(jìn)程。子進(jìn)程輸出其進(jìn)程標(biāo)識(shí)符后,再調(diào)用exec執(zhí)行從命令行中提交的命令(/bin/date),同時(shí)父進(jìn)程等待子進(jìn)程暫;蚪K止,然后輸出從wait中得到的信息:子進(jìn)程標(biāo)識(shí)符或狀態(tài)參數(shù)stat的高八位、低八位的內(nèi)容。從中可以看到:子進(jìn)程因調(diào)用一個(gè)隱含的exit(0)而終止,終止時(shí)傳給父進(jìn)程的值為0。
3〕 帶命令行參數(shù),但參數(shù)不合法
%./feew /etc/shudown
Child pid = 1760
/etc/shutdown: 只有超級(jí)用戶(root)能運(yùn)行 /etc/shutdown。
pid=1760, H_stat=2, L_stat=0
%
子進(jìn)程創(chuàng)建成功。但由于以普通用戶的身份執(zhí)行/etc/shutdown,因此exec失敗,爾后調(diào)用exit(5)而終止;父進(jìn)程調(diào)用wait得到返回值:子進(jìn)程號(hào)和狀態(tài)參數(shù)stat的高八位、低八位的內(nèi)容。從執(zhí)行結(jié)果可以看出:子進(jìn)程因調(diào)用exit(5)而終止,終止時(shí)傳給父進(jìn)程的值為5。
5.3 進(jìn)程管理
進(jìn)程管理包括的面很廣,諸如進(jìn)程的用戶標(biāo)識(shí)符管理、進(jìn)程標(biāo)識(shí)符管理等。進(jìn)程的用戶標(biāo)識(shí)符有兩個(gè):實(shí)際用戶標(biāo)識(shí)符(real user
id)和有效用戶標(biāo)識(shí)符(effective user id),其對(duì)應(yīng)的組標(biāo)識(shí)符分別稱為實(shí)際組標(biāo)識(shí)符(real group
id)和有效組標(biāo)識(shí)符(effective groud
id)。一般而言,進(jìn)程的實(shí)際用戶標(biāo)識(shí)符為運(yùn)行該進(jìn)程的用戶標(biāo)識(shí)符,通常只用于系統(tǒng)記帳,其他功能由有效用戶標(biāo)識(shí)符來完成,如用有效用戶標(biāo)識(shí)符來完成對(duì)新創(chuàng)建文件賦予屬性關(guān)系、檢查文件的存取權(quán)限和利用kill系統(tǒng)調(diào)用向進(jìn)程發(fā)送信號(hào)的權(quán)限。一般情況下,一進(jìn)程的有效用戶標(biāo)識(shí)符和實(shí)際用戶標(biāo)識(shí)符是相等的,但系統(tǒng)允許改變進(jìn)程的有效用戶標(biāo)識(shí)符。
1. 進(jìn)程的用戶標(biāo)識(shí)符管理
UNIX系統(tǒng)提供了一組系統(tǒng)調(diào)用來管理進(jìn)程的用戶標(biāo)識(shí)符,它們的使用形式是:
#include
#include
uid_t getuid (void);
uid_t geteuid (void);
gid_t getgid (void);
gid_t getegid (void);
int setuid(uid_t uid);
int setgid(gid_t gid);
說明:前四個(gè)系統(tǒng)調(diào)用沒有參數(shù),分別返回調(diào)用進(jìn)程的實(shí)際用戶標(biāo)識(shí)符、有效用戶標(biāo)識(shí)符、實(shí)際用戶組標(biāo)識(shí)符和有效組標(biāo)識(shí)符。這些系統(tǒng)調(diào)用的執(zhí)行總能獲得成功,不會(huì)發(fā)生任何錯(cuò)誤。系統(tǒng)調(diào)用setuid和setgid用于設(shè)置進(jìn)程的實(shí)際用戶(組)標(biāo)識(shí)符和有效用戶(組)標(biāo)識(shí)符,如調(diào)用進(jìn)程的有效用戶標(biāo)識(shí)符是超級(jí)用戶標(biāo)識(shí)符,則將調(diào)用的進(jìn)程實(shí)際用戶(組)標(biāo)識(shí)符和有效用戶(組)標(biāo)識(shí)符設(shè)置為uid或gid;如調(diào)用進(jìn)程的有效用戶標(biāo)識(shí)符不是超級(jí)用戶標(biāo)識(shí)符,但其實(shí)際用戶(組)標(biāo)識(shí)符等于uid或gid時(shí),則其有效用戶(組)標(biāo)識(shí)符被設(shè)置為uid或gid;否則setuid或setgid調(diào)用失敗。系統(tǒng)調(diào)用setuid或setgid調(diào)用成功時(shí)返回0,失敗時(shí)返回-1。
2. 進(jìn)程標(biāo)識(shí)符管理
UNIX系統(tǒng)使用進(jìn)程標(biāo)識(shí)符來管理當(dāng)前系統(tǒng)中的進(jìn)程。為對(duì)具有某類似特性的進(jìn)程統(tǒng)一管理,系統(tǒng)又引入了進(jìn)程組的概念,以組標(biāo)識(shí)符來區(qū)別進(jìn)程是否同組。進(jìn)程的組標(biāo)識(shí)符是從父進(jìn)程繼承下來的,所以,通常進(jìn)程的組標(biāo)識(shí)符就是和它相關(guān)聯(lián)的注冊(cè)進(jìn)程的標(biāo)識(shí)符。進(jìn)程的標(biāo)識(shí)符是由系統(tǒng)為之分配的,不能被修改;組標(biāo)識(shí)符可通過setpgrp系統(tǒng)調(diào)用修改。
相關(guān)系統(tǒng)調(diào)用的格式如下:
#include
#include
pid_t getpid(void);
pid_t getpgrp(void);
pid_t getppid(void);
pid_t getpgid(pid_t pid);
說明:前三個(gè)系統(tǒng)調(diào)用分別返回調(diào)用進(jìn)程的進(jìn)程標(biāo)識(shí)符、進(jìn)程組標(biāo)識(shí)符和其父進(jìn)程標(biāo)識(shí)符。它們總能成功地返回。第四個(gè)調(diào)用置進(jìn)程組標(biāo)識(shí)符,它將調(diào)用進(jìn)程的進(jìn)程組標(biāo)識(shí)符改為調(diào)用進(jìn)程的進(jìn)程標(biāo)識(shí)符,使其成為進(jìn)程組首進(jìn)程,并返回這一新的進(jìn)程組標(biāo)識(shí)符。
下面我們來看一個(gè)實(shí)例:
/* setuid.c */
main(argc, argv)
int argc;
char *argv[];
{
int ret, uid;
uid = atoi(argv[1]);
printf("Before uid=%d, euid=%d\n", getuid(), geteuid());
ret = setuid(uid);
printf("After uid=%d, euid=%d\n", getuid(), geteuid());
printf("ret = %d\n", ret);
}
下面分三種情況討論該程序的執(zhí)行:
1、 如果執(zhí)行該程序的用戶為超級(jí)用戶,則只要命令行所給的用戶標(biāo)識(shí)符大于0,無論所給的用戶標(biāo)識(shí)符是否存在,執(zhí)行總能成功。
#./setuid 3434
Before uid=0, euid=0
After uid=3434, euid=3434
ret = 0
#
結(jié)果分析:將進(jìn)程的實(shí)際和有效用戶標(biāo)識(shí)符均改為3434。
2、 如果執(zhí)行該程序的用戶為一般用戶,用id命令得到用戶uid和gid后,再調(diào)用該程序,過程如下:
%id
uid=1111(yds) gid=20(user)
%./setuid 3434
Before uid=1111, euid=1111
After uid=1111, euid=1111
ret = -1
%./setuid 1111
Before uid=1111, euid=1111
After uid=1111, euid=1111
ret = 0
%
結(jié)果分析:當(dāng)命令行參數(shù)為1111時(shí),setuid執(zhí)行成功,因?yàn)橛脩舻膗id就是1111。
值得注意的是:注冊(cè)程序login
是個(gè)典型的setuid系統(tǒng)調(diào)用程序,login進(jìn)程的有效用戶是超級(jí)用戶,該進(jìn)程在建立用戶的Shell進(jìn)程前,調(diào)用setuid將實(shí)際和有效用戶標(biāo)識(shí)符調(diào)整為注冊(cè)用戶的用戶實(shí)際和有效標(biāo)識(shí)符。
第六章 設(shè)備輸入/輸出控制
6.1 概述
UNIX將設(shè)備看成文件,這是UNIX的一大特色。這里需要介紹一個(gè)設(shè)備號(hào)的概念。設(shè)備特別文件與兩個(gè)設(shè)備號(hào)有關(guān)-主設(shè)備號(hào)和次設(shè)備號(hào)。主設(shè)備號(hào)告訴操作系統(tǒng),當(dāng)涉及文件名時(shí),將使用哪種設(shè)備類型。對(duì)于每一種類型的設(shè)備都有一段駐留在操作系統(tǒng)中的程序代碼,以控制相應(yīng)類型的設(shè)備,這段代碼被稱為"設(shè)備驅(qū)動(dòng)程序"。次設(shè)備號(hào)被傳遞給設(shè)備驅(qū)動(dòng)程序,這個(gè)號(hào)碼用來決定使用哪種物理設(shè)備。例如,決定在一塊多重驅(qū)動(dòng)控制卡上,哪個(gè)磁盤驅(qū)動(dòng)器將被訪問,以及該磁盤驅(qū)動(dòng)器中哪一部分將被使用;或者,當(dāng)一個(gè)磁盤驅(qū)動(dòng)器所請(qǐng)求的操作已經(jīng)完成后,應(yīng)該被恢復(fù)原狀。幾個(gè)設(shè)備(如同類型的磁盤驅(qū)動(dòng)器)可以用同一個(gè)主設(shè)備號(hào),但它們將有不同的次設(shè)備號(hào)?聪旅娴睦樱
%ls -l /dev/ttyq*
crw--w---- 2 yds user 15, 1 2月 17日 09時(shí)03分 ttyq1
crw--w---- 2 yds user 15, 14 2月 16日 17時(shí)00分 ttyq14
%
上例中15是主設(shè)備號(hào),1和14是次設(shè)備號(hào)。
用戶可以使用系統(tǒng)提供的統(tǒng)一而且獨(dú)立于設(shè)備的界面-對(duì)文件進(jìn)行操作的系統(tǒng)調(diào)用來操作設(shè)備,而沒有必要涉及設(shè)備的具體細(xì)節(jié)。大部分對(duì)文件進(jìn)行操作的系統(tǒng)調(diào)用對(duì)它們?nèi)云鹱饔茫,用open打開設(shè)備,用read/write對(duì)設(shè)備進(jìn)行讀/寫,設(shè)備操作完成后,用close關(guān)閉設(shè)備。但有的系統(tǒng)調(diào)用在對(duì)設(shè)備文件進(jìn)行操作時(shí),其功效有所不同。如create及open的創(chuàng)建方式都不能創(chuàng)建設(shè)備文件。
6.2 設(shè)備輸入/輸出控制-ioctl系統(tǒng)調(diào)用
ioctl是UNIX系統(tǒng)專門提供的用于設(shè)備控制的系統(tǒng)調(diào)用。該系統(tǒng)調(diào)用與設(shè)備類型(即主設(shè)備號(hào))相關(guān)。不同的設(shè)備,系統(tǒng)提供了不同的控制命令。
ioctl的調(diào)用格式是:
ioctl(int fd, int cmd,arg…)
說明:參數(shù)fd是一設(shè)備文件的文件描述字,cmd是控制命令,它與設(shè)備相關(guān),不同類型的設(shè)備有不同的控制命令。參數(shù)arg沒有固定的數(shù)據(jù)結(jié)構(gòu),它隨cmd的不同而不同。
第七章 高級(jí)編程
7.1 處理信號(hào)
信號(hào)是UNIX進(jìn)程間最基本的通訊手段,主要作用是實(shí)現(xiàn)進(jìn)程間異步事件的通訊。信號(hào)是傳送到進(jìn)程的"軟中斷",它通知進(jìn)程在它們的環(huán)境中出現(xiàn)了非正常事件。進(jìn)程接收到信號(hào)后要進(jìn)行處理,處理方式為以下四種之一:
(1)
缺省方式(SIG_DFL):這是進(jìn)程對(duì)信號(hào)的一般處理方式,在無特殊情況下,進(jìn)程在接收到信號(hào)后將終止執(zhí)行。有一些信號(hào),在終止進(jìn)程運(yùn)行前需將終止進(jìn)程的正文段、數(shù)據(jù)段、user結(jié)構(gòu)和棧段內(nèi)容寫到當(dāng)前目錄的core文件中,以備調(diào)試工具分析與使用。
(2)
忽略方式(SIG_IGN):進(jìn)程接收到一個(gè)已指明忽略的信號(hào),則將該信號(hào)清除后,立即返回,不在任何工作。信號(hào)SIGKILL不能被忽略。
(3) 保持方式(SIG_HOLD):當(dāng)進(jìn)程處于該方式時(shí),將接收的信號(hào)保存起來,等該進(jìn)程的保持方式解除后,再進(jìn)行處理。
(4)
捕獲方式(設(shè)置信號(hào)處理函數(shù)):這是用戶設(shè)置的信號(hào)處理方式,當(dāng)進(jìn)程接收到這種信號(hào)時(shí),執(zhí)行用戶設(shè)置的信號(hào)處理函數(shù),執(zhí)行完后,恢復(fù)現(xiàn)場(chǎng),然后繼續(xù)往下執(zhí)行。
1. 常用信號(hào)種類
UNIX信號(hào)的種類很多,下面介紹一些最常用的信號(hào):
SIGHUP 掛斷。這是當(dāng)控制終端被掛起時(shí)送到進(jìn)程的信號(hào)。
SIGINT 中斷。由鍵盤產(chǎn)生的中斷。
SIGQUIT 退出。由鍵盤產(chǎn)生的中斷。
SIGKILL 終止。這個(gè)信號(hào)不能被捕獲、阻塞或忽略。
SIGALRM 定時(shí)信號(hào)。
SIGTERM 軟件終止信號(hào)。
SIGUSR1 用戶定義的信號(hào)1。
SIGUSR2 用戶定義的信號(hào)2。
這些信號(hào)值的聲明在/usr/include/sys/signal.h文件中。
2. 發(fā)送信號(hào)-kill系統(tǒng)調(diào)用
用戶傳送信號(hào)到進(jìn)程的系統(tǒng)調(diào)用是kill,調(diào)用格式為:
#include
#include
int kill (pid_t pid, int sig);
說明:該系統(tǒng)調(diào)用把一個(gè)信號(hào)值為sig的信號(hào)發(fā)送給進(jìn)程標(biāo)識(shí)符為pid的相關(guān)進(jìn)程。成功時(shí)返回0,失敗時(shí)返回-1。
該調(diào)用執(zhí)行成功與否,依賴于調(diào)用進(jìn)程的有效用戶標(biāo)識(shí)符和參數(shù)pid的值,pid值的含義如下:
大于0:將信號(hào)發(fā)送給進(jìn)程號(hào)等于pid的進(jìn)程。
等于0:將信號(hào)發(fā)送給調(diào)用進(jìn)程的同組進(jìn)程(0和1進(jìn)程除外)。
等于-1:將信號(hào)發(fā)送給實(shí)際用戶標(biāo)識(shí)符等于調(diào)用進(jìn)程的有效用戶標(biāo)識(shí)符的所有進(jìn)程(0和1進(jìn)程除外),如調(diào)用進(jìn)程的有效用戶是超級(jí)用戶,則將信號(hào)發(fā)送給除0和1進(jìn)程外的所有進(jìn)程。
非-1的負(fù)數(shù):將信號(hào)發(fā)送給進(jìn)程組標(biāo)識(shí)符為pid的絕對(duì)值的所有進(jìn)程。
在實(shí)際編程中,kill系統(tǒng)調(diào)用非常有用,具體說來:
·常用方式
kill(pid,SIGUSR1) 向進(jìn)程號(hào)為pid的進(jìn)程發(fā)送信號(hào)SIGUSR1
·用來判斷進(jìn)程是否存在:
if (kill(pid,0) == 0)
進(jìn)程號(hào)為pid的進(jìn)程存在;
else
進(jìn)程號(hào)為pid的進(jìn)程不存在!
·用來殺掉子進(jìn)程
kill(pid,1) 殺掉進(jìn)程號(hào)為pid的進(jìn)程
2. 處理信號(hào)-signal系統(tǒng)調(diào)用
用戶處理信號(hào)的系統(tǒng)調(diào)用是signal,調(diào)用格式為:
#include
void (*signal (int sig, void (*func)()))();
說明:參數(shù)sig是一個(gè)信號(hào)值,func定義了該信號(hào)的處理方式。該系統(tǒng)定義的功能是按func的定義設(shè)置調(diào)用進(jìn)程對(duì)信號(hào)sig的處理方式。執(zhí)行成功時(shí),返回調(diào)用進(jìn)程先前對(duì)信號(hào)sig處理方式的值,失敗則返回-1。參數(shù)取值為SIG_DFL或SIG_IGN或用戶信號(hào)處理函數(shù)的地址時(shí),分別表示缺省方式、忽略方式和捕獲方式。
3.pause系統(tǒng)調(diào)用
pause系統(tǒng)調(diào)用的格式為:
pause()
說明:該調(diào)用沒有參數(shù),其功能為使調(diào)用進(jìn)程睡眠直到其接收到一信號(hào)為止。該系統(tǒng)調(diào)用的結(jié)果依賴于調(diào)用進(jìn)程對(duì)接收到的信號(hào)的處理方式。
缺省方式:終止調(diào)用進(jìn)程,pause無返回值;
忽略方式:進(jìn)程不受該信號(hào)的影響,繼續(xù)睡眠;
捕獲方式:調(diào)用進(jìn)程從信號(hào)處理函數(shù)返回后,繼續(xù)往下執(zhí)行。
4. 使用信號(hào)定時(shí)-alarm系統(tǒng)調(diào)用
系統(tǒng)調(diào)用alarm可以實(shí)現(xiàn)定時(shí)器的功能,調(diào)用格式為:
#include
unsigned alarm(unsigned sec);
說明:參數(shù)sec指定定時(shí)的時(shí)間間隔,以秒為單位。用戶進(jìn)程可以先通過signal調(diào)用指定SIGALRM信號(hào)對(duì)應(yīng)的捕獲函數(shù),然后調(diào)用alarm來設(shè)定鬧鐘,在定時(shí)這段時(shí)間內(nèi)做自己的工作。定時(shí)時(shí)間一到,進(jìn)程就接收到一個(gè)SIGALRM信號(hào),并執(zhí)行該信號(hào)對(duì)應(yīng)的捕獲函數(shù)。系統(tǒng)調(diào)用alarm在多進(jìn)程編程中非常有用。
7.2 管道通訊
用信號(hào)來處理異常事件或錯(cuò)誤是非常合適的,但它用來處理進(jìn)程之間的大量信息傳送,就非常不適宜。為此,UNIX又提供了一種稱為管道的機(jī)構(gòu),主要處理進(jìn)程間的大量信息傳送。所謂管道是指進(jìn)程間連接起來的一條通訊通道。它也UNIX文件概念的一種推廣,管道通訊的介質(zhì)是文件,稱為管道文件。用戶可以用文件操作的有關(guān)系統(tǒng)調(diào)用來操作管道文件,從而簡(jiǎn)化管道應(yīng)用程序的設(shè)計(jì)。管道的形象描述如下圖:
write 寫端 讀端 read
管道是UNIX最強(qiáng)大而最有特色的性能之一,特別是在命令行這一級(jí),它允許任意的命令被順序連接起來。例如:
%who | wc -l
該命令通過管道把命令who的輸出送給字計(jì)數(shù)程序wc,選項(xiàng)-l告訴wc只計(jì)算行數(shù)。通過wc最終輸出的系統(tǒng)已注冊(cè)的用戶個(gè)數(shù)。
1. 管道程序設(shè)計(jì)
在程序中可以用系統(tǒng)調(diào)用pipe建立一個(gè)管道。如果建立成功,就返回兩個(gè)文件描述符,一個(gè)用于寫入管道,一個(gè)用于從管道中讀出。Pipe調(diào)用的格式如下:
int filedes[2], retval;
retval = pipe(filedes);
其中,fildes是一個(gè)含有兩個(gè)整數(shù)的數(shù)組,用來存放標(biāo)識(shí)管道的兩個(gè)文件描述符。如果調(diào)用成功,filedes[0]將被打開用于從管道讀,fildes[1]將被打開用于向管道寫。
管道一旦建立,就能直接用read和write操作它。當(dāng)管道與系統(tǒng)調(diào)用fork聯(lián)用時(shí),才能體現(xiàn)出管道的真正價(jià)值。這時(shí),可以利用父進(jìn)程已打開的文件,對(duì)于其子進(jìn)程仍保持打開這一事實(shí)。下面的程序先建立一個(gè)管道,然后調(diào)用fork創(chuàng)建子進(jìn)程,父進(jìn)程通過管道向子進(jìn)程發(fā)送信息。
/* pipe.c */
#include
#define MSGSIZE 16
char *msg1 = "hello, world#1";
char *msg2 = "hello, world#2";
char *msg3="hello, world#3";
main(argc,argv)
int argc;
char **argv;
{
char inbuf[MSGSIZE];
int p[2], pid,j;
/* 打開管道 */
if (pipe(p)
/* 在父進(jìn)程中向管道寫入 */
if (pid >0 ){
write(p[1], msg1, MSGSIZE);
write(p[1], msg2, MSGSIZE);
write(p[1], msg3, MSGSIZE);
wait((int *)0);
}
/* 在子進(jìn)程中從管道讀入 */
if (pid == 0){
for (j=0; j
程序的輸入結(jié)果如下:
Child Read:hello, world#1
Child Read:hello, world#2
Child Read:hello, world#3
管道是在先進(jìn)先出的基礎(chǔ)上處理數(shù)據(jù)的。所以,首先放入管道的數(shù)據(jù),在其另一端首先被讀出。這個(gè)順序不能被改變,因?yàn)橄到y(tǒng)調(diào)用lseek不能用于管道。
2. 命名管道-FIFO
我們已經(jīng)看到,管道是一種功能很強(qiáng)的進(jìn)程通訊機(jī)構(gòu)。但是,它也存在一些嚴(yán)重的缺點(diǎn)。
首先,管道只能用于連接具有共同祖先的進(jìn)程,如父子進(jìn)程之間的連接。當(dāng)要開發(fā)一個(gè)永遠(yuǎn)保持存在的,提供為全系統(tǒng)范圍服務(wù)的程序時(shí),這一缺點(diǎn)就更加突出,例如網(wǎng)絡(luò)控制服務(wù)程序和打印機(jī)的假脫機(jī)程序等。我們要求調(diào)用進(jìn)程應(yīng)該能夠用管道與任何服務(wù)進(jìn)程進(jìn)行通訊,然后再脫開。遺憾的是,普通管道不能實(shí)現(xiàn)上述功能。
其次,管道不能是常設(shè)的,在需要時(shí)可以建立它們,但是當(dāng)訪問它們的進(jìn)程終止時(shí),管道也隨之被撤銷。所以,它們不可能永久存在。
事實(shí)上,UNIX系統(tǒng)中的FIFO機(jī)制(又稱命名管道),彌補(bǔ)了上述管道的不足之處。FIFO與管道一樣,也是作為進(jìn)程之間先進(jìn)先出的通訊通道,但是FIFO是一種永久性的機(jī)構(gòu),并且具有一個(gè)UNIX文件名。FIFO也具有文件主、長(zhǎng)度和訪問權(quán)限。它能象其他UNIX文件那樣被打開、關(guān)閉和刪除。但在讀和寫時(shí),其性能與管道相同。
在討論FIFO程序設(shè)計(jì)之前,我們先來看一下FIFO在命令級(jí)的使用。UNIX命令mknod可以用來創(chuàng)建一個(gè)FIFO文件channel:
%/etc/mknod channel p
%ls -l channel
prw-r--r-- 1 yds user 0 2月 17日 14時(shí)19分 channel
命令ls的輸出結(jié)果中的首字母p指出channel是一個(gè)FIFO類型的文件。從中我們還可以看到其訪問權(quán)限為文件主可讀寫,組內(nèi)及其他用戶只讀。其用戶主是yds,所屬組為user,長(zhǎng)度為0,此外還有文件建立的時(shí)間。
FIFO程序設(shè)計(jì)大部分與管道相同,最主要的區(qū)別是在建立方面。FIFO是用mknod調(diào)用建立的,而不是用pipe建立的。另外,必須把八進(jìn)制數(shù)010000(定義在文件/usr/include/sys/stat.h的常量S_IFIFO中)加入文件模式中,以指明這是一個(gè)FIFO。下面是一個(gè)建立FIFO的例子:
if (mknod("fifo", 010600,0)
.
.
fd = open("fifo", O_WRONLY);
實(shí)現(xiàn)打開一個(gè)FIFO文件用于寫,下面的例子用于以非阻塞方式打開FIFO文件用于讀:
if ((fd = open("fifo", O_RDONLY | O_NDELAY))
首先是sendfifo.c的程序清單,用于向FIFO文件寫入字串:
/* sendfifo.c */
#include
#include
#include
#define MSGSIZ 63
extern int errno;
main(argc,argv)
int argc;
char **argv;
{
int fd;
char buf[MSGSIZ+1];
int i,nwrite;
if (argc
if ((fd = open("fifo", O_WRONLY | O_NDELAY))
for ( i =1 ; i MSGSIZ) {
fprintf(stderr, " message too long %s!\n", argv);
continue;
}
strcpy(buf, argv);
if ((nwrite = write(fd,buf,MSGSIZ+1))
下面是recvfifo.c的程序清單,實(shí)現(xiàn)從FIFO的讀入:
#include
#include
#define MSGSIZ 63
main(argc,argv)
int argc;
char **argv;
{
int fd;
char buf[MSGSIZ+1];
mknod("fifo",010600,0);
if ((fd = open("fifo", O_RDWR))
運(yùn)行結(jié)果如下:
%recvfifo &
[1] 1706
%sendfifo hello world
FIFO message received: hello
FIFO message received: world
%
首先,運(yùn)行recvfifo程序創(chuàng)建FIFO文件"fifo",并打開文件"fifo"用于讀;然后,運(yùn)行sendfifo程序發(fā)送字符串"hello
world",寫入文件"fifo"中。
7.3 IPC通訊機(jī)制
1. IPC概述
IPC是UNIX
系統(tǒng)V提供的一套新的進(jìn)程間通訊進(jìn)制,它大大增強(qiáng)了進(jìn)程間的通訊功能。IPC機(jī)構(gòu)包括三種:消息、信號(hào)量和共享內(nèi)存。三種IPC機(jī)構(gòu)的程序設(shè)計(jì)接口比較相似,這說明它們的內(nèi)核實(shí)現(xiàn)是相似的。IPC最重要的通用特性就是鍵,鍵是UNIX系統(tǒng)中標(biāo)識(shí)IPC目標(biāo)的一個(gè)數(shù),其方式類似于一個(gè)文件名標(biāo)識(shí)一個(gè)文件。也就是說,鍵可以使多個(gè)進(jìn)程容易共享IPC資源。鍵所標(biāo)識(shí)的目標(biāo)可以是一個(gè)消息隊(duì)列、一組信號(hào)量或一個(gè)共享內(nèi)存段。鍵的實(shí)際數(shù)據(jù)類型由實(shí)現(xiàn)有關(guān)的類型key_t決定,它在頭文件/usr/include/sys/types.h中被定義。
當(dāng)建立一個(gè)IPC目標(biāo)時(shí),系統(tǒng)也建立了一個(gè)IPC機(jī)構(gòu)的狀態(tài)結(jié)構(gòu),其中包含該目標(biāo)有關(guān)的管理信息。對(duì)于消息隊(duì)列、信號(hào)量和共享內(nèi)存均有一種狀態(tài)結(jié)構(gòu)類型,每種類型必須含有僅與特定IPC機(jī)構(gòu)有關(guān)的信息。但是,這三種狀態(tài)結(jié)構(gòu)類型都有有關(guān)權(quán)限結(jié)構(gòu),這種權(quán)限結(jié)構(gòu)的類型用ipc_perm來標(biāo)識(shí),它包含以下內(nèi)容:
u_short cuid /* IPC目標(biāo)創(chuàng)建者的用戶ID */
u_short cgid /* 創(chuàng)建者的用戶組ID */
u_short uid /* 有效用戶ID */
u_short gid /* 有效用戶組ID */
u_short umode /*權(quán)限許可 */
該結(jié)構(gòu)決定一個(gè)用戶是否能對(duì)IPC目標(biāo)進(jìn)行讀/寫。權(quán)限的組成方法與文件的權(quán)限完全一樣。所以,如果umode之值為0644,則表示屬主能讀寫相應(yīng)的目標(biāo),而其他用戶只能讀。注意,有效用戶標(biāo)識(shí)符和有效組標(biāo)識(shí)符(記錄在uid和gid內(nèi))與umode一起確定訪問的許可性。
最后,IPC的每種形式都提供了各種操作功能,以便IPC進(jìn)制可被使用。信息隊(duì)列操作允許消息發(fā)送和接收。信號(hào)量操作允許信號(hào)量增加、減少以及檢測(cè)到某個(gè)值。共享內(nèi)存操作功能允許進(jìn)程加上和減去共享內(nèi)存的部分到它們的地址空間。
2. 消息隊(duì)列
從本質(zhì)上看,一個(gè)消息是一串字符或字節(jié)(不一定以NULL字符結(jié)尾)。進(jìn)程之間通過消息隊(duì)列傳送消息。通過msgget建立或訪問消息隊(duì)列。一個(gè)消息隊(duì)列一旦被建立,只要符合訪問權(quán)限,進(jìn)程就可以通過msgsnd把消息放入隊(duì)列,另一個(gè)進(jìn)程就能用msgrcv讀出該信息。
·msgget系統(tǒng)調(diào)用
msgget調(diào)用格式如下:
#include
#include
#include
int msgget(key_t key, int msgflg);
說明:參數(shù)key是標(biāo)識(shí)消息隊(duì)列的鍵。如果該調(diào)用成功,就建立一個(gè)消息隊(duì)列,或者使一個(gè)已經(jīng)存在的消息隊(duì)列能夠被訪問。調(diào)用返回一個(gè)該消息隊(duì)列的標(biāo)識(shí)符。參數(shù)msgflg確定msgget完成的動(dòng)作?梢匀蓚(gè)常數(shù):
(1)
IPC_CREAT:創(chuàng)建消息隊(duì)列,且在消息隊(duì)列已經(jīng)存在的情況下,不會(huì)被重寫。如果沒有設(shè)置該標(biāo)志,那么當(dāng)隊(duì)列已存在時(shí),msgget就返回該消息隊(duì)列的標(biāo)識(shí)符。
(2)
IPC_EXCL:如果該標(biāo)志與IPC_CREAT都被設(shè)置,本次msgget調(diào)用則只希望建立一個(gè)消息隊(duì)列。所以,當(dāng)給出的鍵值已對(duì)應(yīng)一個(gè)存在的消息隊(duì)列時(shí),調(diào)用失敗,并返回-1。
建立一個(gè)消息隊(duì)列時(shí),msgflg的低9位用來寫出消息隊(duì)列的權(quán)限,這與文件模式一樣。如:
msg_id = msgget((key_t)0100, 0644 |IPC_CREAT|IPC_EXCL);
這個(gè)調(diào)用為鍵值(key_t)0100建立一個(gè)消息隊(duì)列。如果調(diào)用成功,隊(duì)列的權(quán)限為0644,其解釋與文件權(quán)限一樣。
·msgget和msgrcv系統(tǒng)調(diào)用
msgsnd和msgrcv調(diào)用格式如下:
#include
#include
#include
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int
msgflg);
說明:參數(shù)msqid指明消息發(fā)送或接收的隊(duì)列,它的值是通過msgget調(diào)用得到的。消息的結(jié)構(gòu)類型如下:
struct {
long mtype; /* 消息類型 */
char mtext[]; /* 消息正文 */
}
程序員可以根據(jù)這個(gè)結(jié)構(gòu)中的mtype域來對(duì)消息進(jìn)行分類。該域的每種可能的值代表一種不同的類別。mtext域用來存放消息正文,正文大小可用用戶設(shè)定。
系統(tǒng)調(diào)用msgsnd的參數(shù)msgsz指定發(fā)送消息的實(shí)際長(zhǎng)度,其范圍可以從0到系統(tǒng)規(guī)定的消息最大長(zhǎng)度。系統(tǒng)調(diào)用msgrcv中的參數(shù)msgsz指定給出了結(jié)構(gòu)內(nèi)能存放消息的最大長(zhǎng)度。如果調(diào)用成功,msgrcv返回接收到的消息的實(shí)際長(zhǎng)度。
兩個(gè)系統(tǒng)調(diào)用中的參數(shù)msgflg中有個(gè)IPC_NOWAIT。如果沒有設(shè)定它,那么調(diào)用進(jìn)程就會(huì)進(jìn)入睡眠狀態(tài)。否則調(diào)用就會(huì)立即返回。
·msgctl系統(tǒng)調(diào)用
msgctl調(diào)用格式如下:
#include
#include
#include
int msgctl(int msqid, int cmd, .../* struct msqid_ds *buf */);
說明:msgctl用來獲取和修改一個(gè)已經(jīng)存在的消息隊(duì)列的屬性。參數(shù)msgqid是消息隊(duì)列的ID,命令常量cmd的取值有三種:
(1) IPC_STAT:放置關(guān)于結(jié)構(gòu)中消息隊(duì)列當(dāng)前消息的一個(gè)備份。
(2) IPC_SET:為消息隊(duì)列設(shè)置控制變量值。
(3) IPC_RMID:從系統(tǒng)中刪除消息隊(duì)列,但是只有超級(jí)用戶或隊(duì)列屬主才能實(shí)現(xiàn)。
3. 信號(hào)量(略)
4. 共享內(nèi)存
共享內(nèi)存操作允許兩個(gè)或兩個(gè)以上進(jìn)程共享一個(gè)物理存貯器段,它是所有IPC中效力最高的一種。一個(gè)共享內(nèi)存段被唯一的標(biāo)識(shí)符所描述。
·shmget系統(tǒng)調(diào)用
shmget調(diào)用格式如下:
#include
#include
#include
int shmget(key_t key, size_t size, int shmflg);
說明:參數(shù)key是標(biāo)識(shí)共享內(nèi)存的鍵。參數(shù)size是創(chuàng)建或訪問共享內(nèi)存的大小。如果調(diào)用成功,就創(chuàng)建一塊共享內(nèi)存,或者使一塊已經(jīng)存在的共享內(nèi)存能夠被訪問。調(diào)用返回一個(gè)該共享內(nèi)存的標(biāo)識(shí)符。參數(shù)shmflg同調(diào)用msgget,semget中的參數(shù)msgflg,
semflg一樣。
·shmat和shmdt系統(tǒng)調(diào)用
shmat和shmdt調(diào)用格式如下:
#include
#include
#include
void *shmat(int shmid, void *shmaddr, int shmflg);
int shmdt (void *shmaddr);
說明:shmat調(diào)用把參數(shù)shmid標(biāo)識(shí)的內(nèi)存段連到調(diào)用進(jìn)程的一個(gè)有效地址上。調(diào)用成功,shmat返回該地址memptr。參數(shù)shmaddr給出程序員在調(diào)用所選地址的控制。參數(shù)shmflg由標(biāo)志SHM_RDONLY和SHM_RND構(gòu)成。前者請(qǐng)求被連之段為只讀,后者用于shmat處理shmaddr非0的情況。
Shmdt的功能與shmat剛好相反,它實(shí)現(xiàn)把一個(gè)共享內(nèi)存段從進(jìn)程的邏輯地址空間中分離出來。這意味著進(jìn)程將不再使用它。
·shmctl系統(tǒng)調(diào)用
shmclt調(diào)用格式如下:
#include
#include
#include
int shmctl (int shmid, int cmd, .../* struct shmid_ds *buf */);
說明:這個(gè)調(diào)用實(shí)現(xiàn)對(duì)共享內(nèi)存的操作控制,其使用與msgctl完全一樣,其參數(shù)cmd可以取IPC_STAT、IPC_SET和IPC_RMID。
第八章 網(wǎng)絡(luò)編程
8.1 概述
本章介紹UNIX網(wǎng)絡(luò)編程-即網(wǎng)間進(jìn)程通訊。UNIX網(wǎng)間進(jìn)程通訊是通過通訊應(yīng)用程序接口(API)來實(shí)現(xiàn)的。目前,在UNIX環(huán)境下最流行的API是伯克利套接字(Socket)和UNIX
System V的傳送層接口(TLI)。我們主要介紹套接字API。
Socket通過域 (domain)來劃分所支持的協(xié)議, 目前支持的域有:
UNIX域支持在UNIX系統(tǒng)中的進(jìn)程通訊、Internet域支持TCP/IP協(xié)議等。
Socket的實(shí)現(xiàn)者試圖以UNIX文件的操作語義來模擬進(jìn)程通訊的操作,其操作方式與文件操作有許多對(duì)應(yīng)。例如,socket(
)調(diào)用可近似的看成是open( )調(diào)用,調(diào)用返回的文件描述字作為其他調(diào)用的第一參數(shù);socket 中也使用了read 和write
調(diào)用,其語法和語義與文件操作中的read 和 write 調(diào)用幾乎完全一致。Socket中的調(diào)用 bind、connect和accept
顯示了建立網(wǎng)絡(luò)連接的方法。 如圖8-1所示。Socket進(jìn)程通訊仍使用Client/Server 模型,建立連接時(shí),Client 和
Server 所做的工作是不對(duì)稱的。
8.2 套接字編程接口說明
下面結(jié)合實(shí)例來說明套接字編程接口。
·socket系統(tǒng)調(diào)用
實(shí)現(xiàn)套接字的分配,調(diào)用格式如下:
#include
#include
int socket(int domain, int type, int protocol);
其中:參數(shù)domain是一個(gè)常量,它規(guī)定區(qū)域,常用的是AF_INET;參數(shù)type是一個(gè)常量,規(guī)定套接字的類型,可以是SOCK_STREAM,SOCK_DGRAM或SOCK_RAW;protocol是一個(gè)常量,規(guī)定所用的協(xié)議。此參數(shù)僅在type為SOCK_RAW時(shí)有意義,其他情況下忽略。此參數(shù)為0時(shí)選擇默認(rèn)協(xié)議。
·bind系統(tǒng)調(diào)用
當(dāng)應(yīng)用程序獲得套接字后,可以使用bind()調(diào)用為套接字聯(lián)系一個(gè)獨(dú)一無二的名字,如下面一段代碼:
struct sockaddr_in serverAddress ;
memset( (char *)&serverAddress , 0 , sizeof(struct sockaddr_in) ) ;
serverAddress.sin_family =AF_INET ;
serverAddress.sin_addr.s_addr =inet_addr("202.96.6.15") ;
serverAddress.sin_port = htons( 7000);
if( bind( sockfd , &serverAddress ,sizeof( struct sockaddr_in ) )
==-1 )
{
perror( "bind error" ) ;
exit( 2 ) ;
}
這段代碼說明SERVER程序運(yùn)行在IP地址202.96.6.15,端口號(hào)為7000上。Bind調(diào)用之后相當(dāng)于將自己的服務(wù)地址公布出去。
bind的調(diào)用格式如下:
#include
#include
int bind (int s, const struct sockaddr *name, int namelen);
其中:參數(shù)s是socket調(diào)用返回的文件描述字,參數(shù)name是指向結(jié)構(gòu)sockaddr的指針,參數(shù)namelen指定結(jié)構(gòu)的大小。
·listen系統(tǒng)調(diào)用
在bind調(diào)用之后,SERVER程序使用listen調(diào)用來準(zhǔn)備接收來自CLIENT的連接。listen的調(diào)用格式如下:
#include
#include
int listen (int s, int backlog);
其中:參數(shù)s是socket調(diào)用返回的文件描述字,參數(shù)backlog指定最大連接數(shù)。
·accept系統(tǒng)調(diào)用
在listen調(diào)用之后,SERVER程序使用accept調(diào)用實(shí)際接收來自CLIENT的連接請(qǐng)求。accept的調(diào)用格式如下:
#include
#include
int accept (int s, struct sockaddr *addr, int *addrlen);
其中:參數(shù)s是socket調(diào)用返回的文件描述字,參數(shù)addr指向結(jié)構(gòu)sockaddr,負(fù)責(zé)讀入CLIENT端的相應(yīng)信息。參數(shù)addrlen指出addr對(duì)應(yīng)結(jié)構(gòu)的長(zhǎng)度。
·connect系統(tǒng)調(diào)用
在CLIENT方,調(diào)用socket之后,就可使用connect調(diào)用向SERVER初始化一個(gè)連接請(qǐng)求。如下面的代碼:
struct sockaddr_in serverAddress ;
memset( (char *)&serverAddress , 0 , sizeof(struct sockaddr_in) ) ;
serverAddress.sin_family =AF_INET ;
serverAddress.sin_addr.s_addr =inet_addr("202.96.6.15") ;
serverAddress.sin_port = htons( 7000);
if( connect( sockfd , &serverAddress ,sizeof( struct sockaddr_in ) )
==-1 )
{
perror( "bind error" ) ;
exit( 2 ) ;
}
這段代碼完成了向運(yùn)行在IP地址202.96.6.15,端口號(hào)為7000上的SERVER程序建立連接。
connect的調(diào)用格式如下:
#include
#include
int connect (int s, const struct sockaddr *name, int namelen);
其中:參數(shù)s是socket調(diào)用返回的文件描述字,參數(shù)name是指向結(jié)構(gòu)sockaddr的指針,參數(shù)namelen指定結(jié)構(gòu)的大小。
·read/write/close系統(tǒng)調(diào)用
與普通文件操作類似。
:em23:
本文來自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog.chinaunix.net/u2/84425/showart_2069357.html |
|