青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

網(wǎng)絡(luò)服務(wù)器軟件開發(fā)/中間件開發(fā),關(guān)注ACE/ICE/boost

C++博客 首頁 新隨筆 聯(lián)系 聚合 管理
  152 Posts :: 3 Stories :: 172 Comments :: 0 Trackbacks
?
進(jìn)程和線程編程

目 錄

  1. 進(jìn)程和線程編程
    1. 原始管道
      1. pipe()
      2. dup()
      3. dup2()
      4. popen()和pclose()
    2. 命名管道
      1. 創(chuàng)建FIFO
      2. 操作FIFO
      3. 阻塞FIFO
    3. 消息隊(duì)列
      1. msgget()
      2. msgsnd()
      3. msgrcv()
      4. msgctl()
    4. 信號(hào)量
      1. semget()
      2. semop()
      3. semctl()
    5. 共享內(nèi)存
      1. shmget()
      2. shmat()
      3. shmctl()
      4. shmdt()
    6. 線程
      1. 線程同步
      2. 使用信號(hào)量協(xié)調(diào)程序
      3. 代碼例子
        1. newthread
        2. exitthead
        3. getchannel
        4. def
        5. release
        6. redezvous
        7. unbouded


進(jìn)程和線程編程

??? 看一下UNIX系統(tǒng)中的進(jìn)程和Mach的任務(wù)和線程之間的關(guān)系。在UNIX系統(tǒng)中,一個(gè)進(jìn)程包括一個(gè)可執(zhí)行的程序和一系列的資源,例如文件描述符表和地址空間。在Mach中,一個(gè)任務(wù)僅包括一系列的資源;線程處理所有的可執(zhí)行代碼。一個(gè)Mach的任務(wù)可以有任意數(shù)目的線程和它相關(guān),同時(shí)每個(gè)線程必須和某個(gè)任務(wù)相關(guān)。和某一個(gè)給定的任務(wù)相關(guān)的所有線程都共享任務(wù)的資源。這樣,一個(gè)線程就是一個(gè)程序計(jì)數(shù)器、一個(gè)堆棧和一系列的寄存器。所有需要使用的數(shù)據(jù)結(jié)構(gòu)都屬于任務(wù)。一個(gè)UNIX系統(tǒng)中的進(jìn)程在Mach中對(duì)應(yīng)于一個(gè)任務(wù)和一個(gè)單獨(dú)的線程。
[目錄]


原始管道

??? 使用C語言創(chuàng)建管道要比在shell下使用管道復(fù)雜一些。如果要使用C語言創(chuàng)建一個(gè)簡(jiǎn)單的管道,可以使用系統(tǒng)調(diào)用pipe()。它接受一個(gè)參數(shù),也就是一個(gè)包括兩個(gè)整數(shù)的數(shù)組。如果系統(tǒng)調(diào)用成功,此數(shù)組將包括管道使用的兩個(gè)文件描述符。創(chuàng)建一個(gè)管道之后,一般情況下進(jìn)程將產(chǎn)生一個(gè)新的進(jìn)程。

??? 可以通過打開兩個(gè)管道來創(chuàng)建一個(gè)雙向的管道。但需要在子進(jìn)程中正確地設(shè)置文件描述必須在系統(tǒng)調(diào)用fork()中調(diào)用pipe(),否則子進(jìn)程將不會(huì)繼承文件描述符。當(dāng)使用半雙工管道時(shí),任何關(guān)聯(lián)的進(jìn)程都必須共享一個(gè)相關(guān)的祖先進(jìn)程。因?yàn)楣艿来嬖谟谙到y(tǒng)內(nèi)核之中,所以任何不在創(chuàng)建管道的進(jìn)程的祖先進(jìn)程之中的進(jìn)程都將無法尋址它。而在命名管道中卻不是這樣。

[目錄]


pipe()

系統(tǒng)調(diào)用:pipe();
原型:intpipe(intfd[2]);
返回值:如果系統(tǒng)調(diào)用成功,返回0
如果系統(tǒng)調(diào)用失敗返回-1:errno=EMFILE(沒有空閑的文件描述符)
EMFILE(系統(tǒng)文件表已滿)
EFAULT(fd數(shù)組無效)
注意fd[0]用于讀取管道,fd[1]用于寫入管道。
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
main()
{
intfd[2];
pipe(fd);
..
}
一旦創(chuàng)建了管道,我們就可以創(chuàng)建一個(gè)新的子進(jìn)程:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
main()
{
intfd[2];
pid_t childpid;
pipe(fd);
if((childpid=fork())==-1)
{
perror("fork");
exit(1);
}..
}

??? 如果父進(jìn)程希望從子進(jìn)程中讀取數(shù)據(jù),那么它應(yīng)該關(guān)閉fd1,同時(shí)子進(jìn)程關(guān)閉fd0。反之,如果父進(jìn)程希望向子進(jìn)程中發(fā)送數(shù)據(jù),那么它應(yīng)該關(guān)閉fd0,同時(shí)子進(jìn)程關(guān)閉fd1。因?yàn)槲募枋龇窃诟高M(jìn)程和子進(jìn)程之間共享,所以我們要及時(shí)地關(guān)閉不需要的管道的那一端。單從技術(shù)的角度來說,如果管道的一端沒有正確地關(guān)閉的話,你將無法得到一個(gè)EOF。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
main()
{
intfd[2];
pid_t childpid;
pipe(fd);
if((childpid=fork())==-1)
{
perror("fork");
exit(1);
}
if(childpid==0)
{
/*Child process closes up in put side of pipe*/
close(fd[0]);
}
else
{
/*Parent process closes up out put side of pipe*/
close(fd[1]);
}..
}

??? 正如前面提到的,一但創(chuàng)建了管道之后,管道所使用的文件描述符就和正常文件的文件描述符一樣了。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
intmain(void)
{
intfd[2],nbytes;
pid_tchildpid;
charstring[]="Hello,world!\n";
charreadbuffer[80];
pipe(fd);
if((childpid=fork())==-1)
{
perror("fork");
exit(1);
}
if(childpid==0)
{
/*Child process closes up in put side of pipe*/
close(fd[0]);
/*Send"string"through the out put side of pipe*/
write(fd[1],string,strlen(string));
exit(0);
}
else
{
/*Parent process closes up out put side of pipe*/
close(fd[1]);
/*Readinastringfromthepipe*/
nbytes=read(fd[0],readbuffer,sizeof(readbuffer));
printf("Receivedstring:%s",readbuffer);
}
return(0);
}

??? 一般情況下,子進(jìn)程中的文件描述符將會(huì)復(fù)制到標(biāo)準(zhǔn)的輸入和輸出中。這樣子進(jìn)程可以使用exec()執(zhí)行另一個(gè)程序,此程序繼承了標(biāo)準(zhǔn)的數(shù)據(jù)流。

[目錄]


dup()

系統(tǒng)調(diào)用:dup();
原型:intdup(intoldfd);
返回:如果系統(tǒng)調(diào)用成功,返回新的文件描述符
如果系統(tǒng)調(diào)用失敗,返回-1:errno=EBADF(oldfd不是有效的文件描述符)
EBADF(newfd超出范圍)
EMFILE(進(jìn)程的文件描述符太多)

??? 注意舊文件描述符oldfd沒有關(guān)閉。雖然舊文件描述符和新創(chuàng)建的文件描述符可以交換使用,但一般情況下需要首先關(guān)閉一個(gè)。系統(tǒng)調(diào)用dup()使用的是號(hào)碼最小的空閑的文件描述符。

再看下面的程序:
..
childpid=fork();
if(childpid==0)
{
/*Close up standard input of the child*/
close(0);
/*Dup licate the input side of pipe to stdin*/
dup(fd[0]);
execlp("sort","sort",NULL);
.
}
??? 因?yàn)槲募枋龇?(stdin)被關(guān)閉,所以dup()把管道的輸入描述符復(fù)制到它的標(biāo)準(zhǔn)輸入中。這樣我們可以調(diào)用execlp(),使用sort程序覆蓋子進(jìn)程的正文段。因?yàn)樾聞?chuàng)建的程序從它的父進(jìn)程中繼承了標(biāo)準(zhǔn)輸入/輸出流,所以它實(shí)際上繼承了管道的輸入端作為它的標(biāo)準(zhǔn)輸入端。現(xiàn)在,最初的父進(jìn)程送往管道的任何數(shù)據(jù)都將會(huì)直接送往sort函數(shù)。

[目錄]


dup2()

系統(tǒng)調(diào)用:dup2();
原型:intdup2(intoldfd,intnewfd);
返回值:如果調(diào)用成功,返回新的文件描述符
如果調(diào)用失敗,返回-1:errno=EBADF(oldfd不是有效的文件描述符)
EBADF(newfd超出范圍)
EMFILE(進(jìn)程的文件描述符太多)
注意dup2()將關(guān)閉舊文件描述符。

??? 使用此系統(tǒng)調(diào)用,可以將close操作和文件描述符復(fù)制操作集成到一個(gè)系統(tǒng)調(diào)用中。另外,此系統(tǒng)調(diào)用保證了操作的自動(dòng)進(jìn)行,也就是說操作不能被其他的信號(hào)中斷。這個(gè)操作將會(huì)在返回系統(tǒng)內(nèi)核之前完成。如果使用前一個(gè)系統(tǒng)調(diào)用dup(),程序員不得不在此之前執(zhí)行一個(gè)close()操作。請(qǐng)看下面的程序:
..

childpid=fork();
if(childpid==0)
{
/*Close stdin,dup licate the input side of pipe to stdin*/
dup2(0,fd[0]);
execlp("sort","sort",NULL);
..
}

[目錄]


popen()和pclose()

如果你認(rèn)為上面創(chuàng)建和使用管道的方法過于繁瑣的話,你也可以使用下面的簡(jiǎn)單的方法:

庫函數(shù):popen()和pclose();
原型:FILE*popen(char*command,char*type);
返回值:如果成功,返回一個(gè)新的文件流。
如果無法創(chuàng)建進(jìn)程或者管道,返回NULL。
??? 此標(biāo)準(zhǔn)的庫函數(shù)通過在系統(tǒng)內(nèi)部調(diào)用pipe()來創(chuàng)建一個(gè)半雙工的管道,然后它創(chuàng)建一個(gè)子進(jìn)程,啟動(dòng)shell,最后在shell上執(zhí)行command參數(shù)中的命令。管道中數(shù)據(jù)流的方向是由第二個(gè)參數(shù)type控制的。此參數(shù)可以是r或者w,分別代表讀或?qū)憽5荒芡瑫r(shí)為讀和寫。在Linux系統(tǒng)下,管道將會(huì)以參數(shù)type中第一個(gè)字符代表的方式打開。所以,如果你在參數(shù)type中寫入rw,管道將會(huì)以讀的方式打開。

??? 雖然此庫函數(shù)的用法很簡(jiǎn)單,但也有一些不利的地方。例如它失去了使用系統(tǒng)調(diào)用pipe()時(shí)可以有的對(duì)系統(tǒng)的控制。盡管這樣,因?yàn)榭梢灾苯拥厥褂胹hell命令,所以shell中的一些通配符和其他的一些擴(kuò)展符號(hào)都可以在command參數(shù)中使用。
使用popen()創(chuàng)建的管道必須使用pclose()關(guān)閉。其實(shí),popen/pclose和標(biāo)準(zhǔn)文件輸入/輸出流中的fopen()/fclose()十分相似。


庫函數(shù):pclose();
原型:intpclose(FILE*stream);
返回值:返回系統(tǒng)調(diào)用wait4()的狀態(tài)。
如果stream無效,或者系統(tǒng)調(diào)用wait4()失敗,則返回-1。
??? 注意此庫函數(shù)等待管道進(jìn)程運(yùn)行結(jié)束,然后關(guān)閉文件流。庫函數(shù)pclose()在使用popen()創(chuàng)建的進(jìn)程上執(zhí)行wait4()函數(shù)。當(dāng)它返回時(shí),它將破壞管道和文件系統(tǒng)。
??? 在下面的例子中,用sort命令打開了一個(gè)管道,然后對(duì)一個(gè)字符數(shù)組排序:

#include<stdio.h>
#defineMAXSTRS5
intmain(void)
{
intcntr;
FILE*pipe_fp;
char*strings[MAXSTRS]={"echo","bravo","alpha",
"charlie","delta"};
/*Createonewaypipelinewithcalltopopen()*/
if((pipe_fp=popen("sort","w"))==NULL)
{
perror("popen");
exit(1);
}
/*Processingloop*/
for(cntr=0;cntr<MAXSTRS;cntr++){
fputs(strings[cntr],pipe_fp);
fputc('\n',pipe_fp);
}
/*Closethepipe*/
pclose(pipe_fp);
return(0);
}
因?yàn)閜open()使用shell執(zhí)行命令,所以所有的shell擴(kuò)展符和通配符都可以使用。此外,它還可以和popen()一起使用重定向和輸出管道函數(shù)。再看下面的例子:
popen("ls~scottb","r");
popen("sort>/tmp/foo","w");
popen("sort|uniq|more","w");
下面的程序是另一個(gè)使用popen()的例子,它打開兩個(gè)管道(一個(gè)用于ls命令,另一個(gè)用于
sort命令):
#include<stdio.h>
intmain(void)
{
FILE*pipein_fp,*pipeout_fp;
charreadbuf[80];
/*Createonewaypipelinewithcalltopopen()*/
if((pipein_fp=popen("ls","r"))==NULL)
{
perror("popen");
exit(1);
}
/*Createonewaypipelinewithcalltopopen()*/
if((pipeout_fp=popen("sort","w"))==NULL)
{
perror("popen");
exit(1);
}
/*Processingloop*/
while(fgets(readbuf,80,pipein_fp))
fputs(readbuf,pipeout_fp);
/*Closethepipes*/
pclose(pipein_fp);
pclose(pipeout_fp);
return(0);
}
最后,我們?cè)倏匆粋€(gè)使用popen()的例子。此程序用于創(chuàng)建一個(gè)命令和文件之間的管道:
#include<stdio.h>
intmain(intargc,char*argv[])
{
FILE*pipe_fp,*infile;
charreadbuf[80];
if(argc!=3){
fprintf(stderr,"USAGE:popen3[command][filename]\n");
exit(1);
}
/*Open up input file*/
if((infile=fopen(argv[2],"rt"))==NULL)
{
perror("fopen");
exit(1);
}
/*Create one way pipe line with call topopen()*/
if((pipe_fp=popen(argv[1],"w"))==NULL)
{
perror("popen");
exit(1);
}
/*Processingloop*/
do{
fgets(readbuf,80,infile);
if(feof(infile))break;
fputs(readbuf,pipe_fp);
}while(!feof(infile));
fclose(infile);
pclose(pipe_fp);
return(0);
}
下面是使用此程序的例子:
popen3sortpopen3.c
popen3catpopen3.c
popen3morepopen3.c
popen3catpopen3.c|grepmain

[目錄]


命名管道

??? 命名管道和一般的管道基本相同,但也有一些顯著的不同:

*命名管道是在文件系統(tǒng)中作為一個(gè)特殊的設(shè)備文件而存在的。
*不同祖先的進(jìn)程之間可以通過管道共享數(shù)據(jù)。
*當(dāng)共享管道的進(jìn)程執(zhí)行完所有的I/O操作以后,命名管道將繼續(xù)保存在文件系統(tǒng)中以便以后使用。

??? 一個(gè)管道必須既有讀取進(jìn)程,也要有寫入進(jìn)程。如果一個(gè)進(jìn)程試圖寫入到一個(gè)沒有讀取進(jìn)程的管道中,那么系統(tǒng)內(nèi)核將會(huì)產(chǎn)生SIGPIPE信號(hào)。當(dāng)兩個(gè)以上的進(jìn)程同時(shí)使用管道時(shí),這一點(diǎn)尤其重要。

[目錄]


創(chuàng)建FIFO

??? 可以有幾種方法創(chuàng)建一個(gè)命名管道。頭兩種方法可以使用shell。

mknodMYFIFOp
mkfifoa=rwMYFIFO
??? 上面的兩個(gè)命名執(zhí)行同樣的操作,但其中有一點(diǎn)不同。命令mkfifo提供一個(gè)在創(chuàng)建之后直接改變FIFO文件存取權(quán)限的途徑,而命令mknod需要調(diào)用命令chmod。
??? 一個(gè)物理文件系統(tǒng)可以通過p指示器十分容易地分辨出一個(gè)FIFO文件。

$ls-lMYFIFO
prw-r--r--1rootroot0Dec1422:15MYFIFO|

??? 請(qǐng)注意在文件名后面的管道符號(hào)“|”。
??? 我們可以使用系統(tǒng)調(diào)用mknod()來創(chuàng)建一個(gè)FIFO管道:

庫函數(shù):mknod();
原型:intmknod(char*pathname,mode_tmode,dev_tdev);
返回值:如果成功,返回0
如果失敗,返回-1:errno=EFAULT(無效路徑名)
EACCES(無存取權(quán)限)
ENAMETOOLONG(路徑名太長(zhǎng))
ENOENT(無效路徑名)
ENOTDIR(無效路徑名)

??? 下面看一個(gè)使用C語言創(chuàng)建FIFO管道的例子:

mknod("/tmp/MYFIFO",S_IFIFO|0666,0);

??? 在這個(gè)例子中,文件/tmp/MYFIFO是要?jiǎng)?chuàng)建的FIFO文件。它的存取權(quán)限是0666。存取權(quán)限
也可以使用umask修改:

final_umask=requested_permissions&~original_umask

??? 一個(gè)常用的使用系統(tǒng)調(diào)用umask()的方法就是臨時(shí)地清除umask的值:
umask(0);
mknod("/tmp/MYFIFO",S_IFIFO|0666,0);

??? 另外,mknod()中的第三個(gè)參數(shù)只有在創(chuàng)建一個(gè)設(shè)備文件時(shí)才能用到。它包括設(shè)備文件的
主設(shè)備號(hào)和從設(shè)備號(hào)。
}
}


[目錄]


操作FIFO

??? FIFO上的I/O操作和正常管道上的I/O操作基本一樣,只有一個(gè)主要的不同。系統(tǒng)調(diào)用open用來在物理上打開一個(gè)管道。在半雙工的管道中,這是不必要的。因?yàn)楣艿涝谙到y(tǒng)內(nèi)核中,而不是在一個(gè)物理的文件系統(tǒng)中。在我們的例子中,我們將像使用一個(gè)文件流一樣使用管道,也就是使用fopen()打開管道,使用fclose()關(guān)閉它。
??? 請(qǐng)看下面的簡(jiǎn)單的服務(wù)程序進(jìn)程:
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<unistd.h>
#include<linux/stat.h>
#defineFIFO_FILE"MYFIFO"
intmain(void)
{
FILE*fp;
charreadbuf[80];
/*CreatetheFIFOifitdoesnotexist*/
umask(0);
mknod(FIFO_FILE,S_IFIFO|0666,0);
while(1)
{
fp=fopen(FIFO_FILE,"r");
fgets(readbuf,80,fp);
printf("Receivedstring:%s\n",readbuf);
fclose(fp);
return(0);
??? 因?yàn)镕IFO管道缺省時(shí)有阻塞的函數(shù),所以你可以在后臺(tái)運(yùn)行此程序:
$fifoserver&
??? 再來看一下下面的簡(jiǎn)單的客戶端程序:
#include<stdio.h>
#include<stdlib.h>
#defineFIFO_FILE"MYFIFO"
intmain(int argc,char* argv[])
{
FILE*fp;
if(argc!=2){
printf("USAGE:fifoclient[string]\n");
exit(1);
}
if((fp=fopen(FIFO_FILE,"w"))==NULL){
perror("fopen");
exit(1);
}
fputs(argv[1],fp);
fclose(fp);
return(0);
}

[目錄]


阻塞FIFO

??? 一般情況下,F(xiàn)IFO管道上將會(huì)有阻塞的情況發(fā)生。也就是說,如果一個(gè)FIFO管道打開供讀取的話,它將一直阻塞,直到其他的進(jìn)程打開管道寫入信息。這種過程反過來也一樣。如果你不需要阻塞函數(shù)的話,你可以在系統(tǒng)調(diào)用open()中設(shè)置O_NONBLOCK標(biāo)志,這樣可以取消缺省的阻塞函數(shù)。

[目錄]


消息隊(duì)列

??? 在UNIX的SystemV版本,AT&T引進(jìn)了三種新形式的IPC功能(消息隊(duì)列、信號(hào)量、以及共享內(nèi)存)。但BSD版本的UNIX使用套接口作為主要的IPC形式。Linux系統(tǒng)同時(shí)支持這兩個(gè)版本。
[目錄]


msgget()

系統(tǒng)調(diào)用msgget()

??? 如果希望創(chuàng)建一個(gè)新的消息隊(duì)列,或者希望存取一個(gè)已經(jīng)存在的消息隊(duì)列,你可以使用系統(tǒng)調(diào)用msgget()。

系統(tǒng)調(diào)用:msgget();
原型:intmsgget(key_t key,int msgflg);
返回值:如果成功,返回消息隊(duì)列標(biāo)識(shí)符
如果失敗,則返回-1:errno=EACCESS(權(quán)限不允許)
EEXIST(隊(duì)列已經(jīng)存在,無法創(chuàng)建)
EIDRM(隊(duì)列標(biāo)志為刪除)
ENOENT(隊(duì)列不存在)
ENOMEM(創(chuàng)建隊(duì)列時(shí)內(nèi)存不夠)
ENOSPC(超出最大隊(duì)列限制)

??? 系統(tǒng)調(diào)用msgget()中的第一個(gè)參數(shù)是關(guān)鍵字值(通常是由ftok()返回的)。然后此關(guān)鍵字值將會(huì)和其他已經(jīng)存在于系統(tǒng)內(nèi)核中的關(guān)鍵字值比較。這時(shí),打開和存取操作是和參數(shù)msgflg中的內(nèi)容相關(guān)的。
IPC_CREAT如果內(nèi)核中沒有此隊(duì)列,則創(chuàng)建它。
IPC_EXCL當(dāng)和IPC_CREAT一起使用時(shí),如果隊(duì)列已經(jīng)存在,則失敗。

??? 如果單獨(dú)使用IPC_CREAT,則msgget()要么返回一個(gè)新創(chuàng)建的消息隊(duì)列的標(biāo)識(shí)符,要么返回具有相同關(guān)鍵字值的隊(duì)列的標(biāo)識(shí)符。如果IPC_EXCL和IPC_CREAT一起使用,則msgget()要么創(chuàng)建一個(gè)新的消息隊(duì)列,要么如果隊(duì)列已經(jīng)存在則返回一個(gè)失敗值-1。IPC_EXCL單獨(dú)使用是沒有用處的。
下面看一個(gè)打開和創(chuàng)建一個(gè)消息隊(duì)列的例子:
intopen_queue(key_t keyval)
{
intqid;
if((qid=msgget(keyval,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(qid);
}

[目錄]


msgsnd()

系統(tǒng)調(diào)用msgsnd()

??? 一旦我們得到了隊(duì)列標(biāo)識(shí)符,我們就可以在隊(duì)列上執(zhí)行我們希望的操作了。如果想要往隊(duì)列中發(fā)送一條消息,你可以使用系統(tǒng)調(diào)用msgsnd():

系統(tǒng)調(diào)用:msgsnd();
原型:intmsgsnd(int msqid,struct msgbuf*msgp,int msgsz,int msgflg);
返回值:如果成功,0。
如果失敗,-1:errno=EAGAIN(隊(duì)列已滿,并且使用了IPC_NOWAIT)
EACCES(沒有寫的權(quán)限)
EFAULT(msgp地址無效)
EIDRM(消息隊(duì)列已經(jīng)刪除)
EINTR(當(dāng)?shù)却龑懖僮鲿r(shí),收到一個(gè)信號(hào))
EINVAL(無效的消息隊(duì)列標(biāo)識(shí)符,非正數(shù)的消息類型,或
者無效的消息長(zhǎng)度)
ENOMEM(沒有足夠的內(nèi)存復(fù)制消息緩沖區(qū))

??? 系統(tǒng)調(diào)用msgsnd()的第一個(gè)參數(shù)是消息隊(duì)列標(biāo)識(shí)符,它是由系統(tǒng)調(diào)用msgget返回的。第二個(gè)參數(shù)是msgp,是指向消息緩沖區(qū)的指針。參數(shù)msgsz中包含的是消息的字節(jié)大小,但不包括消息類型的長(zhǎng)度(4個(gè)字節(jié))。
??? 參數(shù)msgflg可以設(shè)置為0(此時(shí)為忽略此參數(shù)),或者使用IPC_NOWAIT。

??? 如果消息隊(duì)列已滿,那么此消息則不會(huì)寫入到消息隊(duì)列中,控制將返回到調(diào)用進(jìn)程中。如果沒有指明,調(diào)用進(jìn)程將會(huì)掛起,直到消息可以寫入到隊(duì)列中。
??? 下面是一個(gè)發(fā)送消息的程序:

intsend_message(int qid,struct mymsgbuf *qbuf)
{
intresult,length;
/*The length is essentially the size of the structure minus sizeof(mtype)*/
length=sizeof(structmymsgbuf)-sizeof(long);
if((result=msgsnd(qid,qbuf,length,0))==-1)
{
return(-1);
}
return(result);
}

??? 這個(gè)小程序試圖將存儲(chǔ)在緩沖區(qū)qbuf中的消息發(fā)送到消息隊(duì)列qid中。下面的程序是結(jié)合了上面兩個(gè)程序的一個(gè)完整程序:

#include<stdio.h>
#include<stdlib.h>
#include<linux/ipc.h>
#include<linux/msg.h>
main()
{
intqid;
key_t msgkey;
struct mymsgbuf{
longmtype;/*Message type*/
intrequest;/*Work request number*/
doublesalary;/*Employee's salary*/
}msg;
/*Generateour IPC key value*/
msgkey=ftok(".",'m');
/*Open/createthequeue*/
if((qid=open_queue(msgkey))==-1){
perror("open_queue");
exit(1);
}
/*Load up the message with a r bitrary test data*/
msg.mtype=1;/*Messagetypemustbeapositivenumber!*/
msg.request=1;/*Dataelement#1*/
msg.salary=1000.00;/*Data element #2(my yearly salary!)*/
/*Bombsaway!*/
if((send_message(qid,&msg))==-1){
perror("send_message");
exit(1);
}
}
??? 在創(chuàng)建和打開消息隊(duì)列以后,我們將測(cè)試數(shù)據(jù)裝入到消息緩沖區(qū)中。最后調(diào)用send_messag把消息發(fā)送到消息隊(duì)列中。現(xiàn)在在消息隊(duì)列中有了一條消息,我們可以使用ipcs命令來查看隊(duì)列的狀態(tài)。下面討論如何從隊(duì)列中獲取消息。可以使用系統(tǒng)調(diào)用msgrcv():

[目錄]


msgrcv()

系統(tǒng)調(diào)用:msgrcv();
原型:intmsgrcv(intmsqid,structmsgbuf*msgp,intmsgsz,longmtype,intmsgflg);
返回值:如果成功,則返回復(fù)制到消息緩沖區(qū)的字節(jié)數(shù)。
如果失敗,則返回-1:errno=E2BIG(消息的長(zhǎng)度大于msgsz,沒有MSG_NOERROR)
EACCES(沒有讀的權(quán)限)
EFAULT(msgp指向的地址是無效的)
EIDRM(隊(duì)列已經(jīng)被刪除)
EINTR(被信號(hào)中斷)
EINVAL(msgqid無效,或者msgsz小于0)
ENOMSG(使用IPC_NOWAIT,同時(shí)隊(duì)列中的消息無法滿足要求)

??? 很明顯,第一個(gè)參數(shù)用來指定將要讀取消息的隊(duì)列。第二個(gè)參數(shù)代表要存儲(chǔ)消息的消息緩沖區(qū)的地址。第三個(gè)參數(shù)是消息緩沖區(qū)的長(zhǎng)度,不包括mtype的長(zhǎng)度,它可以按照如下的方法計(jì)算:
??????? msgsz=sizeof(structmymsgbuf)-sizeof(long);
??? 第四個(gè)參數(shù)是要從消息隊(duì)列中讀取的消息的類型。如果此參數(shù)的值為0,那么隊(duì)列中最長(zhǎng)時(shí)間的一條消息將返回,而不論其類型是什么。
如果調(diào)用中使用了IPC_NOWAIT作為標(biāo)志,那么當(dāng)沒有數(shù)據(jù)可以使用時(shí),調(diào)用將把ENOMSG返回到調(diào)用進(jìn)程中。否則,調(diào)用進(jìn)程將會(huì)掛起,直到隊(duì)列中的一條消息滿足msgrcv()的參數(shù)要求。如果當(dāng)客戶端等待一條消息的時(shí)候隊(duì)列為空,將會(huì)返回EIDRM。如果進(jìn)程在等待消息的過程中捕捉到一個(gè)信號(hào),則返回EINTR。
??? 下面就是一個(gè)從隊(duì)列中讀取消息的程序:

intread_message(int qid,long type,struct mymsgbuf*qbuf)
{
intresult,length;
/*The length is essentially the size of the structure minus sizeof(mtype)*/
length=sizeof(structmymsgbuf)-sizeof(long);
if((result=msgrcv(qid,qbuf,length,type,0))==-1)
{
return(-1);
}
return(result);
}
??? 在成功地讀取了一條消息以后,隊(duì)列中的這條消息的入口將被刪除。
??? 參數(shù)msgflg中的MSG_NOERROR位提供一種額外的用途。如果消息的實(shí)際長(zhǎng)度大于msgsz,同時(shí)使用了MSG_NOERROR,那么消息將會(huì)被截?cái)啵挥信cmsgsz長(zhǎng)度相等的消息返回。一般情況下,系統(tǒng)調(diào)用msgrcv()會(huì)返回-1,而這條消息將會(huì)繼續(xù)保存在隊(duì)列中。我們可以利用這個(gè)特點(diǎn)編制一個(gè)程序,利用這個(gè)程序可以查看消息隊(duì)列的情況,看看符合我們條件的消息是否已經(jīng)到來:

intpeek_message(int qid,long type)
{
intresult,length;
if((result=msgrcv(qid,NULL,0,type,IPC_NOWAIT))==-1)
{
if(errno==E2BIG)
return(TRUE);
}
return(FALSE);
}
??? 在上面的程序中,我們忽略了緩沖區(qū)的地址和長(zhǎng)度。這樣,系統(tǒng)調(diào)用將會(huì)失敗。盡管如此,我們可以檢查返回的E2BIG值,它說明符合條件的消息確實(shí)存在。

[目錄]


msgctl()

系統(tǒng)調(diào)用msgctl()

??? 下面我們繼續(xù)討論如何使用一個(gè)給定的消息隊(duì)列的內(nèi)部數(shù)據(jù)結(jié)構(gòu)。我們可以使用系統(tǒng)調(diào)用msgctl ( )來控制對(duì)消息隊(duì)列的操作。

系統(tǒng)調(diào)用: msgctl( ) ;
調(diào)用原型: int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
返回值: 0 ,如果成功。
- 1,如果失敗:errno = EACCES (沒有讀的權(quán)限同時(shí)cmd 是IPC_STAT )
EFAULT (buf 指向的地址無效)
EIDRM (在讀取中隊(duì)列被刪除)
EINVAL (msgqid無效, 或者msgsz 小于0 )
EPERM (IPC_SET或者IPC_RMID 命令被使用,但調(diào)用程序沒有寫的權(quán)限)
下面我們看一下可以使用的幾個(gè)命令:
IPC_STAT
讀取消息隊(duì)列的數(shù)據(jù)結(jié)構(gòu)msqid_ds,并將其存儲(chǔ)在b u f指定的地址中。
IPC_SET
設(shè)置消息隊(duì)列的數(shù)據(jù)結(jié)構(gòu)msqid_ds中的ipc_perm元素的值。這個(gè)值取自buf參數(shù)。
IPC_RMID
從系統(tǒng)內(nèi)核中移走消息隊(duì)列。
??? 我們?cè)谇懊嬗懻撨^了消息隊(duì)列的數(shù)據(jù)結(jié)構(gòu)(msqid_ds)。系統(tǒng)內(nèi)核中為系統(tǒng)中的每一個(gè)消息隊(duì)列保存一個(gè)此數(shù)據(jù)結(jié)構(gòu)的實(shí)例。通過使用IPC_STAT命令,我們可以得到一個(gè)此數(shù)據(jù)結(jié)構(gòu)的副本。下面的程序就是實(shí)現(xiàn)此函數(shù)的過程:

int get_queue_ds( int qid, struct msgqid_ds *qbuf )
{
if( msgctl( qid, IPC_STAT, qbuf) == -1)
{
return(-1);
}
return(0);
}

??? 如果不能復(fù)制內(nèi)部緩沖區(qū),調(diào)用進(jìn)程將返回-1。如果調(diào)用成功,則返回0。緩沖區(qū)中應(yīng)該包括消息隊(duì)列中的數(shù)據(jù)結(jié)構(gòu)。
??? 消息隊(duì)列中的數(shù)據(jù)結(jié)構(gòu)中唯一可以改動(dòng)的元素就是ipc_perm。它包括隊(duì)列的存取權(quán)限和關(guān)于隊(duì)列創(chuàng)建者和擁有者的信息。你可以改變用戶的id、用戶的組id以及消息隊(duì)列的存取權(quán)限。
??? 下面是一個(gè)修改隊(duì)列存取模式的程序:

int change_queue_mode(int qid, char *mode )
{
struct msqid_ds tmpbuf;
/* Retrieve a current copy of the internal data structure */
get_queue_ds( qid, &tmpbuf);
/* Change the permissions using an old trick */
sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);
/* Update the internal data structure */
if( msgctl( qid, IPC_SET, &tmpbuf) == -1)
{
return(-1);
}
return(
}

??? 我們通過調(diào)用get_queue_ds來讀取隊(duì)列的內(nèi)部數(shù)據(jù)結(jié)構(gòu)。然后,我們調(diào)用sscanf( )修改數(shù)據(jù)結(jié)構(gòu)msg_perm中的mode 成員的值。但直到調(diào)用msgctl()時(shí),權(quán)限的改變才真正完成。在這里msgctl()使用的是IPC_SET命令。
??? 最后,我們使用系統(tǒng)調(diào)用msgctl ( )中的IPC_RMID命令刪除消息隊(duì)列:

int remove_queue(int qid )
{
if( msgctl( qid, IPC_RMID, 0) == -1)
{
return(-1);
}
return(0);
}
};

[目錄]


信號(hào)量

??? 信號(hào)量是一個(gè)可以用來控制多個(gè)進(jìn)程存取共享資源的計(jì)數(shù)器。它經(jīng)常作為一種鎖定機(jī)制來防止當(dāng)一個(gè)進(jìn)程正在存取共享資源時(shí),另一個(gè)進(jìn)程也存取同一資源。下面先簡(jiǎn)要地介紹一下信號(hào)量中涉及到的數(shù)據(jù)結(jié)構(gòu)。

1.內(nèi)核中的數(shù)據(jù)結(jié)構(gòu)semid_ds
和消息隊(duì)列一樣,系統(tǒng)內(nèi)核為內(nèi)核地址空間中的每一個(gè)信號(hào)量集都保存了一個(gè)內(nèi)部的數(shù)據(jù)結(jié)構(gòu)。數(shù)據(jù)結(jié)構(gòu)的原型是semid_ds。它是在linux/sem.h中做如下定義的:
/*One semid data structure for each set of semaphores in the system.*/
structsemid_ds{
structipc_permsem_perm;/*permissions..seeipc.h*/
time_tsem_otime;/*last semop time*/
time_tsem_ctime;/*last change time*/
structsem*sem_base;/*ptr to first semaphore in array*/
structwait_queue*eventn;
structwait_queue*eventz;
structsem_undo*undo;/*undo requestson this array*/
ushortsem_nsems;/*no. of semaphores in array*/
};
sem_perm是在linux/ipc.h定義的數(shù)據(jù)結(jié)構(gòu)ipc_perm的一個(gè)實(shí)例。它保存有信號(hào)量集的存取權(quán)限的信息,以及信號(hào)量集創(chuàng)建者的有關(guān)信息。
sem_otime最后一次semop()操作的時(shí)間。
sem_ctime最后一次改動(dòng)此數(shù)據(jù)結(jié)構(gòu)的時(shí)間。
sem_base指向數(shù)組中第一個(gè)信號(hào)量的指針。
sem_undo數(shù)組中沒有完成的請(qǐng)求的個(gè)數(shù)。
sem_nsems信號(hào)量集(數(shù)組)中的信號(hào)量的個(gè)數(shù)。

2.內(nèi)核中的數(shù)據(jù)結(jié)構(gòu)sem
在數(shù)據(jù)結(jié)構(gòu)semid_ds中包含一個(gè)指向信號(hào)量數(shù)組的指針。此數(shù)組中的每一個(gè)元素都是一個(gè)
數(shù)據(jù)結(jié)構(gòu)sem。它也是在linux/sem.h中定義的:
/*One semaphore structure for each semaphore in the system.*/
structsem{
shortsempid;/*pid of las toperation*/
ushortsemval;/*current value*/
ushortsemncnt;/*num procs awaiting increase in semval*/
ushortsemzcnt;/*num procs awaiting semval=0*/
};
sem_pid最后一個(gè)操作的PID(進(jìn)程ID)。
sem_semval信號(hào)量的當(dāng)前值。
sem_semncnt等待資源的進(jìn)程數(shù)目。
sem_semzcnt等待資源完全空閑的進(jìn)程數(shù)目。

[目錄]


semget()

??? 我們可以使用系統(tǒng)調(diào)用semget()創(chuàng)建一個(gè)新的信號(hào)量集,或者存取一個(gè)已經(jīng)存在的信號(hào)量集:

系統(tǒng)調(diào)用:semget();
原型:intsemget(key_t key,int nsems,int semflg);
返回值:如果成功,則返回信號(hào)量集的IPC標(biāo)識(shí)符。如果失敗,則返回-1:errno=EACCESS(沒有權(quán)限)
EEXIST(信號(hào)量集已經(jīng)存在,無法創(chuàng)建)
EIDRM(信號(hào)量集已經(jīng)刪除)
ENOENT(信號(hào)量集不存在,同時(shí)沒有使用IPC_CREAT)
ENOMEM(沒有足夠的內(nèi)存創(chuàng)建新的信號(hào)量集)
ENOSPC(超出限制)

??? 系統(tǒng)調(diào)用semget()的第一個(gè)參數(shù)是關(guān)鍵字值(一般是由系統(tǒng)調(diào)用ftok()返回的)。系統(tǒng)內(nèi)核將此值和系統(tǒng)中存在的其他的信號(hào)量集的關(guān)鍵字值進(jìn)行比較。打開和存取操作與參數(shù)semflg中的內(nèi)容相關(guān)。IPC_CREAT如果信號(hào)量集在系統(tǒng)內(nèi)核中不存在,則創(chuàng)建信號(hào)量集。IPC_EXCL當(dāng)和IPC_CREAT一同使用時(shí),如果信號(hào)量集已經(jīng)存在,則調(diào)用失敗。如果單獨(dú)使用IPC_CREAT,則semget()要么返回新創(chuàng)建的信號(hào)量集的標(biāo)識(shí)符,要么返回系統(tǒng)中已經(jīng)存在的同樣的關(guān)鍵字值的信號(hào)量的標(biāo)識(shí)符。如果IPC_EXCL和IPC_CREAT一同使用,則要么返回新創(chuàng)建的信號(hào)量集的標(biāo)識(shí)符,要么返回-1。IPC_EXCL單獨(dú)使用沒有意義。參數(shù)nsems指出了一個(gè)新的信號(hào)量集中應(yīng)該創(chuàng)建的信號(hào)量的個(gè)數(shù)。信號(hào)量集中最多的信號(hào)量的個(gè)數(shù)是在linux/sem.h中定義的:

#defineSEMMSL32/*<=512maxnumofsemaphoresperid*/
下面是一個(gè)打開和創(chuàng)建信號(hào)量集的程序:
intopen_semaphore_set(key_t keyval,int numsems)
{
intsid;
if(!numsems)
return(-1);
if((sid=semget(mykey,numsems,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(sid);
}
};


[目錄]


semop()

系統(tǒng)調(diào)用:semop();
調(diào)用原型:int semop(int semid,struct sembuf*sops,unsign ednsops);
返回值:0,如果成功。-1,如果失敗:errno=E2BIG(nsops大于最大的ops數(shù)目)
EACCESS(權(quán)限不夠)
EAGAIN(使用了IPC_NOWAIT,但操作不能繼續(xù)進(jìn)行)
EFAULT(sops指向的地址無效)
EIDRM(信號(hào)量集已經(jīng)刪除)
EINTR(當(dāng)睡眠時(shí)接收到其他信號(hào))
EINVAL(信號(hào)量集不存在,或者semid無效)
ENOMEM(使用了SEM_UNDO,但無足夠的內(nèi)存創(chuàng)建所需的數(shù)據(jù)結(jié)構(gòu))
ERANGE(信號(hào)量值超出范圍)

??? 第一個(gè)參數(shù)是關(guān)鍵字值。第二個(gè)參數(shù)是指向?qū)⒁僮鞯臄?shù)組的指針。第三個(gè)參數(shù)是數(shù)組中的操作的個(gè)數(shù)。參數(shù)sops指向由sembuf組成的數(shù)組。此數(shù)組是在linux/sem.h中定義的:

/*semop systemcall takes an array of these*/
structsembuf{
ushortsem_num;/*semaphore index in array*/
shortsem_op;/*semaphore operation*/
shortsem_flg;/*operation flags*/
sem_num將要處理的信號(hào)量的個(gè)數(shù)。
sem_op要執(zhí)行的操作。
sem_flg操作標(biāo)志。

??? 如果sem_op是負(fù)數(shù),那么信號(hào)量將減去它的值。這和信號(hào)量控制的資源有關(guān)。如果沒有使用IPC_NOWAIT,那么調(diào)用進(jìn)程將進(jìn)入睡眠狀態(tài),直到信號(hào)量控制的資源可以使用為止。如果sem_op是正數(shù),則信號(hào)量加上它的值。這也就是進(jìn)程釋放信號(hào)量控制的資源。最后,如果sem_op是0,那么調(diào)用進(jìn)程將調(diào)用sleep(),直到信號(hào)量的值為0。這在一個(gè)進(jìn)程等待完全空閑的資源時(shí)使用。


[目錄]


semctl()

系統(tǒng)調(diào)用:semctl();
原型:int semctl(int semid,int semnum,int cmd,union semunarg);
返回值:如果成功,則為一個(gè)正數(shù)。
如果失敗,則為-1:errno=EACCESS(權(quán)限不夠)
EFAULT(arg指向的地址無效)
EIDRM(信號(hào)量集已經(jīng)刪除)
EINVAL(信號(hào)量集不存在,或者semid無效)
EPERM(EUID沒有cmd的權(quán)利)
ERANGE(信號(hào)量值超出范圍)

??? 系統(tǒng)調(diào)用semctl用來執(zhí)行在信號(hào)量集上的控制操作。這和在消息隊(duì)列中的系統(tǒng)調(diào)用msgctl是十分相似的。但這兩個(gè)系統(tǒng)調(diào)用的參數(shù)略有不同。因?yàn)樾盘?hào)量一般是作為一個(gè)信號(hào)量集使用的,而不是一個(gè)單獨(dú)的信號(hào)量。所以在信號(hào)量集的操作中,不但要知道IPC關(guān)鍵字值,也要知道信號(hào)量集中的具體的信號(hào)量。這兩個(gè)系統(tǒng)調(diào)用都使用了參數(shù)cmd,它用來指出要操作的具體命令。兩個(gè)系統(tǒng)調(diào)用中的最后一個(gè)參數(shù)也不一樣。在系統(tǒng)調(diào)用msgctl中,最后一個(gè)參數(shù)是指向內(nèi)核中使用的數(shù)據(jù)結(jié)構(gòu)的指針。我們使用此數(shù)據(jù)結(jié)構(gòu)來取得有關(guān)消息隊(duì)列的一些信息,以及設(shè)置或者改變隊(duì)列的存取權(quán)限和使用者。但在信號(hào)量中支持額外的可選的命令,這樣就要求有一個(gè)更為復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。
系統(tǒng)調(diào)用semctl()的第一個(gè)參數(shù)是關(guān)鍵字值。第二個(gè)參數(shù)是信號(hào)量數(shù)目。

??? 參數(shù)cmd中可以使用的命令如下:
??? ·IPC_STAT讀取一個(gè)信號(hào)量集的數(shù)據(jù)結(jié)構(gòu)semid_ds,并將其存儲(chǔ)在semun中的buf參數(shù)中。
??? ·IPC_SET設(shè)置信號(hào)量集的數(shù)據(jù)結(jié)構(gòu)semid_ds中的元素ipc_perm,其值取自semun中的buf參數(shù)。
??? ·IPC_RMID將信號(hào)量集從內(nèi)存中刪除。
??? ·GETALL用于讀取信號(hào)量集中的所有信號(hào)量的值。
??? ·GETNCNT返回正在等待資源的進(jìn)程數(shù)目。
??? ·GETPID返回最后一個(gè)執(zhí)行semop操作的進(jìn)程的PID。
??? ·GETVAL返回信號(hào)量集中的一個(gè)單個(gè)的信號(hào)量的值。
??? ·GETZCNT返回這在等待完全空閑的資源的進(jìn)程數(shù)目。
??? ·SETALL設(shè)置信號(hào)量集中的所有的信號(hào)量的值。
??? ·SETVAL設(shè)置信號(hào)量集中的一個(gè)單獨(dú)的信號(hào)量的值。

??? 參數(shù)arg代表一個(gè)semun的實(shí)例。semun是在linux/sem.h中定義的:
/*arg for semctl systemcalls.*/
unionsemun{
intval;/*value for SETVAL*/
structsemid_ds*buf;/*buffer for IPC_STAT&IPC_SET*/
ushort*array;/*array for GETALL&SETALL*/
structseminfo*__buf;/*buffer for IPC_INFO*/
void*__pad;

??? val當(dāng)執(zhí)行SETVAL命令時(shí)使用。buf在IPC_STAT/IPC_SET命令中使用。代表了內(nèi)核中使用的信號(hào)量的數(shù)據(jù)結(jié)構(gòu)。array在使用GETALL/SETALL命令時(shí)使用的指針。
??? 下面的程序返回信號(hào)量的值。當(dāng)使用GETVAL命令時(shí),調(diào)用中的最后一個(gè)參數(shù)被忽略:

intget_sem_val(intsid,intsemnum)
{
return(semctl(sid,semnum,GETVAL,0));
}

??? 下面是一個(gè)實(shí)際應(yīng)用的例子:

#defineMAX_PRINTERS5
printer_usage()
{
int x;
for(x=0;x<MAX_PRINTERS;x++)
printf("Printer%d:%d\n\r",x,get_sem_val(sid,x));
}

??? 下面的程序可以用來初始化一個(gè)新的信號(hào)量值:

void init_semaphore(int sid,int semnum,int initval)
{
union semunsemopts;
semopts.val=initval;
semctl(sid,semnum,SETVAL,semopts);
}

??? 注意系統(tǒng)調(diào)用semctl中的最后一個(gè)參數(shù)是一個(gè)聯(lián)合類型的副本,而不是一個(gè)指向聯(lián)合類型的指針。

[目錄]


共享內(nèi)存

??? 共享內(nèi)存就是由幾個(gè)進(jìn)程共享一段內(nèi)存區(qū)域。這可以說是最快的IPC形式,因?yàn)樗鼰o須任何的中間操作(例如,管道、消息隊(duì)列等)。它只是把內(nèi)存段直接映射到調(diào)用進(jìn)程的地址空間中。這樣的內(nèi)存段可以是由一個(gè)進(jìn)程創(chuàng)建的,然后其他的進(jìn)程可以讀寫此內(nèi)存段。

??? 每個(gè)系統(tǒng)的共享內(nèi)存段在系統(tǒng)內(nèi)核中也保持著一個(gè)內(nèi)部的數(shù)據(jù)結(jié)構(gòu)shmid_ds。此數(shù)據(jù)結(jié)構(gòu)是在linux/shm.h中定義的,如下所示:

/* One shmid data structure for each shared memory segment in the system. */
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
time_t shm_atime; /* last attach time */
time_t shm_dtime; /* last detach time */
time_t shm_ctime; /* last change time */
unsigned short shm_cpid; /* pid of creator */
unsigned short shm_lpid; /* pid of last operator */
short shm_nattch; /* no. of current attaches */
/* the following are private */
unsigned short shm_npages; /* size of segment (pages) */
unsigned long *shm_pages; /* array of ptrs to frames -> SHMMAX */
struct vm_area_struct *attaches; /* descriptors for attaches */
};

shm_perm 是數(shù)據(jù)結(jié)構(gòu)ipc_perm的一個(gè)實(shí)例。這里保存的是內(nèi)存段的存取權(quán)限,和其他的有關(guān)內(nèi)存段創(chuàng)建者的信息。
shm_segsz 內(nèi)存段的字節(jié)大小。
shm_atime 最后一個(gè)進(jìn)程存取內(nèi)存段的時(shí)間。
shm_dtime 最后一個(gè)進(jìn)程離開內(nèi)存段的時(shí)間。
shm_ctime 內(nèi)存段最后改動(dòng)的時(shí)間。
shm_cpid 內(nèi)存段創(chuàng)建進(jìn)程的P I D。
shm_lpid 最后一個(gè)使用內(nèi)存段的進(jìn)程的P I D。
shm_nattch 當(dāng)前使用內(nèi)存段的進(jìn)程總數(shù)。

[目錄]


shmget()

系統(tǒng)調(diào)用:shmget();
原型:int shmget(key_t key,int size,int shmflg);
返回值:如果成功,返回共享內(nèi)存段標(biāo)識(shí)符。如果失敗,則返回-1:errno=EINVAL(無效的內(nèi)存段大小)
EEXIST(內(nèi)存段已經(jīng)存在,無法創(chuàng)建)
EIDRM(內(nèi)存段已經(jīng)被刪除)
ENOENT(內(nèi)存段不存在)
EACCES(權(quán)限不夠)
ENOMEM(沒有足夠的內(nèi)存來創(chuàng)建內(nèi)存段)

??? 系統(tǒng)調(diào)用shmget()中的第一個(gè)參數(shù)是關(guān)鍵字值(它是用系統(tǒng)調(diào)用ftok()返回的)。其他的操作都要依據(jù)shmflg中的命令進(jìn)行。
??? ·IPC_CREAT如果系統(tǒng)內(nèi)核中沒有共享的內(nèi)存段,則創(chuàng)建一個(gè)共享的內(nèi)存段。
??? ·IPC_EXCL當(dāng)和IPC_CREAT一同使用時(shí),如果共享內(nèi)存段已經(jīng)存在,則調(diào)用失敗。
??? 當(dāng)IPC_CREAT單獨(dú)使用時(shí),系統(tǒng)調(diào)用shmget()要么返回一個(gè)新創(chuàng)建的共享內(nèi)存段的標(biāo)識(shí)符,要么返回一個(gè)已經(jīng)存在的共享內(nèi)存段的關(guān)鍵字值。如果IPC_EXCL和IPC_CREAT一同使用,則要么系統(tǒng)調(diào)用新創(chuàng)建一個(gè)共享的內(nèi)存段,要么返回一個(gè)錯(cuò)誤值-1。IPC_EXCL單獨(dú)使用沒有意義。

??? 下面是一個(gè)定位和創(chuàng)建共享內(nèi)存段的程序:

int open_segment(key_t keyval,int segsize)
{
int shmid;
if((shmid=shmget(keyval,segsize,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(shmid);
}

??? 一旦一個(gè)進(jìn)程擁有了一個(gè)給定的內(nèi)存段的有效IPC標(biāo)識(shí)符,它的下一步就是將共享的內(nèi)存段映射到自己的地址空間中。

[目錄]


shmat()

系統(tǒng)調(diào)用: shmat();
原型:int shmat ( int shmid, char *shmaddr, int shmflg);
返回值:如果成功,則返回共享內(nèi)存段連接到進(jìn)程中的地址。如果失敗,則返回- 1:errno = EINVAL (無效的IPC ID 值或者無效的地址)
ENOMEM (沒有足夠的內(nèi)存)
EACCES (存取權(quán)限不夠)

??? 如果參數(shù)a d d r的值為0,那么系統(tǒng)內(nèi)核則試圖找出一個(gè)沒有映射的內(nèi)存區(qū)域。我們推薦使用這種方法。你可以指定一個(gè)地址,但這通常是為了加快對(duì)硬件設(shè)備的存取,或者解決和其他程序的沖突。
??? 下面的程序中的調(diào)用參數(shù)是一個(gè)內(nèi)存段的I P C標(biāo)識(shí)符,返回內(nèi)存段連接的地址:

char *attach_segment(int shmid)
{
return(shmat(shmid, 0, 0));
}

??? 一旦內(nèi)存段正確地連接到進(jìn)程以后,進(jìn)程中就有了一個(gè)指向該內(nèi)存段的指針。這樣,以后就可以使用指針來讀取此內(nèi)存段了。但一定要注意不能丟失該指針的初值。

[目錄]


shmctl()

系統(tǒng)調(diào)用:shmctl ( ) ;
原型:int shmctl( int shmqid, int cmd, struct shmid_ds *buf );
返回值: 0 ,如果成功。
-1,如果失敗:errno = EACCES (沒有讀的權(quán)限,同時(shí)命令是IPC_STAT)
EFAULT(buf指向的地址無效,同時(shí)命令是IPC_SET和IPC_STAT )
EIDRM (內(nèi)存段被移走)
EINVAL (shmqid 無效)
EPERM (使用IPC_SET 或者IPC_RMID 命令,但調(diào)用進(jìn)程沒有寫的權(quán)限)
IPC_STAT 讀取一個(gè)內(nèi)存段的數(shù)據(jù)結(jié)構(gòu)shmid_ds,并將它存儲(chǔ)在buf參數(shù)指向的地址中。
IPC_SET 設(shè)置內(nèi)存段的數(shù)據(jù)結(jié)構(gòu)shmid_ds中的元素ipc_perm的值。從參數(shù)buf中得到要設(shè)置的值。
IPC_RMID 標(biāo)志內(nèi)存段為移走。

??? 命令I(lǐng)PC_RMID并不真正從系統(tǒng)內(nèi)核中移走共享的內(nèi)存段,而是把內(nèi)存段標(biāo)記為可移除。進(jìn)程調(diào)用系統(tǒng)調(diào)用shmdt()脫離一個(gè)共享的內(nèi)存段。

[目錄]


shmdt()

系統(tǒng)調(diào)用:shmdt();
調(diào)用原型:int shmdt ( char *shmaddr );
返回值:如果失敗,則返回- 1:errno = EINVAL (無效的連接地址)

??? 當(dāng)一個(gè)進(jìn)程不在需要共享的內(nèi)存段時(shí),它將會(huì)把內(nèi)存段從其地址空間中脫離。但這不等于將共享內(nèi)存段從系統(tǒng)內(nèi)核中移走。當(dāng)進(jìn)程脫離成功后,數(shù)據(jù)結(jié)構(gòu)shmid_ds中元素shm_nattch將減1。當(dāng)此數(shù)值減為0以后,系統(tǒng)內(nèi)核將物理上把內(nèi)存段從系統(tǒng)內(nèi)核中移走。

[目錄]


線程

??? 線程通常叫做輕型的進(jìn)程。雖然這個(gè)叫法有些簡(jiǎn)單化,但這有利于了解線程的概念。因?yàn)榫€程和進(jìn)程比起來很小,所以相對(duì)來說,線程花費(fèi)更少的CPU資源。進(jìn)程往往需要它們自己的資源,但線程之間可以共享資源,所以線程更加節(jié)省內(nèi)存。Mach的線程使得程序員可以編寫并發(fā)運(yùn)行的程序,而這些程序既可以運(yùn)行在單處理器的機(jī)器上,也可以運(yùn)行在多處理器的機(jī)器中。另外,在單處理器環(huán)境中,當(dāng)應(yīng)用程序執(zhí)行容易引起阻塞和延遲的操作時(shí),線程可以提高效率。

??? 用子函數(shù)pthread_create創(chuàng)建一個(gè)新的線程。它有四個(gè)參數(shù):一個(gè)用來保存線程的線程變量、一個(gè)線程屬性、當(dāng)線程執(zhí)行時(shí)要調(diào)用的函數(shù)和一個(gè)此函數(shù)的參數(shù)。例如:
pthread_ta_thread ;
pthread_attr_ta_thread_attribute ;
void thread_function(void *argument);
char * some_argument;
pthread_create( &a_thread, a_thread_attribute, (void *)&thread_function,
(void *) &some_argument);
??? 線程屬性只指明了需要使用的最小的堆棧大小。在以后的程序中,線程的屬性可以指定其他的值,但現(xiàn)在大部分的程序可以使用缺省值。不像UNIX系統(tǒng)中使用fork系統(tǒng)調(diào)用創(chuàng)建的進(jìn)程,它們和它們的父進(jìn)程使用同一個(gè)執(zhí)行點(diǎn),線程使用在pthread_create中的參數(shù)指明要開始執(zhí)行的函數(shù)。

??? 現(xiàn)在我們可以編制第一個(gè)程序了。我們編制一個(gè)多線程的應(yīng)用程序,在標(biāo)準(zhǔn)輸出中打印“Hello Wo r l d”。首先我們需要兩個(gè)線程變量,一個(gè)新線程開始執(zhí)行時(shí)可以調(diào)用的函數(shù)。我們還需要指明每一個(gè)線程應(yīng)該打印的信息。一個(gè)做法是把要打印的字符串分開,給每一個(gè)線程一個(gè)字符串作為開始的參數(shù)。請(qǐng)看下面的代碼:
void print_message_function( void *ptr );
main( )
{
pthread_t thread1, thread2;
char *message1 = "Hello";
char *message2 = "Wo r l d " ;
pthread_create( &thread1, pthread_attr_default,
(void*)&print_message_function, (void*) message1);
pthread_create(&thread2, pthread_attr_default,
(void*)&print_message_function, (void*) message2);
exit( 0 ) ;
}
void print_message_function( void *ptr )
{
char *message;
message = (char *) ptr;
printf("%s ", message);
}

??? 程序通過調(diào)用pthread_create創(chuàng)建第一個(gè)線程,并將“Hello”作為它的啟動(dòng)參數(shù)。第二個(gè)線程的參數(shù)是“World”。當(dāng)?shù)谝粋€(gè)線程開始執(zhí)行時(shí),它使用參數(shù)“Hello”執(zhí)行函數(shù)print_message_function。它在標(biāo)準(zhǔn)輸出中打印“Hello”,然后結(jié)束對(duì)函數(shù)的調(diào)用。線程當(dāng)離開它的初始化函數(shù)時(shí)就將終止,所以第一個(gè)線程在打印完“Hello”后終止。當(dāng)?shù)诙€(gè)線程執(zhí)行時(shí),它打印“World”然后終止。但這個(gè)程序有兩個(gè)主要的缺陷。
??? 首先也是最重要的是線程是同時(shí)執(zhí)行的。這樣就無法保證第一個(gè)線程先執(zhí)行打印語句。所以你很可能在屏幕上看到“World Hello”,而不是“Hello World”。請(qǐng)注意對(duì)exit的調(diào)用是父線程在主程序中使用的。這樣,如果父線程在兩個(gè)子線程調(diào)用打印語句之前調(diào)用exit,那么將不會(huì)有任何的打印輸出。這是因?yàn)閑xit函數(shù)將會(huì)退出進(jìn)程,同時(shí)釋放任務(wù),所以結(jié)束了所有的線程。任何線程(不論是父線程或者子線程)調(diào)用exit 都會(huì)終止所有其他線程。如果希望線程分別終止,可以使用pthread_exit函數(shù)。
我們可以使用一個(gè)辦法彌補(bǔ)此缺陷。我們可以在父線程中插入一個(gè)延遲程序,給子線程足夠的時(shí)間完成打印的調(diào)用。同樣,在調(diào)用第二個(gè)之前也插入一個(gè)延遲程序保證第一個(gè)線程在第二個(gè)線程執(zhí)行之前完成任務(wù)。

void print_message_function( void *ptr );
main ( )
{
pthread_t thread1, thread2;
char *message1 = "Hello”;
char *message2 = "Wo r l d " ;
pthread_create( &thread1, pthread_attr_default,
(void *) &print_message_function, (void *) message1);
sleep (10) ;
pthread_create(&thread2, pthread_attr_default,
(void *) &print_message_function, (void *) message2);
sleep ( 10 ) ;
exit (0) ;
}
void print_message_function( void *ptr )
{
char *message;
message = (char *) ptr;
printf("%s", message);
pthread_exit(0) ;
}

??? 這樣是否達(dá)到了我們的要求了呢?不盡如此,因?yàn)橐揽繒r(shí)間的延遲執(zhí)行同步是不可靠的。這里遇到的情形和一個(gè)分布程序和共享資源的情形一樣。共享的資源是標(biāo)準(zhǔn)的輸出設(shè)備,分布計(jì)算的程序是三個(gè)線程。
其實(shí)這里還有另外一個(gè)錯(cuò)誤。函數(shù)sleep和函數(shù)e x i t一樣和進(jìn)程有關(guān)。當(dāng)線程調(diào)用sleep時(shí),整個(gè)的進(jìn)程都處于睡眠狀態(tài),也就是說,所有的三個(gè)線程都進(jìn)入睡眠狀態(tài)。這樣我們實(shí)際上沒有解決任何的問題。希望使一個(gè)線程睡眠的函數(shù)是pthread_delay_np。例如讓一個(gè)線程睡眠2秒鐘,用如下程序:

struct timespec delay;
delay.tv_sec = 2;
delay.tv_nsec = 0;
pthread_delay_np( &delay );
}

[目錄]


線程同步

POSIX提供兩種線程同步的方法,mutex和條件變量。mutex是一種簡(jiǎn)單的加鎖的方法來控制對(duì)共享資源的存取。我們可以創(chuàng)建一個(gè)讀/寫程序,它們共用一個(gè)共享緩沖區(qū),使用mutex來控制對(duì)緩沖區(qū)的存取。
void reader_function(void);
void writer_function(void);
char buf f e r ;
int buffer_has_item = 0;
pthread_mutex_t mutex;
struct timespec delay;
main( )
{
pthread_t reader;
delay.tv_sec = 2;
delay.tv_nsec = 0;
pthread_mutex_init(&mutex, pthread_mutexattr_default);
pthread_create( &reader, pthread_attr_default, (void*)&reader_function,
N U L L ) ;
writer_function( )
void writer_function(void)
{
while( 1 )
{
pthread_mutex_lock( &mutex );
if ( buffer_has_item == 0 )
{
buffer = make_new_item();
buffer_has_item = 1;
}
pthread_mutex_unlock( &mutex );
pthread_delay_np( &delay );
}
}
void reader_function(void)
{
while( 1 )
{
pthread_mutex_lock( &mutex );
if ( buffer_has_item == 1)
{
consume_item( buffer );
buffer_has_item = 0;
}
pthread_mutex_unlock( &mutex );
pthread_delay_np( &delay );
}
}
在上面的程序中,我們假定緩沖區(qū)只能保存一條信息,這樣緩沖區(qū)只有兩個(gè)狀態(tài),有一條信息或者沒有信息。使用延遲是為了避免一個(gè)線程永遠(yuǎn)占有mutex。
但mutex的缺點(diǎn)在于它只有兩個(gè)狀態(tài),鎖定和非鎖定。POSIX的條件變量通過允許線程阻塞和等待另一個(gè)線程的信號(hào)方法,從而彌補(bǔ)了mutex的不足。當(dāng)接受到一個(gè)信號(hào)時(shí),阻塞線程將會(huì)被喚起,并試圖獲得相關(guān)的mutex的鎖。

[目錄]


使用信號(hào)量協(xié)調(diào)程序

我們可以使用信號(hào)量重新看一下上面的讀/寫程序。涉及信號(hào)量的操作是semaphore_up、semaphore_down、semaphore_init、semaphore_destroy和semaphore_decrement。所有這些操作都只有一個(gè)參數(shù),一個(gè)指向信號(hào)量目標(biāo)的指針。
void reader_function(void);
void writer_function(void);
char buffer ;
Semaphore writers_turn;
Semaphore readers_turn;
main( )
{
pthread_t reader;
semaphore_init( &readers_turn );
semaphore_init( &writers_turn );
/* writer must go first */
semaphore_down( &readers_turn );
pthread_create( &reader, pthread_attr_default,
(void *)&reader_function, NULL);
w r i t e r _ f u n c t i o n ( ) ;
}
void writer_function(void)
{
w h i l e ( 1 )
{
semaphore_down( &writers_turn );
b u ffer = make_new_item();
semaphore_up( &readers_turn );
}
}
void reader_function(void)
{
w h i l e ( 1 )
{
semaphore_down( &readers_turn );
consume_item( buffer );
semaphore_up( &writers_turn );
}
}
這個(gè)例子也沒有完全地利用一般信號(hào)量的所有函數(shù)。我們可以使用信號(hào)量重新編寫“Hello world” 的程序:
void print_message_function( void *ptr );
Semaphore child_counter;
Semaphore worlds_turn;
main( )
{
pthread_t thread1, thread2;
char *message1 = "Hello";
char *message2 = "Wo r l d " ;
semaphore_init( &child_counter );
semaphore_init( &worlds_turn );
semaphore_down( &worlds_turn ); /* world goes second */
semaphore_decrement( &child_counter ); /* value now 0 */
semaphore_decrement( &child_counter ); /* value now -1 */
/*
* child_counter now must be up-ed 2 times for a thread blocked on it
* to be released
*
* /
pthread_create( &thread1, pthread_attr_default,
(void *) &print_message_function, (void *) message1);
semaphore_down( &worlds_turn );
pthread_create(&thread2, pthread_attr_default,
(void *) &print_message_function, (void *) message2);
semaphore_down( &child_counter );
/* not really necessary to destroy since we are exiting anyway */
semaphore_destroy ( &child_counter );
semaphore_destroy ( &worlds_turn );
e x i t ( 0 ) ;
}
void print_message_function( void *ptr )
{
char *message;
message = (char *) ptr;
printf("%s ", message);
fflush(stdout);
semaphore_up( &worlds_turn );
semaphore_up( &child_counter );
p t h r e a d _ e x i t ( 0 ) ;
}
信號(hào)量c h i l d _ c o u n t e r用來強(qiáng)迫父線程阻塞,直到兩個(gè)子線程執(zhí)行完p r i n t f語句和其后的semaphore_up( &child_counter )語句才繼續(xù)執(zhí)行。

Semaphore.h

#ifndef SEMAPHORES
#define SEMAPHORES
#include
#include
typedef struct Semaphore
{
int v;
pthread_mutex_t mutex;
pthread_cond_t cond;
}
S e m a p h o r e ;
int semaphore_down (Semaphore * s);
int semaphore_decrement (Semaphore * s);
int semaphore_up (Semaphore * s);
void semaphore_destroy (Semaphore * s);
void semaphore_init (Semaphore * s);
int semaphore_value (Semaphore * s);
int tw_pthread_cond_signal (pthread_cond_t * c);
int tw_pthread_cond_wait (pthread_cond_t * c, pthread_mutex_t * m);
int tw_pthread_mutex_unlock (pthread_mutex_t * m);
int tw_pthread_mutex_lock (pthread_mutex_t * m);
void do_error (char *msg);
# e n d i f

Semaphore.c

#include "semaphore.h"
/ *
* function must be called prior to semaphore use.
*
* /
v o i d
semaphore_init (Semaphore * s)
{
s->v = 1;
if (pthread_mutex_init (&(s->mutex), pthread_mutexattr_default) == -1)
do_error ("Error setting up semaphore mutex");
if (pthread_cond_init (&(s->cond), pthread_condattr_default) == -1)
do_error ("Error setting up semaphore condition signal");
* function should be called when there is no longer a need for
* the semaphore.
*
* /
v o i d
semaphore_destroy (Semaphore * s)
{
if (pthread_mutex_destroy (&(s->mutex)) == -1)
do_error ("Error destroying semaphore mutex");
if (pthread_cond_destroy (&(s->cond)) == -1)
do_error ("Error destroying semaphore condition signal");
}
/ *
* function increments the semaphore and signals any threads that
* are blocked waiting a change in the semaphore.
*
* /
i n t
semaphore_up (Semaphore * s)
{
int value_after_op;
tw_pthread_mutex_lock (&(s->mutex));
( s - > v ) + + ;
value_after_op = s->v;
tw_pthread_mutex_unlock (&(s->mutex));
tw_pthread_cond_signal (&(s->cond));
return (value_after_op);
}
/ *
* function decrements the semaphore and blocks if the semaphore is
* <= 0 until another thread signals a change.
*
* /
i n t
semaphore_down (Semaphore * s)
{
int value_after_op;
tw_pthread_mutex_lock (&(s->mutex));
while (s->v <= 0)
{
tw_pthread_cond_wait (&(s->cond), &(s->mutex));
}
( s - > v ) - - ;
value_after_op = s->v;
tw_pthread_mutex_unlock (&(s->mutex));
return (value_after_op);
}
/ *
* function does NOT block but simply decrements the semaphore.
* should not be used instead of down -- only for programs where
* multiple threads must up on a semaphore before another thread
* can go down, i.e., allows programmer to set the semaphore to
* a negative value prior to using it for synchronization.
*
* /
i n t
semaphore_decrement (Semaphore * s)
{
int value_after_op;
tw_pthread_mutex_lock (&(s->mutex));
s - > v - - ;
value_after_op = s->v;
tw_pthread_mutex_unlock (&(s->mutex));
return (value_after_op);
}
/ *
* function returns the value of the semaphore at the time the
* critical section is accessed. obviously the value is not guarenteed
* after the function unlocks the critical section. provided only
* for casual debugging, a better approach is for the programmar to
* protect one semaphore with another and then check its value.
* an alternative is to simply record the value returned by semaphore_up
* or semaphore_down.
*
* /
i n t
semaphore_value (Semaphore * s)
{
/* not for sync */
int value_after_op;
tw_pthread_mutex_lock (&(s->mutex));
value_after_op = s->v;
tw_pthread_mutex_unlock (&(s->mutex));
return (value_after_op);
}
/* -------------------------------------------------------------------- */
/* The following functions replace standard library functions in that */
/* they exit on any error returned from the system calls. Saves us */
/* from having to check each and every call above. */
/* -------------------------------------------------------------------- */
i n t
tw_pthread_mutex_unlock (pthread_mutex_t * m)
{
int return_value;
if ((return_value = pthread_mutex_unlock (m)) == -1)
do_error ("pthread_mutex_unlock");
return (return_value);
}
i n t
tw_pthread_mutex_lock (pthread_mutex_t * m)
{
int return_value;
if ((return_value = pthread_mutex_lock (m)) == -1)
do_error ("pthread_mutex_lock");
return (return_value);
}
i n t
tw_pthread_cond_wait (pthread_cond_t * c, pthread_mutex_t * m)
{
int return_value;
if ((return_value = pthread_cond_wait (c, m)) == -1)
do_error ("pthread_cond_wait");
return (return_value);
}
i n t
tw_pthread_cond_signal (pthread_cond_t * c)
{
int return_value;
if ((return_value = pthread_cond_signal (c)) == -1)
do_error ("pthread_cond_signal");
return (return_value);
}
/ *
* function just prints an error message and exits
*
* /
v o i d
do_error (char *msg)
{
perror (msg);
exit (1);
}

[目錄]


代碼例子

[目錄]


newthread

/***********************************************************************
??? Case study source code from the book `The Linux A to Z'
??? by Phil Cornes. Published by Prentice Hall, 1996.
??? Copyright (C) 1996 Phil Cornes

??? This program is free software; you can redistribute it and/or modify
??? it under the terms of the GNU General Public License as published by
??? the Free Software Foundation; either version 2 of the License, or
??? (at your option) any later version.

??? This program is distributed in the hope that it will be useful,
??? but WITHOUT ANY WARRANTY; without even the implied warranty of
??? MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.? See the
??? GNU General Public License for more details.

??? You should have received a copy of the GNU General Public License
??? along with this program; if not, write to the Free Software
??? Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

new_thread(int (*start_addr)(void), int stack_size)
{
??? struct context *ptr;
??? int esp;

??? /* 1 */
??? if (!(ptr = (struct context *)malloc(sizeof(struct context))))
??????? return 0;

??? /* 2 */
??? if (!(ptr->stack = (char *)malloc(stack_size)))
??????? return 0;

??? /* 3 */
??? esp = (int)(ptr->stack+(stack_size-4));
??? *(int *)esp = (int)exit_thread;
??? *(int *)(esp-4) = (int)start_addr;
??? *(int *)(esp-8) = esp-4;
??? ptr->ebp = esp-8;

??? /* 4 */
??? if (thread_count++)
??? {
??????? /* 5 */
??????? ptr->next = current->next;
??????? ptr->prev = current;
??????? current->next->prev = ptr;
??????? current->next = ptr;
??? }
??? else
??? {
??????? /* 6 */
??????? ptr->next = ptr;
??????? ptr->prev = ptr;
??????? current = ptr;
??????? switch_context(&main_thread, current);
??? }

??? return 1;
}

[目錄]


exitthead

/***********************************************************************
??? Case study source code from the book `The Linux A to Z'
??? by Phil Cornes. Published by Prentice Hall, 1996.
??? Copyright (C) 1996 Phil Cornes

??? This program is free software; you can redistribute it and/or modify
??? it under the terms of the GNU General Public License as published by
??? the Free Software Foundation; either version 2 of the License, or
??? (at your option) any later version.

??? This program is distributed in the hope that it will be useful,
??? but WITHOUT ANY WARRANTY; without even the implied warranty of
??? MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.? See the
??? GNU General Public License for more details.

??? You should have received a copy of the GNU General Public License
??? along with this program; if not, write to the Free Software
??? Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

static exit_thread(void)
{
??? struct context dump, *ptr;

??? /* 1 */
??? if (--thread_count)
??? {
??????? /* 2 */
??????? ptr = current;
??????? current->prev->next = current->next;
??????? current->next->prev = current->prev;
??????? current = current->next;
??????? free(ptr->stack);
??????? free(ptr);
??????? switch_context(&dump, current);
??? }
??? else
??? {
??????? /* 3 */
??????? free(current->stack);
??????? free(current);
??????? switch_context(&dump, &main_thread);
??? }
}

[目錄]


getchannel

/***********************************************************************
??? Case study source code from the book `The Linux A to Z'
??? by Phil Cornes. Published by Prentice Hall, 1996.
??? Copyright (C) 1996 Phil Cornes

??? This program is free software; you can redistribute it and/or modify
??? it under the terms of the GNU General Public License as published by
??? the Free Software Foundation; either version 2 of the License, or
??? (at your option) any later version.

??? This program is distributed in the hope that it will be useful,
??? but WITHOUT ANY WARRANTY; without even the implied warranty of
??? MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.? See the
??? GNU General Public License for more details.

??? You should have received a copy of the GNU General Public License
??? along with this program; if not, write to the Free Software
??? Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

get_channel(int number)
{
??? struct channel *ptr;

??? /* 1 */
??? for (ptr = channel_list; ptr; ptr = ptr->link)
??????? if (ptr->number==number)
??????????? return((int)ptr);

??? /* 2 */
??? if (!(ptr = (struct channel *)malloc(sizeof(struct channel))))
??????? return 0;

??? /* 3 */
??? ptr->number = number;
??? ptr->message_list = 0;
??? ptr->message_tail = 0;
??? ptr->sr_flag = 0;
??? ptr->link = channel_list;
??? channel_list = ptr;
??? return((int)ptr);
}

[目錄]


def

/***********************************************************************
??? Case study source code from the book `The Linux A to Z'
??? by Phil Cornes. Published by Prentice Hall, 1996.
??? Copyright (C) 1996 Phil Cornes

??? This program is free software; you can redistribute it and/or modify
??? it under the terms of the GNU General Public License as published by
??? the Free Software Foundation; either version 2 of the License, or
??? (at your option) any later version.

??? This program is distributed in the hope that it will be useful,
??? but WITHOUT ANY WARRANTY; without even the implied warranty of
??? MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.? See the
??? GNU General Public License for more details.

??? You should have received a copy of the GNU General Public License
??? along with this program; if not, write to the Free Software
??? Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

#include <string.h>

struct context???????????????????? /* One structure for each thread */
{
??? int ebp;??????????? /* Base pointer (stack frame pointer) store */
??? char *stack;??????? /* Pointer to memory block for thread stack */
??? struct context *next;????? /* Round robin circular list pointer */
??? struct context *prev;????? /* Round robin circular list pointer */
};

struct channel????? /* One structure for each communication channel */
{
??? int number;?????????????????????????????????? /* Channel number */
??? int sr_flag;???? /* 0=no queue, 1=send queued, 2=receive queued */
??? struct channel *link;?????????? /* Link to next channel in list */
??? struct message *message_list;????????? /* Head of message queue */
??? struct message *message_tail;????????? /* Tail of message queue */
};

struct message?????? /* One structure for each pending send/receive */
{
??? int size;?????????????????????????? /* Size of message in bytes */
??? char *addr;????????????????????? /* Pointer to start of message */
??? struct message *link;????????? /* Link to next message in queue */
??? struct context *thread;?? /* Which thread blocks on this struct */
};

static struct context main_thread;??? /* Storage for main() details */
static struct context *current;?????? /* Currently executing thread */
static int thread_count = 0;?????? /* Number of threads to schedule */
static struct channel *channel_list = 0;??? /* List of all channels */

static int switch_context(struct context *, struct context *);
static int exit_thread(void);
static int rendezvous(struct channel *, char *, int, int);

[目錄]


release

/***********************************************************************
??? Case study source code from the book `The Linux A to Z'
??? by Phil Cornes. Published by Prentice Hall, 1996.
??? Copyright (C) 1996 Phil Cornes

??? This program is free software; you can redistribute it and/or modify
??? it under the terms of the GNU General Public License as published by
??? the Free Software Foundation; either version 2 of the License, or
??? (at your option) any later version.

??? This program is distributed in the hope that it will be useful,
??? but WITHOUT ANY WARRANTY; without even the implied warranty of
??? MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.? See the
??? GNU General Public License for more details.

??? You should have received a copy of the GNU General Public License
??? along with this program; if not, write to the Free Software
??? Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

release(void)
{
??? /* 1 */
??? if (thread_count<=1)
??????? return 0;

??? /* 2 */
??? current = current->next;
??? switch_context(current->prev, current);
??? return 1;
}


static switch_context(struct context *from, struct context *to)
{
??? /* 3 */
??? __asm__
??? (
??????? "movl 8(%ebp),%eax\n\t"
??????? "movl %ebp,(%eax)\n\t"
??????? "movl 12(%ebp),%eax\n\t"
??????? "movl (%eax),%ebp\n\t"
??? );
}

[目錄]


redezvous

/***********************************************************************
??? Case study source code from the book `The Linux A to Z'
??? by Phil Cornes. Published by Prentice Hall, 1996.
??? Copyright (C) 1996 Phil Cornes

??? This program is free software; you can redistribute it and/or modify
??? it under the terms of the GNU General Public License as published by
??? the Free Software Foundation; either version 2 of the License, or
??? (at your option) any later version.

??? This program is distributed in the hope that it will be useful,
??? but WITHOUT ANY WARRANTY; without even the implied warranty of
??? MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.? See the
??? GNU General Public License for more details.

??? You should have received a copy of the GNU General Public License
??? along with this program; if not, write to the Free Software
??? Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

send(int chan, char *addr, int size)
{
??? /* 1 */
??? return(rendezvous((struct channel *)chan, addr, size, 1));
}


receive(int chan, char *addr, int size)
{
??? /* 2 */
??? return(rendezvous((struct channel *)chan, addr, size, 2));
}


static int rendezvous(struct channel *chan, char *addr,
int size, int sr_flag)
{
??? struct message *ptr;
??? int nbytes;

??? /* 3 */
??? if (sr_flag==3-chan->sr_flag)
??? {
??????? /* 4 */
??????? ptr = chan->message_list;
??????? chan->message_list = ptr->link;
??????? ptr->thread->next = current->next;
??????? ptr->thread->prev = current;
??????? current->next->prev = ptr->thread;
??????? current->next = ptr->thread;
??????? ++thread_count;
??????? /* 5 */
??????? nbytes = (size<ptr->size)?size:ptr->size;
??????? ptr->size = nbytes;

??????? /* 6 */
??????? if (sr_flag==1)
??????????? memcpy(ptr->addr, addr, nbytes);
??????? else
??????????? memcpy(addr, ptr->addr, nbytes);

??????? /* 7 */
??????? if (!chan->message_list)
??????????? chan->sr_flag = 0;

??????? return(nbytes);
??? }
??? else
??? {
??????? /* 8 */
??????? ptr = (struct message *)malloc(sizeof(struct message));

??????? if (!chan->message_list)
??????????? chan->message_list = ptr;
??????? else
??????????? chan->message_tail->link = ptr;

??????? chan->message_tail = ptr;
??????? ptr->link = 0;
??????? ptr->size = size;
??????? ptr->addr = addr;
??????? chan->sr_flag = sr_flag;
??????? ptr->thread = current;
??????? current->prev->next = current->next;
??????? current->next->prev = current->prev;

??????? /* 9 */
??????? if (--thread_count)
??????? {
??????????? current = current->next;
??????????? switch_context(ptr->thread, current);
??????? }
??????? else
??????????? switch_context(ptr->thread, &main_thread);

??????? /* 10 */
??????? nbytes = ptr->size;
??????? free(ptr);
??????? return(nbytes);
??? }
}

[目錄]


unbouded

/***********************************************************************
??? Case study source code from the book `The Linux A to Z'
??? by Phil Cornes. Published by Prentice Hall, 1996.
??? Copyright (C) 1996 Phil Cornes

??? This program is free software; you can redistribute it and/or modify
??? it under the terms of the GNU General Public License as published by
??? the Free Software Foundation; either version 2 of the License, or
??? (at your option) any later version.

??? This program is distributed in the hope that it will be useful,
??? but WITHOUT ANY WARRANTY; without even the implied warranty of
??? MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.? See the
??? GNU General Public License for more details.

??? You should have received a copy of the GNU General Public License
??? along with this program; if not, write to the Free Software
??? Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

int buffer(void);
int start(void);

int ch_desc1, ch_desc2;

main(void)
{
??? ch_desc1 = get_channel(1);
??? ch_desc2 = get_channel(2);
??? new_thread(start, 1024);
}

start(void)
{
??? int i, n;

??? new_thread(buffer, 1024);

??? for (i = 0; i<10, ++i)
??? {
??????? send(ch_desc1, &i, sizeof(int));
??????? release();
??? }

??? for (i = 0; i<10, ++i)
??? {
??????? receive(ch_desc2, &n, sizeof(int));
??????? printf("i=%d n=%d\n", i, n);
??????? release();
??? }
}

buffer(void)
{
??? int i;

??? receive(ch_desc1, &i, sizeof(int));
??? new_thread(buffer, 1024);
??? send(ch_desc2, &i, sizeof(int));
}

[目錄]


[ 本文件由良友·收藏家自動(dòng)生成 ]
posted on 2007-03-27 16:28 true 閱讀(612) 評(píng)論(0)  編輯 收藏 引用 所屬分類: linux
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久国产加勒比精品无码| 午夜精品视频| 国产午夜久久| 欧美精品色综合| 久久成人精品| 亚洲午夜激情| 亚洲精品欧美| 黑人巨大精品欧美一区二区小视频 | 亚洲一区二区精品| 亚洲激情第一页| 国产原创一区二区| 国产精品扒开腿做爽爽爽软件 | 在线视频精品一区| 亚洲欧洲一区二区三区| 精品动漫3d一区二区三区免费版 | 久久久久国产一区二区三区| 亚洲一区在线观看视频 | 一区二区三区四区五区视频| 久久亚洲私人国产精品va媚药| 亚洲欧美久久久久一区二区三区| 亚洲免费精彩视频| 亚洲精品日本| 亚洲精品亚洲人成人网| 亚洲午夜极品| 久久精品夜色噜噜亚洲aⅴ | 一区二区三区精密机械公司| 中文国产一区| 亚洲欧美三级在线| 久久免费偷拍视频| 久久久久国产一区二区| 欧美国产成人精品| 欧美成人资源网| 99re热精品| 一区二区激情| 欧美综合国产| 久久久久久亚洲精品杨幂换脸 | 一区二区三区欧美亚洲| 欧美一区二区三区成人| 欧美一二三视频| 欧美 日韩 国产精品免费观看| 免费欧美电影| 国产精品免费看片| 国产麻豆精品视频| 国产亚洲欧美日韩美女| 国语自产精品视频在线看| 亚洲免费电影在线| 久久精品视频在线看| 麻豆成人在线| 91久久精品视频| 一区二区三区欧美在线观看| 久久久99久久精品女同性| 老司机精品视频一区二区三区| 欧美成人高清视频| 欧美三级网页| 国产日韩欧美日韩大片| 99国产精品视频免费观看| 亚洲视频一区二区在线观看 | 欧美成人精品福利| 亚洲黄一区二区| 久久精品99久久香蕉国产色戒| 欧美日韩成人精品| 国产毛片一区二区| 一本久久a久久免费精品不卡| 亚洲欧美激情一区| 亚洲国产精品一区二区第四页av| 99国产精品久久| 欧美一级视频免费在线观看| 欧美日韩国产黄| 亚洲国产1区| 亚洲一区二区综合| 久久综合精品国产一区二区三区| 中文av字幕一区| 欧美久久久久久久| 国产婷婷精品| 亚洲女性喷水在线观看一区| 亚洲国产专区校园欧美| 久久综合色影院| 激情综合网激情| 久久久久国色av免费观看性色| 亚洲一级免费视频| 欧美三级电影一区| 一本在线高清不卡dvd | 亚洲欧洲一区二区在线播放| 久久久久久久久久久成人| 亚洲一区二区视频在线| 欧美性猛交视频| 中文国产成人精品久久一| 亚洲黄色三级| 欧美aaa级| 亚洲经典三级| 亚洲第一精品夜夜躁人人爽 | 亚洲福利在线观看| 老司机精品导航| 亚洲成人资源| 欧美一级午夜免费电影| 亚洲高清中文字幕| 欧美 亚欧 日韩视频在线| 亚洲国产精品一区二区久| 美女免费视频一区| 午夜精品一区二区三区在线| 欧美激情乱人伦| 国产午夜精品理论片a级探花| 欧美一级久久| 欧美在线播放| 影音先锋久久精品| 欧美在线你懂的| 久久爱www| 一区二区三区在线视频免费观看| 久久天天躁狠狠躁夜夜av| 久久久久.com| 亚洲黄色一区| 亚洲精品一区二区三区蜜桃久| 欧美精品一区在线播放| 亚洲色图制服丝袜| 亚洲影院在线| 韩国成人福利片在线播放| 欧美~级网站不卡| 欧美激情1区2区| 亚洲在线一区二区| 欧美在线免费播放| 亚洲高清不卡在线| 日韩一级大片| 国产麻豆精品久久一二三| 老妇喷水一区二区三区| 欧美成人乱码一区二区三区| 一区二区三区国产在线| 亚洲——在线| 亚洲国产老妈| 一本色道久久综合亚洲精品婷婷| 国产欧美亚洲日本| 久久国产精品久久久久久| 久久精品国产第一区二区三区| 最新69国产成人精品视频免费| 99爱精品视频| 激情欧美日韩| 亚洲九九爱视频| 国产一区二区精品| 亚洲黄色有码视频| 国产精品视频一区二区高潮| 午夜视频一区在线观看| 久久久久综合一区二区三区| 99这里有精品| 久久精品国产精品亚洲综合| 日韩图片一区| 欧美中文字幕第一页| 日韩亚洲一区二区| 性欧美暴力猛交69hd| 亚洲免费福利视频| 欧美在线播放视频| 亚洲午夜在线视频| 久久亚洲高清| 亚洲精品精选| 午夜伦欧美伦电影理论片| 91久久国产综合久久| 亚洲欧美日本另类| 激情亚洲网站| 亚洲视频在线观看视频| 亚洲福利小视频| 午夜精品视频| 中文在线不卡| 美日韩精品免费| 亚洲香蕉视频| 欧美成人免费观看| 久久精品视频免费播放| 欧美视频一区二区三区四区| 蜜臀av在线播放一区二区三区| 国产精品久久久久秋霞鲁丝| 欧美激情视频网站| 欧美日韩一区二| 久久久国产91| 国产精品精品视频| 久久综合电影| 国产日韩精品电影| 一本色道久久综合精品竹菊| 91久久久亚洲精品| 久久国产精品99精品国产| 午夜精品久久| 欧美先锋影音| 亚洲欧洲精品一区二区三区不卡| 在线精品在线| 久久久91精品国产| 久久精品国产精品| 国产麻豆成人精品| 亚洲一区二区欧美| 亚洲无限av看| 欧美日韩免费观看一区| 亚洲国产乱码最新视频| 亚洲国产毛片完整版| 久久久久久噜噜噜久久久精品| 久久久www成人免费精品| 国产精品二区三区四区| 夜夜精品视频一区二区| 99精品欧美一区| 欧美大片国产精品| 亚洲激情不卡| 日韩视频在线一区二区| 欧美韩国在线| 亚洲精品永久免费| 亚洲视频一区在线| 国产精品成人播放|