




版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 感染科疫情防控工作總結(jié)與反思計(jì)劃
- 胃癌治療進(jìn)展
- 會(huì)計(jì)人員如何制定周密的工作計(jì)劃
- 開放式課堂激發(fā)幼兒探索精神計(jì)劃
- 前臺(tái)文員創(chuàng)新工作的實(shí)踐計(jì)劃
- 《貴州勁同礦業(yè)有限公司清鎮(zhèn)市麥格鄉(xiāng)貴耐鋁土礦(修編)礦產(chǎn)資源綠色開發(fā)利用方案(三合一)》專家組評(píng)審意見
- 第22課 活動(dòng)課:唱響《國(guó)際歌》 教學(xué)設(shè)計(jì)-2023-2024學(xué)年浙江省部編版歷史與社會(huì)九年級(jí)上冊(cè)
- 2025年浙江道路貨運(yùn)從業(yè)資格證模擬考試
- 腎部專業(yè)知識(shí)培訓(xùn)課件
- 2025年杭州貨運(yùn)從業(yè)資格證年考試題目
- 2025年榆林市公共交通總公司招聘(57人)筆試參考題庫(kù)附帶答案詳解
- 醫(yī)院培訓(xùn)課件:《多發(fā)性骨髓瘤》
- 2025年遼寧石化職業(yè)技術(shù)學(xué)院?jiǎn)握新殬I(yè)傾向性測(cè)試題庫(kù)審定版
- 2025年湖南省長(zhǎng)沙市單招職業(yè)傾向性測(cè)試題庫(kù)及參考答案
- 十八項(xiàng)核心制度培訓(xùn)課件
- 2024年遠(yuǎn)程教育行業(yè)市場(chǎng)運(yùn)營(yíng)現(xiàn)狀及行業(yè)發(fā)展趨勢(shì)報(bào)告
- 2025年2月上海市高三聯(lián)考高考調(diào)研英語(yǔ)試題(答案詳解)
- 2024-2025學(xué)年六年級(jí)上學(xué)期數(shù)學(xué)第三單元3.1-搭積木比賽(教案)
- DeepSeek從入門到精通
- 植保機(jī)械技術(shù)培訓(xùn)課件
- 2024年水利工程建設(shè)行業(yè)市場(chǎng)發(fā)展監(jiān)測(cè)及投資潛力預(yù)測(cè)報(bào)告
評(píng)論
0/150
提交評(píng)論