13-系統程序2.ppt_第1頁
13-系統程序2.ppt_第2頁
13-系統程序2.ppt_第3頁
13-系統程序2.ppt_第4頁
13-系統程序2.ppt_第5頁
已閱讀5頁,還剩51頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、第十三章 UNIX系統程序設計(二),系統調用返回值 (page 323),大多數的系統調用都返回一個值。如打開一個文件的系統調用fd=open(name,mode)返回一個文件描述符fd。 當指定的文件不存在時返回 -1。返回值 -1指示一個系統調用可能失敗了,這時系統的全局變量errno值為相應的出錯代碼。 系統還定義了另外兩個外部變量,即對應于出錯代碼的消息數組sys_errlist和比該數組最大下標大1的整型變量sys_neer。 為了在系統調用失敗后獲得出錯代碼和出錯信息,我們可以編寫一個如下的C函數:,#include void syserr(syscall) char *sysc

2、all; extern int errno,sys_nerr; extern const char const *sys_errlist; fprintf(stderr,ERROR: %s %d,syscall,errno); if(errno0 ,13.1文件系統程序設計,13.1.1獲取文件的狀態 在C語言程序設計中,有時需要獲得有關文件的類型、大小、文件主及時間信息,這可通過系統調用stat和fstat來獲取。 include include int stat(pathname,sbuf) char pathname; int fstat(fd,sbuf) int fd; struct

3、stat *sbuf; stat和fstat都是從一個文件的i節點獲得有關狀態信息的。stat是根據參數pathname給出的文件路徑名,通過搜索目錄項結構來獲取文件的外存i節點,fstat是根據參數fd給出的打開文件描述符,通過打開文件結構獲取內存i節點。,stat結構定義如下:,struct stat dev_t st_dev; /* i節點所在設備號*/ ino_t st_ino; /* i節點號(ushort) */ ushort st_mode; /* 文件模式 */ short st_nlink; /* 文件鏈接數(short)*/ ushort st_uid; /* 文件主用戶標

4、識符 */ ushort st_gid; /* 文件用戶組標識符 */ dev_t st_rdev; /* 針對設備特別文件的設備號*/ off_t st_size; /* 文件的當前大小,特別文件為0*/ time_t st_atime; /* 文件存取時間(long)*/ time_t st_mtime; /* 文件修改時間(long)*/ time_t st_ctime; /* 文件的創建時間*/ 用fstat可以通過打開的無名管道文件描述符,訪問“隱藏”的i節點,獲取其狀態信息,而stat調用則無能為力。,13.1.2搜索目錄樹,有時,用戶需要在一棵目錄樹的范圍內對文件和目錄執行某些操

5、作。例行程序ftw能從指定的目錄開始掃描目錄樹,并對找到的每一個目錄項,調用用戶定義的函數。ftw函數的格式如下: #include int ftw(path,func,depth) char *path; /* 指向目錄路徑名 */ int func(); /* 用戶定義的處理函數 */ int depth; /* 即可同時打開的文件個數 */,用戶定義的函數的格式:,int func(name,statptr,type) char *name; /* 存放ftw找到的目標名 */ struct stat *statptr; /* 指向stat結構指針, ftw在該結構中存放目標的狀態信息

6、*/ int type; /* ftw指示目標的類型 和執行狀態*/ /* body of function */ ,參數type的目標類型,參數type的目標類型在ftw.h中定義,類型取值為: FTW_F 目標是文件 FTW_D 目標是目錄 FTW_DNR 目標是不能讀的目錄 FTW_NS 目標不能被stat成功地執行 如果目標是不能讀的目錄,那么此目錄的所有下級也不能處理。對于stat不能成功執行的目標,那么傳送給用戶的stat結構中的內容是無效的。 在調用ftw中,如用戶定義的函數返回非0值,ftw就中止掃描,并把此時用戶函數的返回值作為ftw的返回值。如ftw在執行中出錯,會使ftw

7、返回 -1,同時在errno中設置出錯代碼。,下面的程序使用ftw及dspstatus函數輸出一棵目錄樹中所有帶有路徑的目錄或文件名,對于文件還顯示文件的類型、i節點號及文件鏈接數,對于特別文件還顯示主次設備號。用戶也可以在自定義函數中執行其他類型的操作。 主程序從參數中獲得一個作為目錄樹掃描起點的路徑名,該參數的缺省值為當前目錄。,#incude #incude #incude int dspstatus(name,statptr,type) char *name; struct stat *statptr; int type; switch(type) case FTW_DNR: prin

8、tf(-30st: Dir cant readn,name); case FTW_NS:/* 執行失敗 */ return(0); default:/* 正常狀態 */ break; printf(-30st:,name);,switch(statprt-st_mode .,main(argc,argv) int argc; char *argv; int dspstatus(); if(argc2) ftw(.,dspstatus,2); else ftw(argv1,dspstatus,2); exit(0); ,13.2用文件的系統調用實現進程通信,13.2.1利用文件的系統調用實現信號

9、燈 可以用creat系統調用生成一個新文件,但如該文件原已存在,則調用進程要對其有寫權限才能將該文件的長度截為零。利用這一特性可以將一個預先約定好的文件作為信號燈。 當若干進程要訪問臨界資源時,規定遵循以下協議:先試圖創建同一個沒有寫權限的文件,這樣只有一個進程才能獲得成功,允許其進入臨界區,其他進程的creat操作將失敗(返回值為-1)。 進程從臨界區退出時就刪除這個作為信號燈的文件,這樣正在等待中的進程的一個就能成功地創建它。,13.2.2利用管道實現進程間通信,用C語言實現含有管道符的UNIX復合命令 who | wc l,父進程,子進程1,子進程2,圖象改換,圖象改換,who_wc()

10、 /* who | wc */ int pfd2; if(pipe(pfd)=-1) syserr(pipe); switch(fork() case -1: syserr(fork); case 0:/* 子進程1 */ if(close(1)=-1) syserr(close); if(dup(pfd1)!=1) fprintf(stderr,ERROR: dupn); exit(1); ,if(close(pfd0)=-1|close(pfd1)=-1) syserr(close2); execlp(who,who,NULL); switch(fork() case 0:/*子進程2 *

11、/ if(close(0)=-1) syserr(close3); if(dup(pfd0)!=0) fprintf(stderr,ERROR: dup2n); if(close(pfd0)=-1|close(pfd1)=-1) syserr(close4); execlp(wc,wc,-l,NULL); syserr(execlp2); if(close(pfd0)=-1|close(pfd1)=-1) syserr(“close5”); /* 父進程 */ while(wait(NULL)!=-1) ; ,系統調用int dup(int fd)復制一個已存在的文件標識字,返回同一個文件或管

12、道的新的文件標識字,該文件標識字是當前可用的最小文件標識字。兩個文件標識字共享一個文件讀寫指針。 利用dup系統調用使用編號最小的可用文件標識字的規則,可以使對任何文件或管道的讀寫與所希望的文件標識字聯系起來,如使用fd為0的標準輸入與管道讀相聯系,fd為1的標準輸出與管道寫相聯系,這就是管道命令的運行機制。 如將文件的輸入/輸出與標準輸入/輸出相聯系,這就是I/O重定向的運行機制。,為了將前一個命令的標準輸出與一個管道的寫端相連接,后一個命令的標準輸入與同一個管道的讀端相連接,可以先關閉文字標識字0且用dup復制讀管道的文件標識字,由于0是最小的文件標識字,且又剛剛使它處于“空閑”可用的狀態

13、,故dup將返回標識字為0的管道的讀端。 同樣也可使標識字1成為管道的寫端。 接下來如果再關閉管道文件的原先兩個文件標識字,就構成標識字為0和1的管道的讀寫端。 由于子進程和通過exec命令執行的進程繼承了父進程的所有文件標識字和打開文件結構,故用這種方法可以建立管道命令符左右兩邊的命令輸出和輸入的聯系。,父進程首先生成一個管道文件,再產生第一個子進程。 子進程繼承了父進程的標準文件標識字和管道文件標識字,接著關閉標準輸出,復制寫管道文件標識字,其值為1。然后子進程關閉了從父進程繼承來的管道文件的原兩個標識字,使得該管道文件的寫端僅與子進程的標準輸出相連接。接下來子進程改換圖像,執行UNIX命

14、令who。 在who命令的執行期間,繼承了第一個子進程的全部文件標識字和打開文件及管道文件狀態,who命令的標準輸出就源源不斷地寫入管道文件中。 為了建立第二個命令的管道讀端,原父進程又產生了第二個子進程,類似地,將標準輸入與管道文件的讀端相連接,并執行UNIX命令wc,使該命令從管道的讀端接收數據,從而建立了who和wc兩個命令的讀寫聯系。,13.4遠程進程間通信,利用管道通信的進程必須屬于同一進程族,消息通信、共享內存和信號燈只能用于同一臺機器上的進程間通信,它們都不能用于分布在網絡上的進程間通信。UNIX提供了Socket(插座或稱套接字)的通信機構,主要用于異地進程間的通信,也可用于本

15、地進程間通信。 Socket是在傳送層上提供給應用程序的網絡通信接口。Socket通過“域”來劃分所支持的協議,不同的通信域間不能建立通信連接。目前最常用的是支持TCP/IP協議的INET通信域和支持UNIX系統中進程通信的UNIX通信域。,13.4.1 Socket通信概述,通信域,通信域在sys/socket.h中定義: #define AF_UNIX 1 /* 用于UNIX內部 */ #define AF_INET 2 /* 支持UDP,TCP等 */ 每種通信域又有幾種類型方式,它們也在sys/socket.h中定義: #define SOCK_STREAM 1 /* 虛電路方式*/

16、#define SOCK_DGRAM 2 /* 數據報方式 */ Socket模仿UNIX文件操作來實現進程通信操作,將網絡通信看成網絡上兩端進程間的I/O操作。因此定義了類似文件描述符的Socket描述符,以及與文件讀寫相似的數據接收和發送操作。,Socket通信模型,由于建立打開文件結構,取得文件描述符是建立進程與本地文件靜態資源之間的聯系,而建立網絡的連接需要指定協議,經過多次的握手(請求、應答)過程,故網絡上的進程通信要比文件I/O考慮更多的情況。 Socket通信采用了顧客服務員模型,建立連接時,顧客進程與服務員進程所做的工作是不對稱的。圖13-2給出了Socket實現面向連接的進程

17、通信基本過程。,1建立插座,sockfd=socket(domain,type,protocal) int sockfd; 插座標識字 int domain,type,protocal; 通信域,通信方式、 協議 domain指定通信域,取值主要有AF_UNIX,AF_INET。 type指明通信方式,主要取值有SOCK_STREAM,SOCK_DGRAM。 protocal規定了通信協議,一般可選擇0,表示將遵循通信域和插座類型使用的通信協議。當使用INET通信域的數據報方式時,protocal取值為IPPROTO_UDP。 當調用成功,socket返回一個類似于文件描述符的socket描述

18、符號,可為后繼的其他的調用使用。,2聯系socket地址名和socket描述符,int bind(sockfd,myaddr,addrlen) int sockfd,addrlen /* socket描述字,myaddr的地址長度 */ const struct sockaddr *myaddr; /* 名字(UNIX)或地址(互連網域)*/ 對INET通信域,參數myaddr是一個在netinet/in.h中定義的結構sockaddr_in: struct sockaddr_in short sin_family;/* 通信域值為AF_INET */ u_short sin_port; /*

19、 16位端口號 */ struck in_addr sin_addr;/* 32位IP地址 */ ;,3啟動一個連接請求(client),int connect (sockfd, servaddr, addrlen) int sockfd,addrlen; struct sockaddr *servaddr; servaddr是通信信道另一個端點上的進程地址,addrlen是地址長度。 sockaddr用于socket與TCP/IP協議模塊交換地址參數,該結構對大多數基于socket的應用程序是透明的,其定義如下: struct sockaddr u_short sa_family; /* 地

20、址類型 */ char sa_data14 /* 14字節地址 */ ;,4接受連接請求(server),int listen(sockfd,queuelen) listen調用為指定的插座規定能接受的連接請求的最大數目,也即隊列的最大長度。 newsockfd=accept(sockfd,clieaddr,addrlen) struct sockaddr *clieaddr; 取隊列中第一個連接請求,clieaddr和addrlen是accept返回時由系統填入的對方插座地址和地址結構的長度。 返回值newsockfd是一個與sockfd不同的新的插座描述符,這樣服務器一方面可以在與sock

21、fd相關的地址上繼續監聽對方新的服務請求,另一方面可以在與newsockfd相關的另一個信道上與請求連接的client進程進行聯系通信。,5數據傳送,count=send(sockfd,msg,len,flags) count=write(sockfd,msg,len) count=recv(sockfd,buf,len,flags) count=read(sockfd,buf,len) msg為發送數據的緩沖區, buf是接收數據的緩沖區, flags為0時,表示正常的數據發送或接收,flags等于MSG_OOB時表示發送或接收帶外數據(緊急數據);flags等于MSG_PEEK時表示可以“

22、偷看”一個到來的報文,檢查其內容,但不把它從隊列中移去。當flags等于MSG_DONTROUTE時表示可以在沒有網絡路由選擇的情況下發送數據。 返回值count是實際發送或接收的數據長度。,6關閉插座,可以像關閉打開的文件一樣,利用close系統調用可關閉一插座,釋放插座描述符。 int close(int sockfd) 插座關閉后,在連接狀態下正在進行的信息發送還能繼續進行。,13.4.3 socket通信程序設計,一個使用TCP/IP協議進行socket通信的例子。 服務器方使用getsockname獲得一未用端口號,并將其打印出來,以供client程序使用該端口號作為命令參數,與服務

23、器建立socket連接。此端口號也可由用戶直接定義。 服務器進程在調用accept獲得顧客方發來的連接請求后,產生子進程,使子進程使用一個新的插座描述符與顧客程序通信,而父進程繼續在原地址上監聽。,通信雙方程序的頭文件,/* sockcom.h */ #include #include #include #include #include ,/* server */ #include “sockcom.h” main() int sockfd,newsockfd,length,count; struct sockaddr_in server; char buf1024; sockfd=sock

24、et(AF_INET,SOCK_STREAM,0); 生成插座 server.sin_family=AF_INET; /* 構成socked名(地址) 和建立聯系 */ server.sin_addr.s_addr=INADDR_ANY; server.sin_port=0; /* 選擇一個已釋放的端口號 */ if(bind(sockfd,(struct sockaddr *) /* 獲取并打印端口號 */,/* 獲取并打印端口號 */ length=sizeof(server); if(getsockname(sockfd,(strcut sockaddr *) ,while(1) new

25、sockfd=accept(sockfd,(struct sockaddr *)0 ,(int *)0); if(!fork() 子進程 close(sockfd); bzero(buf,sizeof(buf); 調用庫函數,清緩沖區 if(count=recv(newsockfd,buf,sizeof(buf),0)0) syserr(“Reading stream message”); printf(“message received: %sn”,buf); exit(0); close(newsockfd); ,調用形式:命令,主機名,端口號,/* client */ #include

26、“sockcom.h” main(argc,argv) int argc; char *argv; int sockfd; struct sockaddr_in server; struct hostent *hp,*gethostbyname(); char msg1024;,sockfd=socket(AF_INET,SOCK_STREAM,0); /* 與由命令行參數指定的主機建立連接 */ hp=gethostbyname(argv1)=NULL) server.sin_family = AF_INET; bcopy(char *)hp-h_addr,(char *) ,while(1

27、) printf(“Enter send message: ”); scanf(“%s”,msg); if(!strlen(msg) break; if(send(sockfd,msg,strlen(msg),0)0) syserr(“sendint message”); bzero(msg,sizeof(msg); printf(“EOF.disconnectn”); close(sockfd); exit(0); ,在顧客方程序中調用了gethostbyname,通過查找/etc/hosts文件,將服務器主機的IP地址填入hostent結構中,并返回指向該結構的指針。hostnet在net

28、db.h中定義: struct hostent char *h_name; /* 主機名 */ char *h_aliases; /* 別名表 */ int h_addrtype; /* 主機地址類型 */ int h_length; /* 地址長度 */ char *h_addr_list; /* 域名服務器地址表,以NULL終止 */ ; bcopy()是字節拷貝函數。,socket進階,1套接字(插座)的功能 套接字實質上提供了進程通信的端點。進程通信之前,雙方首先必須各自創建一個端點,否則是沒有辦法建立聯系并相互通信的。正如打電話之前,雙方必須各自擁有一臺電話機一樣。 有人說:“在UN

29、IX系統中,任何東西都是一個文件。”這句話描述了這樣一個事實:在UNIX系統中,任何對I/O的操作,都是通過讀或寫一個文件描述符來實現的。所以,如果你想通過Internet和另外一個程序通信的話,你將會是通過一個文件的描述符來實現的。 可以用write( )和read( )對套接字描述符進行操作的,但是,通過使用send( )和recv( )函數,你可以對網絡數據的傳輸進行更好的控制。,2Socket是怎樣在網絡上傳輸數據的,OSI模型共分為七層。從下到上依次為:物理層、數據鏈路層、網絡層、運輸層、會話層、表示層和應用層。這個模型是最一般的模型,但是在UNIX中,真正用到的模型層次是下面這樣子

30、的: 應用層(Telnet、FTP等等) 主機間對話層,即傳輸層(TCP和UDP) 網絡層(IP和路由) 網絡底層(相當于OSI模型中網絡、數據鏈路和物理層) 對流式套接字我們所需要做的是調用send( )函數來發送數據。UNIX系統內核中已經建立了Transport Layer和Internet Layer。硬件負責NetworkAccess Layer。,3基本轉換函數,(1)網絡字節順序 因為每一個機器內部對變量的字節存儲順序不同(有的系統是高位在前,低位在后,而有的系統是低位在前,高位在后),而網絡傳輸的數據順序是一定要統一的。所以對與內部字節表示順序和網絡字節順序不同的機器,一定要對

31、數據進行轉換(比如IP地址的表示,端口號表示)。 內部字節順序和網絡字節順序相同的機器也要調用轉換函數,但是真正轉換還是不轉換是由系統函數自己來決定的。,(2) 有關的轉換函數,通常使用的有兩種數據類型:短型(兩個字節)和長型(四個字節)。下面介紹的這些轉換函數對于這兩種的無符號整型變量都可以進行正確的轉換。 如果你想將一個短型數據從主機字節順序轉換到網絡字節順序,有這樣一個函數:它以“h”開頭,代表“主機”;緊跟著它的是“to”,代表“轉換到”;然后是“n”,代表“網絡”;最后是“s”,代表“短型數據”。h-to-n-s,就是htons( )函數。,套接字字節轉換函數列表:,htons( )

32、主機字節順序轉換為網絡字節順序(對無符號短型進行操作) htonl( )主機字節順序轉換為網絡字節順序(對無符號長型進行操作) ntohs( )網絡字節順序轉換為主機字節順序(對無符號短型進行操作) ntohl( )網絡字節順序轉換為主機字節順序(對無符號長型進行操作),在struct socketaddr_in 中的sin_addr和sin_port的字節順序都是網絡字節順序,而sin_family卻不是網絡字節順序。這是因為sin_addr和sin_port是從IP和UDP協議層取出數據的,而在IP和UDP協議層,是直接和網絡相關的,所以它們必須使用網絡字節順序。 sin_family域只

33、是內核用來判斷struct socketaddr_in存儲的是什么類型的數據,并且,sin_family永遠也不會被發送到網絡上,所以可以使用主機字節順序來存儲。,(3) IP地址轉換,UNIX系統提供很多用于轉換IP地址的函數。假設有一個struct sockaddr_in ina,并且IP是2,如果想把它存儲到ina中,可以使用函數inet_addr( ),它能夠把一個用數字和點表示的IP地址的字符串轉換成一個無符號長整型。使用方式為: ina.sin_addr.s_addr=inet_addr(2); inet_addr( )返回的地址已經

34、是網絡字節順序了,沒有必要再去調用htonl( )函數。 必須注意,如果inet_addr( )函數執行錯誤,它將會返回-1,二進制的無符號整數值-1,相當于55,一個廣播用的IP地址。,如果有一個struct in_addr,并且想把它代表的地址打印出來(按照數字.數字.數字.數字的格式),可以使用函數inet_ntoa( ),例如: printf(%s,inet_ntoa(ina.sin_addr); 這段代碼將會把struct in_addr里面存儲的網絡地址以數字.數字.數字.數字的格式顯示出來。,inet_ntoa( )返回一個字符指針,它指向一個定義在函數inet_ntoa( )中的static類型字符串。所以每次調用inet_ntoa( ),都會改變最后一次調用inet_ntoa( )函數時所得到的結果。例如: char *a1, *a2; a1=inet

溫馨提示

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

評論

0/150

提交評論