《計(jì)算機(jī)操作系統(tǒng)教程》課件-第9章_第1頁(yè)
《計(jì)算機(jī)操作系統(tǒng)教程》課件-第9章_第2頁(yè)
《計(jì)算機(jī)操作系統(tǒng)教程》課件-第9章_第3頁(yè)
《計(jì)算機(jī)操作系統(tǒng)教程》課件-第9章_第4頁(yè)
《計(jì)算機(jī)操作系統(tǒng)教程》課件-第9章_第5頁(yè)
已閱讀5頁(yè),還剩90頁(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)介

9.1應(yīng)用程序編程接口概述9.2進(jìn)程間通訊實(shí)現(xiàn)方法與實(shí)例9.3線程編程及實(shí)現(xiàn)方法應(yīng)用程序編程接口(ApplicationProgrammingInterface,API)可定義為:由操作系統(tǒng)所支持的函數(shù)定義、參數(shù)定義以及消息格式的集合。應(yīng)用程序可使用API函數(shù)的處理系統(tǒng)所提供的各項(xiàng)功能。例如,WindowsAPI函數(shù)提供了Windows所支持的所有系統(tǒng)服務(wù)的功能。9.1應(yīng)用程序編程接口概述

Windows發(fā)展的早期階段,編程工具還不像現(xiàn)在這樣完善和方便。那時(shí),Windows程序員可以使用的編程工具只有API函數(shù),程序員的工作就是利用這些API函數(shù),像“搭積木”一樣構(gòu)造出符合自己要求的程序。隨著軟件技術(shù)的不斷發(fā)展,在Windows平臺(tái)上出現(xiàn)了許多優(yōu)秀的可視化編程環(huán)境或工具,例如,VisualC++、VisualBasic、VisualFoxPro、Delphi、C++Builder,等等。這些開發(fā)工具提供了大量的類庫(kù)和控件,程序員可以采用“所見即所得”的編程方式來(lái)開發(fā)具有界面精美、功能完善的應(yīng)用程序。這樣一來(lái),仿佛API的功能正在逐漸削弱。實(shí)際上則不然,這些類庫(kù)和控件都是在WindowsAPI的基礎(chǔ)上所構(gòu)建的。類庫(kù)的封裝和控件的開發(fā)都可以屏蔽底層的技術(shù)細(xì)節(jié),減少程序員的工作量和降低程序開發(fā)的復(fù)雜程度;但是它們只能提供Windows的一般功能,對(duì)于比較復(fù)雜和功能特殊的程序來(lái)說(shuō),單純使用類庫(kù)和控件是難以實(shí)現(xiàn)的,還必須直接使用API函數(shù)來(lái)編寫。

Microsoft32位Windows平臺(tái)上的API稱為Win32API,它提供了相當(dāng)豐富的函數(shù),所有在32位Windows平臺(tái)上運(yùn)行的程序都可以調(diào)用這些函數(shù)。但是由于各平臺(tái)功能的差異,有些函數(shù)只能用于特定的平臺(tái),例如安全函數(shù)只能在WindowsNT平臺(tái)上使用。標(biāo)準(zhǔn)的Win32API函數(shù)總體上可以分成七類,分別為:

(1)窗口管理類。它為應(yīng)用程序提供了創(chuàng)建和管理用戶界面的方法,提供用戶、鍵盤、鼠標(biāo)等輸入消息的處理手段。

(2)窗口通用控制類。通用控制庫(kù)是conctl32.dll(通用控制庫(kù))支持的一個(gè)控制窗口集,應(yīng)用程序通過(guò)調(diào)用這些控制,可以使用系統(tǒng)Shell提供的控制功能。

(3)Shell特性類。它為應(yīng)用程序增強(qiáng)系統(tǒng)Shell功能提供了相應(yīng)的手段。

(4)圖形設(shè)備接口類。它為應(yīng)用程序能夠在顯示器、打印機(jī)或其它圖形設(shè)備上生成圖形化的輸出結(jié)果提供了必要的手段。

(5)系統(tǒng)服務(wù)類。它為應(yīng)用程序提供了訪問(wèn)計(jì)算機(jī)資源以及底層操作系統(tǒng)特性的手段。例如訪問(wèn)內(nèi)存、文件系統(tǒng)、設(shè)備、進(jìn)程和線程。

(6)國(guó)際特性類。它包括輸入方法編輯器函數(shù)(IME)、國(guó)家語(yǔ)言支持函數(shù)(NLS)以及Unicode和字符集支持函數(shù)。

(7)網(wǎng)絡(luò)服務(wù)類。它用于網(wǎng)絡(luò)上不同計(jì)算機(jī)之間的通信。傳統(tǒng)的程序大都是運(yùn)行在一臺(tái)主機(jī)上的,它可以通過(guò)全局變量或者函數(shù)調(diào)用和本機(jī)上的其它模塊或其它應(yīng)用程序進(jìn)行通訊。但是,這種方法對(duì)于網(wǎng)絡(luò)上運(yùn)行的程序或者開發(fā)分布式應(yīng)用就無(wú)能為力了,此時(shí)必須使用進(jìn)程間通訊(Interprocess

Communications,簡(jiǎn)稱IPC)技術(shù)。

9.2.1管道

簡(jiǎn)單說(shuō)來(lái),管道就是連接一個(gè)程序的輸出和另外一個(gè)程序的輸入的單向通道,它是UNIX中一種古老的進(jìn)程間通訊機(jī)制。9.2進(jìn)程間通訊實(shí)現(xiàn)方法與實(shí)例了解管道的一個(gè)直觀的例子是在命令中使用管道符,例如命令:

ls-1|greplinux|more

就使用了兩個(gè)管道符,grep的輸入來(lái)自于ls的輸出,而grep的輸出又作為more命令的輸入使用。該命令的含義是在當(dāng)前目錄中查找名字中包含“l(fā)inux”的目錄或者文件,然后分屏顯示。

管道可以分成兩類:無(wú)名管道(簡(jiǎn)稱管道)和FIFO(也稱為命名管道)。二者之間主要的區(qū)別在于無(wú)名管道只能用于父、子進(jìn)程之間的通訊,而FIFO則可以用于任何進(jìn)程之間的通訊。

1.無(wú)名管道

所有的UNIX系統(tǒng)都支持管道的通訊機(jī)制。管道有兩種限制:

(1)管道是半雙工的,數(shù)據(jù)只能單向流動(dòng);

(2)管道只能在父子進(jìn)程之間使用。

管道由系統(tǒng)調(diào)用pipe()產(chǎn)生,該函數(shù)的語(yǔ)法為

intpipe(intfd[2])

參數(shù)fd用來(lái)存放pipe()創(chuàng)建的管道的句柄。fd[0]用于讀,fd[1]用于寫。管道通常由父進(jìn)程創(chuàng)建。父進(jìn)程創(chuàng)建管道之后再fork一個(gè)進(jìn)程,該管道就可用于這兩個(gè)父子進(jìn)程之間的通訊。父進(jìn)程在fork一個(gè)子進(jìn)程之后,該子進(jìn)程就自動(dòng)繼承了父進(jìn)程打開的文件句柄。因此,原則上來(lái)說(shuō),父子進(jìn)程都可以讀寫該管道,可以進(jìn)行雙工通訊,但是由于沒(méi)有提供鎖定的保護(hù)機(jī)制,實(shí)際上數(shù)據(jù)只能單向傳遞,程序員需要決定數(shù)據(jù)的流向,從而在父進(jìn)程和子進(jìn)程兩方分別關(guān)閉不需要的句柄。例如數(shù)據(jù)從父進(jìn)程流向子進(jìn)程的情況,管道如圖9.1所示。圖9.1父進(jìn)程向子進(jìn)程傳遞信息的管道圖9.1中父進(jìn)程和子進(jìn)程中的fd[0]和fd[1]是相同的,本例中,父進(jìn)程需要關(guān)閉讀句柄fd[0],子進(jìn)程需要關(guān)閉寫句柄fd[1]。

管道建立之后,我們就可以像普通文件一樣使用write()、read()函數(shù)對(duì)其進(jìn)行讀寫操作了。下面是一個(gè)使用管道實(shí)現(xiàn)子進(jìn)程向父進(jìn)程傳遞信息的例子:

#include<stdio.h>

#include<unistd.h>

#include<sys/types.h>

intmain(void)

{

intfd[2],nbytes;

pid_tpid;

charstring[]=“Hello,world!\n”;

charreadbuffer[80];

pipe(fd);/*創(chuàng)建管道*/

if((pid=fork())==-1) /*創(chuàng)建子進(jìn)程*/

{

perror(“fork”);

exit(1);

}

if(pid==0) /*子進(jìn)程*/

{

close(fd[0]); /*關(guān)閉管道讀句柄*/

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

/*向管道中寫入數(shù)據(jù)*/

_exit(0);

}

else /*父進(jìn)程*/

{

close(fd[1]); /*關(guān)閉管道寫句柄*/

nbytes=read(fd[0],readbuffer,sizeof(readbuffer));

/*從管道中讀取數(shù)據(jù)*/

printf(“ReceivedStrings:%s\n”,readbuffer);

}

return(0);

}本例中程序員必須自行關(guān)閉管道不需要的句柄。但是,因?yàn)楣艿雷畛R姷氖褂梅绞绞怯糜趦蓚€(gè)進(jìn)程(比如s4hell命令)之間信息的通訊,所以標(biāo)準(zhǔn)I/O庫(kù)(stdio.h)中為了實(shí)現(xiàn)這些操作而提供了兩個(gè)函數(shù)popen和pclose,使用這兩個(gè)函數(shù),程序員就可以不必處理這樣的細(xì)節(jié)了。popen函數(shù)原型為

FILE*popen(constchar*cmdstring,constchar*type)

該函數(shù)首先創(chuàng)建一個(gè)管道,再執(zhí)行fork創(chuàng)建一個(gè)子進(jìn)程,然后調(diào)用exec執(zhí)行cmdstring指定的命令,并返回一個(gè)標(biāo)準(zhǔn)的文件指針。該文件指針根據(jù)type值進(jìn)行定位:如果type是“r”,則鏈接到cmdstring的標(biāo)準(zhǔn)輸出上;如果type是“w”,則鏈接到cmdstring的標(biāo)準(zhǔn)輸入上。下面的程序?qū)崿F(xiàn)ls|more的功能:

#include<stdio.h>

intmain(void)

{

FILE*in_fp,*out_fp;

charreadbuffer[80];

if((in_fp=popen(“l(fā)s”,“r”)==NULL))

{

perror(“popen”);

exit(1);

}

if((out_fp=popen(“more”,“w”)==NULL))

{

perror(“popen”);

exit(1);

}

while(fgets(readbuf,80,in_fp))

fputs(readbuf,out_fp);

pclose(in_fp);

pclose(out_fp);

return(0);

}我們已經(jīng)看到,使用管道實(shí)現(xiàn)進(jìn)程間通訊是通過(guò)內(nèi)核進(jìn)行交互的,因此速度受到一定的限制。無(wú)名管道只能用于具有父子關(guān)系的進(jìn)程之間的通訊,為了擺脫這種限制,必須使用其它的通訊方法。

2.FIFO

實(shí)際上,無(wú)名管道在系統(tǒng)內(nèi)部是以Inode節(jié)點(diǎn)的方式來(lái)存放的,但是對(duì)于外部用戶來(lái)說(shuō),它是不可見的,因此不能創(chuàng)建新的文件句柄對(duì)其進(jìn)行訪問(wèn)。而命名管道則不同,它是在文件系統(tǒng)中確實(shí)存在的一個(gè)特殊文件,具有普通文件的優(yōu)點(diǎn),可以方便地進(jìn)行讀寫,因此就可以方便地實(shí)現(xiàn)任意兩個(gè)進(jìn)程之間的通訊。

在系統(tǒng)shell中,我們可以使用下面的命令來(lái)創(chuàng)建命名管道:

mknodFIFOnamep

mkfifo-m0666FIFOname

而在C語(yǔ)言中,我們可以使用mknod函數(shù)或者makefifo函數(shù)創(chuàng)建命名管道,函數(shù)原型如下:

intmknod(char*pathname,mode_tmode,dev_tdev)

intmkfifo(constchar*pathname,mode_tmode)

命名管道一旦創(chuàng)建,在使用前必須使用fopen函數(shù)打開,然后就可以像普通文件一樣進(jìn)行讀寫操作了。命名管道支持自動(dòng)阻塞,也就是說(shuō),為只讀打開的命名管道要一直阻塞到有其它進(jìn)程為寫打開之時(shí),反之亦然。如果需要,可以在打開命名管道時(shí)使用O_NONBLOCK標(biāo)志來(lái)關(guān)閉其自動(dòng)阻塞的特性。通過(guò)讀/寫公開的FIFO,進(jìn)程之間就可以實(shí)現(xiàn)通訊,從而也可以開發(fā)C/S模式的應(yīng)用程序。下面的程序片斷說(shuō)明了FIFO的使用:

服務(wù)器端程序:

FILE*in_file;

charbuffer[80];

in_file=fopen(“FIFOname”,“r”);

if(in_file==NULL)

{

perror(“fopen”);

exit(1);

}

fread(buffer,1,80,in_file);

printf(“DatarecievedfromFIFO:%s\n”,buffer);

fclose(in_file);客戶端程序:

FILE*out_file;

charbuffer[80];

out_file=fopen(“FIFOname”,“w”);

if(out_file==NULL)

{

perror(“fopen”);

exit(1);

}

sprintf(buffer,“Testdatafornamedpipe!\n”);

fwrite(buffer,1,80,out_file);

printf(“DatarecievedfromFIFO:%s\n”,buffer);

fclose(out_file);

命名管道只能用于同種文件系統(tǒng)中的進(jìn)程之間的通訊,在使用的過(guò)程中要注意其獨(dú)立性問(wèn)題,也就是說(shuō)操作不能被其它進(jìn)程打斷。一次獨(dú)立操作可以傳送的最大字節(jié)數(shù)在POSIX標(biāo)準(zhǔn)中是在/usr/include/bits/posix1_lim.h中定義的:

#define_POSIX_PIPE_BUF512在Linux中,由/usr/include/linux/limits.h定義:

#definePIPE_BUF4096

如果進(jìn)行通訊的進(jìn)程需要寫入管道的數(shù)據(jù)超過(guò)這個(gè)限制,就必須將其分割成幾個(gè)獨(dú)立的操作過(guò)程。在C/S模式的應(yīng)用程序中,多個(gè)客戶端都可以對(duì)命名管道進(jìn)行讀寫,服務(wù)器端必須能夠正確區(qū)分這些客戶端的操作。9.2.2SystemVIPC機(jī)制

UnixSystemV中引入了三種新的IPC機(jī)制:消息隊(duì)列(MessageQueue)、信號(hào)量(Semaphores)和共享內(nèi)存(SharedMemory)。這三種IPC機(jī)制有很多相似之處,你可以使用ipcs命令來(lái)查看當(dāng)前系統(tǒng)中這三種IPC的使用情況。

消息隊(duì)列、信號(hào)量和共享內(nèi)存在系統(tǒng)中都有一個(gè)惟一的標(biāo)志符(標(biāo)志符是一個(gè)非負(fù)、遞增的數(shù)字,達(dá)到最大值時(shí)再?gòu)?開始計(jì)數(shù)),內(nèi)核根據(jù)這個(gè)標(biāo)志符來(lái)區(qū)分這些IPC對(duì)象。在程序中,我們需要使用關(guān)鍵字(key)來(lái)訪問(wèn)它們。關(guān)鍵字可以使用ftok系統(tǒng)調(diào)用來(lái)生成,函數(shù)原型如下:

key_tftok(char*pathname,charproj)進(jìn)行通訊的進(jìn)程必須使用同一個(gè)關(guān)鍵字,只要二者運(yùn)行的目錄(由參數(shù)pathname指定)是相同的,就可以保證ftok產(chǎn)生的關(guān)鍵字相同。

這三種IPC對(duì)象和普通文件不同,不能按照普通文件進(jìn)行讀寫或者設(shè)置權(quán)限。系統(tǒng)為每個(gè)IPC對(duì)象都設(shè)置了一個(gè)ipc_perm結(jié)構(gòu),該結(jié)構(gòu)說(shuō)明了這個(gè)IPC對(duì)象的權(quán)限和屬主信息。該結(jié)構(gòu)創(chuàng)建之后,對(duì)于這三種IPC對(duì)象,可以分別使用msgctl、segctl和shmctl對(duì)該結(jié)構(gòu)進(jìn)行修改。ipc_perm結(jié)構(gòu)的定義在linux/ipc.h中,如下所示:

structipc_perm

{

__kernel_key_tkey;

__kernel_uid_tuid;

__kernel_gid_tgid;

__kernel_uid_tcuid;

__kernel_gid_tcgid;

__kernel_mode_tmode;

unsignedshortseq;

}

1.消息隊(duì)列

消息隊(duì)列把進(jìn)程之間傳遞的消息(structmsg結(jié)構(gòu)的數(shù)據(jù))以鏈表的形式組織在一起,進(jìn)行通訊的寫方進(jìn)程經(jīng)過(guò)權(quán)限認(rèn)定之后,把需要傳遞的數(shù)據(jù)追加在隊(duì)列的尾部,讀方進(jìn)程就可以從該隊(duì)列中讀取需要的數(shù)據(jù),從而實(shí)現(xiàn)多個(gè)進(jìn)程之間的通訊。每個(gè)消息隊(duì)列都有一個(gè)structmsqid_ds(在linux/msg.h中定義)的結(jié)構(gòu)和它相關(guān),該結(jié)構(gòu)反映消息隊(duì)列的當(dāng)前狀態(tài):

structmsqid_ds{

structipc_permmsg_perm;

structmsg*msg_first;/*firstmessageonqueue,unused*/

structmsg*msg_last;/*lastmessageinqueue,unused*/

__kernel_time_tmsg_stime;/*lastmsgsndtime*/

__kernel_time_tmsg_rtime;/*lastmsgrcvtime*/

__kernel_time_tmsg_ctime;/*lastchangetime*/

unsignedlongmsg_lcbytes;/*Reusejunkfieldsfor32bit*/

unsignedlongmsg_lqbytes;/*ditto*/

unsignedshortmsg_cbytes;/*currentnumberofbytesonqueue*/

unsignedshortmsg_qnum;/*numberofmessagesinqueue*/

unsignedshortmsg_qbytes;/*maxnumberofbytesonqueue*/

__kernel_ipc_pid_tmsg_lspid;/*pidoflastmsgsnd*/

__kernel_ipc_pid_tmsg_lrpid;/*lastreceivepid*/

}和消息隊(duì)列的使用有關(guān)的系統(tǒng)調(diào)用有四個(gè):msgget、msgsnd、msgrcv和msgctl,其函數(shù)原型如下:

intmsgget(key_tkey,intmsgflg)

intmsgsnd(intmsqid,structmsgbuf*msgp,intmsgsz,intmsgflg)

intmsgrcv(intmsqid,structmsgbuf*msgp,intmsgsz,longmsgtyp,intmsgflg)

intmsgctl(intmsqid,intcmd,structmsqid_ds*buf)

msgget用于打開或者創(chuàng)建一個(gè)消息隊(duì)列;msgsnd把msgp中的數(shù)據(jù)發(fā)送到隊(duì)列中;msgrcv從隊(duì)列中讀取數(shù)據(jù);msgctl對(duì)消息隊(duì)列執(zhí)行cmd指定的操作。使用消息隊(duì)列我們可以傳遞自定義的數(shù)據(jù)類型。下面是linux/msg.h中對(duì)消息類型的定義:

structmsgbuf{

longmtype; /*消息類型*/

charmtext[1]; /*消息內(nèi)容*/

}該結(jié)構(gòu)中的mtype和msgrcv中的msgtype參數(shù)意義相同,用來(lái)定義消息的類型。通過(guò)指定msgtype參數(shù),msgrcv就可以接收指定類型的消息;也就是說(shuō),消息隊(duì)列并不是先進(jìn)先出的。另外,我們也可以自行定義數(shù)據(jù)類型。下面的例子簡(jiǎn)單說(shuō)明了消息隊(duì)列的用法:

#include<stdio.h>

#include<stdlib.h>

#include<ctype.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

structmymsgbuf{

longmtype;

charmtext[80];

};

voidsend_message(intqid,structmymsgbuf*qbuf,longtype,char*text);

voidread_message(intqid,structmymsgbuf*qbuf,longtype);

voidremove_queue(intqid);

voidchange_queue_mode(intqid,char*mode);

voidusage(void);

intmain(intargc,char*argv[])

{

key_tkey;

intmsgqueue_id;

structmymsgbufqbuf;

if(argc==1)

usage();

key=ftok(“.”,′m′);/*創(chuàng)建關(guān)鍵字*/

if((msgqueue_id=msgget(key,IPC_CREAT|0660))==-1){

/*打開隊(duì)列*/

perror(“msgget”);

exit(1);

}

switch(tolower(argv[1][0]))

{case′s′:send_message(msgqueue_id,(structmymsgbuf*)&qbuf,atol(argv[2]),argv[3]);

break;

case′r′:read_message(msgqueue_id,&qbuf,atol(argv[2]));

break;

case′d′:remove_queue(msgqueue_id);

break;

case′m′:change_queue_mode(msgqueue_id,argv[2]);

break;

default:usage();

}

return(0);

}

voidsend_message(intqid,structmymsgbuf*qbuf,longtype,char*text)

{

printf(“Sendingamessage...\n”);

qbuf->mtype=type;

strcpy(qbuf->mtext,text);

if((msgsnd(qid,(structmsgbuf*)qbuf,strlen(qbuf->mtext)+1,0))==-1) /*把消息追加到隊(duì)列中*/

{

perror(“msgsnd”);

exit(1);

}

}

voidread_message(intqid,structmymsgbuf*qbuf,longtype)

{

printf(“Readingamessage...\n”);

qbuf->mtype=type;

msgrcv(qid,(structmsgbuf*)qbuf,MAX_SEND_SIZE,type,0); /*從隊(duì)列中接收消息*/

printf(“Type:%ldText:%s\n”,qbuf->mtype,qbuf->mtext);

}

voidremove_queue(intqid)

{

msgctl(qid,IPC_RMID,0);/*把消息從隊(duì)列中清除*/

}

voidchange_queue_mode(intqid,char*mode)

{

structmsqid_dsmyqueue_ds;

msgctl(qid,IPC_STAT,&myqueue_ds);

sscanf(mode,“%ho”,&myqueue_ds.msg_perm.mode);

msgctl(qid,IPC_SET,&myqueue_ds);

}

voidusage(void)

{

fprintf(stderr,“msgtool-Autilityfortinkeringwithmsgqueues\n”);

fprintf(stderr,“\nUSAGE:msgtool(s)end<type><messagetext>\n”);

fprintf(stderr,“(r)ecv<type>\n”);

fprintf(stderr,“(d)elete\n”);

fprintf(stderr,“(m)ode<octalmode>\n”);

exit(1);

}

2.信號(hào)量

信號(hào)量實(shí)際上是一個(gè)計(jì)數(shù)器,用來(lái)控制多個(gè)進(jìn)程對(duì)共享資源的存取。一個(gè)信號(hào)量的初值可以設(shè)置為可用資源的個(gè)數(shù),進(jìn)程申請(qǐng)n個(gè)資源就將其減去n;等進(jìn)程運(yùn)行完畢釋放資源時(shí),就對(duì)其加n;當(dāng)前可用的資源數(shù)如果是0,則申請(qǐng)資源的進(jìn)程就掛起等待。要注意的是,在SystemV中的信號(hào)量實(shí)際上是一個(gè)信號(hào)量集,個(gè)數(shù)由semid_ds.sem_nsems定義。系統(tǒng)在linux/sem.h中為每一個(gè)信號(hào)量都保留了一個(gè)結(jié)構(gòu):

structsemid_ds{

structipc_permsem_perm; /*permissions..seeipc.h*/

__kernel_time_tsem_otime; /*lastsemoptime*/

__kernel_time_tsem_ctime; /*lastchangetime*/

structsem*sem_base;

/*ptrtofirstsemaphoreinarray*/

structsem_queue*sem_pending;/*pendingoperationstobeprocessed*/

structsem_queue**sem_pending_last;/*lastpendingoperation*/

structsem_undo*undo; /*undorequestsonthisarray*/

unsignedshortsem_nsems; /*no.ofsemaphoresinarray*/

}和信號(hào)量有關(guān)的系統(tǒng)調(diào)用有:

intsemget(key_tkey,intnsems,intsemflg)

intsemop(intsemid,structsembuf*sops,unsignednsops)

intsemctl(intsemid,intsemnum,intcmd,unionsemunarg)

semget用來(lái)打開或者創(chuàng)建一個(gè)信號(hào)量集;semctl對(duì)信號(hào)量執(zhí)行cmd指定的操作;semop中的參數(shù)sops是一個(gè)structsembuf類型的指針,該結(jié)構(gòu)在linux/sem.h中定義:

structsembuf{

unsignedshortsem_num;/*semaphoreindexinarray*/

shortsem_op; /*semaphoreoperation*/

shortsem_flg; /*operationflags*/

}

sem_op如果為正數(shù)就表示釋放資源,為負(fù)數(shù)則代表申請(qǐng)資源。

有關(guān)信號(hào)量的操作的例子我們?cè)谙乱还?jié)中和共享內(nèi)存一起給出。

3.共享內(nèi)存

共享內(nèi)存是進(jìn)程間通訊最快的方法。它就是由一個(gè)進(jìn)程申請(qǐng)一段內(nèi)存區(qū)域,其它進(jìn)程也能夠訪問(wèn)這段內(nèi)存區(qū)域,從而實(shí)現(xiàn)進(jìn)程間的通訊。使用共享內(nèi)存的方式來(lái)實(shí)現(xiàn)進(jìn)程間的通訊時(shí),必須解決讀寫進(jìn)程的原子操作,也就是說(shuō),在寫操作的進(jìn)程完成之前,讀進(jìn)程不能讀取這段內(nèi)存區(qū)域的內(nèi)容。通常使用信號(hào)量或者記錄鎖來(lái)保證進(jìn)行通訊的進(jìn)程對(duì)共享內(nèi)存的同步存取。系統(tǒng)在linux/shm.h中為每個(gè)共享內(nèi)存段都設(shè)置了一個(gè)結(jié)構(gòu):

structshmid_ds{

structipc_perm_shm_perm;/*operationperms*/

intshm_segsz; /*sizeofsegment(bytes)*/

__kernel_time_tshm_atime; /*lastattachtime*/

__kernel_time_tshm_dtime; /*lastdetachtime*/

__kernel_time_tshm_ctime; /*lastchangetime*/

__kernel_ipc_pid_tshm_cpid;/*pidofcreator*/

__kernel_ipc_pid_tshm_lpid; /*pidoflastoperator*/

unsignedshortshm_nattch; /*no.ofcurrentattaches*/

unsignedshortshm_unused;/*compatibility*/

void*shm_unused2;/*ditto-usedbyDIPC*/

void*shm_unused3;/*unused*/

}和共享內(nèi)存有關(guān)的系統(tǒng)調(diào)用有:

intshmget(key_tkey,intsize,intshmflg)

void*shmat(intshmid,constvoid*shmaddr,intshmflg)

intshmdt(constvoid*shmaddr)

intshmctl(intshmid,intcmd,structshmid_ds*buf)

shmget用于打開或者創(chuàng)建一個(gè)共享內(nèi)存段。如果shmadd是0,那么shmat就返回一個(gè)沒(méi)有映射的內(nèi)存區(qū)域,供進(jìn)程使用;shmdt取消共享內(nèi)存段和進(jìn)程之間的關(guān)聯(lián);shmget對(duì)共享內(nèi)存段執(zhí)行cmd指定的操作。在Linux中,我們可以編輯/etc/lilo.conf,預(yù)留一段內(nèi)存供共享內(nèi)存使用。例如,我們有64MB內(nèi)存,在/etc/lilo.conf中增加一行:

append=“mem=63m”

這樣我們就預(yù)留了1MB內(nèi)存。

下面是使用信號(hào)量和共享內(nèi)存實(shí)現(xiàn)cp的功能的例子,命令格式為

./a.out<infile>outfile源程序如下:

#include<stdio.h>

#include<signal.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include<sys/sem.h>

#defineSHMKEY1(key_t)0x10/*共享內(nèi)存的關(guān)鍵字*/

#defineSHMKEY2(key_t)0x20/*共享內(nèi)存的關(guān)鍵字*/

#defineSEMKEY(key_t)0x30/*信號(hào)量的關(guān)鍵字*/

#defineMAXSIZE5*BUFSIZ

structdatabuf{

intd_nread;

chard_buf[MAXSIZE];

};

#defineERR((structdatabuf*)-1)

structsembufp_sem1={0,-1,0},p_sem2={1,-1,0},

v_sem1={0,1,0},v_sem2={1,1,0};

staticintshmid1,shmid2,semid;

voidfail(char*msg)

{

perror(msg);

exit(1);

}

voidgetseg(structdatabuf**buf1,structdatabuf**buf2)

{

if((shmid1=shmget(SHMKEY1,sizeof(structdatabuf),IPC_CREAT|IPC_EXCL|0666))<0)

/*創(chuàng)建共享內(nèi)存區(qū)*/

fail("shmgetfail!");

if((shmid2=shmget(SHMKEY2,sizeof(structdatabuf),IPC-CREAT|IPC_EXCL|0666))<0)

fail(“shmgetfail!”);

if((*buf1=(structdatabuf*)(shmat(shmid1,0,0)))==ERR) /*建立與共享內(nèi)存區(qū)的連接*/

fail(“shmatfail!”);

if((*buf2=(structdatabuf*)(shmat(shmid2,0,0)))==ERR)

fail(“shmatfail!”);

}

intgetsem()

{

if((semid=semget(SEMKEY,2,IPC_CREAT|IPC_EXCL|0666))==-1)

/*創(chuàng)建信號(hào)量*/

fail(“semgetfail!”);

if(semctl(semid,0,SETVAL,0)<0)

fail(“semctlfail!”);

if(semctl(semid,1,SETVAL,0)<0)

fail(“semctlfail!”);

return(semid);

}

voidclean()

{

if(shmctl(shmid1,IPC_RMID,NULL)<0)

/*釋放共享內(nèi)存區(qū)*/

fail(“shmctlfail!”);

if(shmctl(shmid2,IPC_RMID,NULL)<0)

fail(“shmctlfail!”);

if(semctl(semid,0,IPC_RMID,NULL)<0)

/*釋放信號(hào)量*/

fail(“semctlfail!”);

}

voidreaddata(intsemid,structdatabuf*buf1,structdatabuf*buf2)

{

for(;;){

buf1->d_nread=read(0,buf1->d_buf,MAXSIZE);

semop(semid,&v_sem1,1);/*同步*/

semop(semid,&p_sem2,1);

if(buf1->d_nread<=0)

return;

semop(semid,&v_sem2,1);

semop(semid,&p_sem1,1);

if(buf2->d_nread<=0)

return;

}

}

voidwritedata(intsemid,structdatabuf*buf1,structdatabuf*buf2)

{

for(;;){

semop(semid,&p_sem1,1);

if(buf1->d_nread<=0)

return;

write(1,buf1->d_buf,buf1->d_nread);

semop(semid,&v_sem1,1);

semop(semid,&p_sem2,1);

if(buf2->d_nread<=0)

return;

write(1,buf2->d_buf,buf2->d_nread);

semop(semid,&v_sem2,1);

}

}

intmain()

{

intsemid,pid;

structdatabuf*buf1,*buf2;

semid=getsem();

getseg(&buf1,&buf2);

fprintf(stderr,“shmid:%d,shmid2:%d,semid:%d”,

shmid1,shmid2,semid);

if((pid=fork())<0)

fail(“forkfail!”);

elseif(pid==0){

writedata(semid,buf1,buf2);

}

else{

readdata(semid,buf1,buf2);

}

clean();

exit(0);

}9.2.3套接字

套接字(Socket)是目前進(jìn)行網(wǎng)絡(luò)編程使用最為廣泛的技術(shù)。簡(jiǎn)單地說(shuō),套接字就是使用UNIX系統(tǒng)中的文件描述符實(shí)現(xiàn)系統(tǒng)進(jìn)程間通訊的一種方法。

從歷史發(fā)展來(lái)看,套接字起源于UNIX系統(tǒng),主要有兩種類型:UNIX域套接字(DomainSocket)和伯克利套接字(BerkelySocket)?,F(xiàn)在伯克利套接字(以下簡(jiǎn)稱套接字)已經(jīng)

成為事實(shí)上的標(biāo)準(zhǔn),廣泛用于各種平臺(tái)之間的網(wǎng)絡(luò)通訊,例如ftp、telnet、http等通訊在底層都是使用Socket編程技術(shù)實(shí)現(xiàn)的。在ISO的OSI七層模型中,傳輸層采用的協(xié)議有兩種,分別是TCP和UDP。其中TCP是面向連接的,提供可靠傳輸;UDP是無(wú)連接的,傳輸?shù)男畔⒖赡馨l(fā)生丟失或者次序混亂。套接字有兩種類型分別對(duì)應(yīng)這兩種協(xié)議:流式套接字(SOCK_STREAM)和數(shù)據(jù)報(bào)套接字(SOCK_DGRAM)。流式套接字的處理流程如圖9.2所示,讀者可以自行分析數(shù)據(jù)報(bào)套接字的處理流程。圖9.2流式套接字的處理流程記住一點(diǎn),在Linux中所有的內(nèi)容歸根結(jié)底都是文件,Socket也不例外。一個(gè)Socket對(duì)應(yīng)一個(gè)描述符,它是一個(gè)整型數(shù),還有幾個(gè)結(jié)構(gòu)和Socket有關(guān):

structsockaddr{

sa_family_tsa_family;

/*address族,對(duì)于IP協(xié)議是AF_INET*/

charsa_data[14];

/*14字節(jié)的協(xié)議地址*/

}為了處理簡(jiǎn)單,程序員可以定義一個(gè)類似于sockaddr的結(jié)構(gòu)在程序中使用:

structsockaddr_in{

sa_family_tsin_family;

/*address族,對(duì)于IP協(xié)議是AF_INET*/

unsignedshortsin_port; /*端口號(hào)*/

structin_addrsin_addr; /*Internet地址*/

unsignedcharsin_zero[8];

/*添0,保證該結(jié)構(gòu)和sockaddr結(jié)構(gòu)大小相同*/

}

sin_addr和sin_port惟一定義了一個(gè)套接字,因此可以進(jìn)行惟一的訪問(wèn)。sockaddr_in結(jié)構(gòu)中的sin_addr成員是in_addr的結(jié)構(gòu),定義如下:

structin_addr{

unsingnedlongs_addr;

}在Linux中,sockaddr結(jié)構(gòu)的定義位于linux/socket.h,sockaddr_in和in_結(jié)構(gòu)的定義位于linux/in.h,和我們上面的定義稍微有些差別。同時(shí),Linux還提供了相當(dāng)豐富的進(jìn)行類型轉(zhuǎn)換的函數(shù),例如有一個(gè)structsockaddr_inina的變量,希望將其IP地址賦值為,可以這樣使用:

ina.sin_addr.s_addr=inet_addr(“”)

對(duì)Socket進(jìn)行操作的系統(tǒng)調(diào)用有很多,下面我們簡(jiǎn)單介紹圖8.2中涉及到的一些系統(tǒng)調(diào)用。

(1)socket()。socket()用來(lái)創(chuàng)建一個(gè)套接字描述符,函數(shù)原型如下:

intsocket(intdomain,inttype,intprotocol)

參數(shù)domain指明協(xié)議類型,對(duì)于IP協(xié)議該參數(shù)應(yīng)該設(shè)置為PF_INET;type指明套接字類型,流式套接字是SOCK_STREAM,數(shù)據(jù)報(bào)套接字是SOCK_DGRAM。

(2)bind()。bind()用于服務(wù)器端。其函數(shù)原型為

intbind(intsockfd,structsockaddr*my_addr,intaddrlen)

該函數(shù)將一個(gè)套接字描述符和一個(gè)sockaddr結(jié)構(gòu)的參數(shù)my_addr綁定在一起。addrlen是structsockaddr的大小。

(3)listen()。listen()用于服務(wù)器端。其函數(shù)原型為

intlisten(ints,intbacklog)

服務(wù)器端完成bind()操作之后,就要調(diào)用listen(),開始偵聽來(lái)自客戶端的connect()發(fā)出的連接請(qǐng)求。如果有請(qǐng)求到達(dá),就將其加入請(qǐng)求隊(duì)列(隊(duì)列長(zhǎng)度由backlog參數(shù)指定),然后調(diào)用accept()處理。

(4)accept()。accept()用于服務(wù)器端。其函數(shù)原型為:

intaccept(ints,structsockaddr*addr,int*addrlen)

如果accept()成功,則返回一個(gè)新的套接字描述符。實(shí)際上在客戶端和服務(wù)器進(jìn)行通訊時(shí),服務(wù)器端除了自行創(chuàng)建的套接字描述符用于listen()之外,每一個(gè)客戶端的成功連接都會(huì)在服務(wù)器端新創(chuàng)建一個(gè)套接字描述符,客戶端和服務(wù)器就是通過(guò)它進(jìn)行通訊的。

(5)connect()。connect()用于客戶端。其函數(shù)原型為

intconnect(intsockfd,structsockaddr*serv_addr,intaddrlen)

參數(shù)serv_addr中保存了服務(wù)器的信息,該函數(shù)將試圖和服務(wù)器建立連接。

(6)send()和recv()。這兩個(gè)系統(tǒng)調(diào)用用于面向連接的流式套接字的數(shù)據(jù)的發(fā)送和接收。套接字描述符當(dāng)然可以使用read()和write()進(jìn)行讀寫,但是send()和recv()提供了更完善的控制。

send()函數(shù)原型為

intsend(ints,constvoid*msg,intlen,unsignedintflags)

s為進(jìn)行通訊的描述符,也就是accept()的返回值;該函數(shù)返回發(fā)送的字節(jié)數(shù)。recv()和它類似,函數(shù)原型為

intrecv(ints,void*buf,intlen,unsignedintflags)

(7)sendto()和recvfrom()。這兩個(gè)系統(tǒng)調(diào)用用于無(wú)連接的流式數(shù)據(jù)報(bào)的數(shù)據(jù)的發(fā)送和接收,和send()與recv()類似。其函數(shù)原型如下:

intsendto(ints,constvoid*msg,intlen,unsignedintflags,conststructsockaddr*to,inttolen)

intrecvfrom(ints,void*buf,intlen,unsignedintflagsstructsockaddr*from,int*fromlen)

(8)close()。close()用于關(guān)閉套接字描述符。其函數(shù)原型為

intclose(intfd)

另外一個(gè)函數(shù)shutdown()也提供了類似的功能:

intshutdown(ints,inthow)

二者之間的區(qū)別在于close()將關(guān)閉套接字描述符,以后不允許任何操作;而shutdown()允許單向關(guān)閉套接字描述符,由參數(shù)how指定,意義如下:

0以后不允許接收數(shù)據(jù)

1以后不允許發(fā)送數(shù)據(jù)

2以后不允許任何操作下面我們給出一個(gè)面向連接的流式套接字進(jìn)行通訊的例子。本例由兩部分組成:server.c

和client.c。源程序如下:

**************

server.c源程序

**************

#include<stdio.h>

#include<stdlib.h>

#include<errno.h>

#include<string.h>

#include<netinet/in.h>

#include<sys/types.h>

#include<sys/socket.h>

#include<sys/wait.h>

#defineMYPORT8000

#defineBACKLOG10

intmain()

{

intsock_fd,new_fd;

structsockaddr_inmy_addr;

structsockaddr_intheir_addr;

intsin_size;

if((sock_fd=socket(AF_INET,SOCK_STREAM,0))==-1){

/*創(chuàng)建套接字*/

perror(“socket”);

exit(1);

}

my_addr.sin_family=AF_INET; /*協(xié)議地址族*/

my_addr.sin_port=htons(MYPORT); /*端口*/

my_addr.sin_addr.s_addr=INADDR_ANY;

/*internet地址*/

bzero(&(my_addr.sin_zero),8);

if(bind(sock_fd,(structsockaddr*)&my_addr,sizeof(stru

ctsockaddr))==-1){ /*綁定*/

perror(“bind”);

exit(1);

}

if(listen(sock_fd,BACKLOG)==-1){ /*偵聽*/

perror(“l(fā)isten”);

exit(1);

}

while(1){

sin_size=sizeof(structsockaddr_in);

if((new_fd=accept(sock_fd,(structsockaddr*)&their_addr,&sin_size))==-1) /*接收*/

perror("accept");

exit(1);

}

printf("server:getconnectionfrom%s\n",inet_ntoa(their_addr.sin_addr));

if(!fork()){ /*子進(jìn)程*/

if(send(new_fd,"Hello,World!\n",14,0)==-1)

/*發(fā)送信息*/

perror("send");

close(new_fd); /*關(guān)閉套接字*/

exit(0);

}

close(new_fd);/*關(guān)閉套接字*/

while(waitpid(-1,NULL,WNOHANG)>0);

/*等待子進(jìn)程全部退出*/

}

close(sock_fd);

return0;

}**************

client.c源程序

**************

#include<stdio.h>

#include<stdlib.h>

#include<errno.h>

#include<string.h>

#include<netinet/in.h>

#include<sys/types.h>

#include<sys/socket.h>

#include<sys/wait.h>

#defineMYPORT8000

#defineMAXDATASIZE100

intmain(intargc,char*argv[])

{

intsockfd,numbytes;

charbuf[MAXDATASIZE];

structsockaddr_intheir_addr;

if(argc!=2){

fprintf(stderr,“usage:clienthostname\n”);

exit(1);

}

if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){ /*創(chuàng)建套接字*/

perror(“socket”);

exit(1);

}

their_addr.sin_family=AF_INET; /*協(xié)議地址族*/

their_addr.sin_port=htons(MYPORT); /*端口*/

their_addr.sin_addr=iner_addr(argv

溫馨提示

  • 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ù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 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)論