?
目 錄
- 進程和線程編程
- 原始管道
- pipe()
- dup()
- dup2()
- popen()和pclose()
- 命名管道
- 創(chuàng)建FIFO
- 操作FIFO
- 阻塞FIFO
- 消息隊列
- msgget()
- msgsnd()
- msgrcv()
- msgctl()
- 信號量
- semget()
- semop()
- semctl()
- 共享內(nèi)存
- shmget()
- shmat()
- shmctl()
- shmdt()
- 線程
- 線程同步
- 使用信號量協(xié)調(diào)程序
- 代碼例子
- newthread
- exitthead
- getchannel
- def
- release
- redezvous
- unbouded
進程和線程編程??? 看一下UNIX系統(tǒng)中的進程和Mach的任務(wù)和線程之間的關(guān)系。在UNIX系統(tǒng)中,一個進程包括一個可執(zhí)行的程序和一系列的資源,例如文件描述符表和地址空間。在Mach中,一個任務(wù)僅包括一系列的資源;線程處理所有的可執(zhí)行代碼。一個Mach的任務(wù)可以有任意數(shù)目的線程和它相關(guān),同時每個線程必須和某個任務(wù)相關(guān)。和某一個給定的任務(wù)相關(guān)的所有線程都共享任務(wù)的資源。這樣,一個線程就是一個程序計數(shù)器、一個堆棧和一系列的寄存器。所有需要使用的數(shù)據(jù)結(jié)構(gòu)都屬于任務(wù)。一個UNIX系統(tǒng)中的進程在Mach中對應(yīng)于一個任務(wù)和一個單獨的線程。
[目錄]
原始管道??? 使用C語言創(chuàng)建管道要比在shell下使用管道復(fù)雜一些。如果要使用C語言創(chuàng)建一個簡單的管道,可以使用系統(tǒng)調(diào)用pipe()。它接受一個參數(shù),也就是一個包括兩個整數(shù)的數(shù)組。如果系統(tǒng)調(diào)用成功,此數(shù)組將包括管道使用的兩個文件描述符。創(chuàng)建一個管道之后,一般情況下進程將產(chǎn)生一個新的進程。
??? 可以通過打開兩個管道來創(chuàng)建一個雙向的管道。但需要在子進程中正確地設(shè)置文件描述必須在系統(tǒng)調(diào)用fork()中調(diào)用pipe(),否則子進程將不會繼承文件描述符。當(dāng)使用半雙工管道時,任何關(guān)聯(lián)的進程都必須共享一個相關(guān)的祖先進程。因為管道存在于系統(tǒng)內(nèi)核之中,所以任何不在創(chuàng)建管道的進程的祖先進程之中的進程都將無法尋址它。而在命名管道中卻不是這樣。
[目錄]
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)建一個新的子進程:
#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);
}..
}
??? 如果父進程希望從子進程中讀取數(shù)據(jù),那么它應(yīng)該關(guān)閉fd1,同時子進程關(guān)閉fd0。反之,如果父進程希望向子進程中發(fā)送數(shù)據(jù),那么它應(yīng)該關(guān)閉fd0,同時子進程關(guān)閉fd1。因為文件描述符是在父進程和子進程之間共享,所以我們要及時地關(guān)閉不需要的管道的那一端。單從技術(shù)的角度來說,如果管道的一端沒有正確地關(guān)閉的話,你將無法得到一個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);
}
??? 一般情況下,子進程中的文件描述符將會復(fù)制到標準的輸入和輸出中。這樣子進程可以使用exec()執(zhí)行另一個程序,此程序繼承了標準的數(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(進程的文件描述符太多)
??? 注意舊文件描述符oldfd沒有關(guān)閉。雖然舊文件描述符和新創(chuàng)建的文件描述符可以交換使用,但一般情況下需要首先關(guān)閉一個。系統(tǒng)調(diào)用dup()使用的是號碼最小的空閑的文件描述符。
再看下面的程序:
..
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);
.
}
??? 因為文件描述符0(stdin)被關(guān)閉,所以dup()把管道的輸入描述符復(fù)制到它的標準輸入中。這樣我們可以調(diào)用execlp(),使用sort程序覆蓋子進程的正文段。因為新創(chuàng)建的程序從它的父進程中繼承了標準輸入/輸出流,所以它實際上繼承了管道的輸入端作為它的標準輸入端。現(xiàn)在,最初的父進程送往管道的任何數(shù)據(jù)都將會直接送往sort函數(shù)。
[目錄]
dup2()系統(tǒng)調(diào)用:dup2();
原型:intdup2(intoldfd,intnewfd);
返回值:如果調(diào)用成功,返回新的文件描述符
如果調(diào)用失敗,返回-1:errno=EBADF(oldfd不是有效的文件描述符)
EBADF(newfd超出范圍)
EMFILE(進程的文件描述符太多)
注意dup2()將關(guān)閉舊文件描述符。
??? 使用此系統(tǒng)調(diào)用,可以將close操作和文件描述符復(fù)制操作集成到一個系統(tǒng)調(diào)用中。另外,此系統(tǒng)調(diào)用保證了操作的自動進行,也就是說操作不能被其他的信號中斷。這個操作將會在返回系統(tǒng)內(nèi)核之前完成。如果使用前一個系統(tǒng)調(diào)用dup(),程序員不得不在此之前執(zhí)行一個close()操作。請看下面的程序:
..
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()如果你認為上面創(chuàng)建和使用管道的方法過于繁瑣的話,你也可以使用下面的簡單的方法:
庫函數(shù):popen()和pclose();
原型:FILE*popen(char*command,char*type);
返回值:如果成功,返回一個新的文件流。
如果無法創(chuàng)建進程或者管道,返回NULL。
??? 此標準的庫函數(shù)通過在系統(tǒng)內(nèi)部調(diào)用pipe()來創(chuàng)建一個半雙工的管道,然后它創(chuàng)建一個子進程,啟動shell,最后在shell上執(zhí)行command參數(shù)中的命令。管道中數(shù)據(jù)流的方向是由第二個參數(shù)type控制的。此參數(shù)可以是r或者w,分別代表讀或?qū)憽5荒芡瑫r為讀和寫。在Linux系統(tǒng)下,管道將會以參數(shù)type中第一個字符代表的方式打開。所以,如果你在參數(shù)type中寫入rw,管道將會以讀的方式打開。
??? 雖然此庫函數(shù)的用法很簡單,但也有一些不利的地方。例如它失去了使用系統(tǒng)調(diào)用pipe()時可以有的對系統(tǒng)的控制。盡管這樣,因為可以直接地使用shell命令,所以shell中的一些通配符和其他的一些擴展符號都可以在command參數(shù)中使用。
使用popen()創(chuàng)建的管道必須使用pclose()關(guān)閉。其實,popen/pclose和標準文件輸入/輸出流中的fopen()/fclose()十分相似。
庫函數(shù):pclose();
原型:intpclose(FILE*stream);
返回值:返回系統(tǒng)調(diào)用wait4()的狀態(tài)。
如果stream無效,或者系統(tǒng)調(diào)用wait4()失敗,則返回-1。
??? 注意此庫函數(shù)等待管道進程運行結(jié)束,然后關(guān)閉文件流。庫函數(shù)pclose()在使用popen()創(chuàng)建的進程上執(zhí)行wait4()函數(shù)。當(dāng)它返回時,它將破壞管道和文件系統(tǒng)。
??? 在下面的例子中,用sort命令打開了一個管道,然后對一個字符數(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);
}
因為popen()使用shell執(zhí)行命令,所以所有的shell擴展符和通配符都可以使用。此外,它還可以和popen()一起使用重定向和輸出管道函數(shù)。再看下面的例子:
popen("ls~scottb","r");
popen("sort>/tmp/foo","w");
popen("sort|uniq|more","w");
下面的程序是另一個使用popen()的例子,它打開兩個管道(一個用于ls命令,另一個用于
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);
}
最后,我們再看一個使用popen()的例子。此程序用于創(chuàng)建一個命令和文件之間的管道:
#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)中作為一個特殊的設(shè)備文件而存在的。
*不同祖先的進程之間可以通過管道共享數(shù)據(jù)。
*當(dāng)共享管道的進程執(zhí)行完所有的I/O操作以后,命名管道將繼續(xù)保存在文件系統(tǒng)中以便以后使用。
??? 一個管道必須既有讀取進程,也要有寫入進程。如果一個進程試圖寫入到一個沒有讀取進程的管道中,那么系統(tǒng)內(nèi)核將會產(chǎn)生SIGPIPE信號。當(dāng)兩個以上的進程同時使用管道時,這一點尤其重要。
[目錄]
創(chuàng)建FIFO??? 可以有幾種方法創(chuàng)建一個命名管道。頭兩種方法可以使用shell。
mknodMYFIFOp
mkfifoa=rwMYFIFO
??? 上面的兩個命名執(zhí)行同樣的操作,但其中有一點不同。命令mkfifo提供一個在創(chuàng)建之后直接改變FIFO文件存取權(quán)限的途徑,而命令mknod需要調(diào)用命令chmod。
??? 一個物理文件系統(tǒng)可以通過p指示器十分容易地分辨出一個FIFO文件。
$ls-lMYFIFO
prw-r--r--1rootroot0Dec1422:15MYFIFO|
??? 請注意在文件名后面的管道符號“|”。
??? 我們可以使用系統(tǒng)調(diào)用mknod()來創(chuàng)建一個FIFO管道:
庫函數(shù):mknod();
原型:intmknod(char*pathname,mode_tmode,dev_tdev);
返回值:如果成功,返回0
如果失敗,返回-1:errno=EFAULT(無效路徑名)
EACCES(無存取權(quán)限)
ENAMETOOLONG(路徑名太長)
ENOENT(無效路徑名)
ENOTDIR(無效路徑名)
??? 下面看一個使用C語言創(chuàng)建FIFO管道的例子:
mknod("/tmp/MYFIFO",S_IFIFO|0666,0);
??? 在這個例子中,文件/tmp/MYFIFO是要創(chuàng)建的FIFO文件。它的存取權(quán)限是0666。存取權(quán)限
也可以使用umask修改:
final_umask=requested_permissions&~original_umask
??? 一個常用的使用系統(tǒng)調(diào)用umask()的方法就是臨時地清除umask的值:
umask(0);
mknod("/tmp/MYFIFO",S_IFIFO|0666,0);
??? 另外,mknod()中的第三個參數(shù)只有在創(chuàng)建一個設(shè)備文件時才能用到。它包括設(shè)備文件的
主設(shè)備號和從設(shè)備號。
}
}
[目錄]
操作FIFO??? FIFO上的I/O操作和正常管道上的I/O操作基本一樣,只有一個主要的不同。系統(tǒng)調(diào)用open用來在物理上打開一個管道。在半雙工的管道中,這是不必要的。因為管道在系統(tǒng)內(nèi)核中,而不是在一個物理的文件系統(tǒng)中。在我們的例子中,我們將像使用一個文件流一樣使用管道,也就是使用fopen()打開管道,使用fclose()關(guān)閉它。
??? 請看下面的簡單的服務(wù)程序進程:
#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);
??? 因為FIFO管道缺省時有阻塞的函數(shù),所以你可以在后臺運行此程序:
$fifoserver&
??? 再來看一下下面的簡單的客戶端程序:
#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管道上將會有阻塞的情況發(fā)生。也就是說,如果一個FIFO管道打開供讀取的話,它將一直阻塞,直到其他的進程打開管道寫入信息。這種過程反過來也一樣。如果你不需要阻塞函數(shù)的話,你可以在系統(tǒng)調(diào)用open()中設(shè)置O_NONBLOCK標志,這樣可以取消缺省的阻塞函數(shù)。
[目錄]
消息隊列??? 在UNIX的SystemV版本,AT&T引進了三種新形式的IPC功能(消息隊列、信號量、以及共享內(nèi)存)。但BSD版本的UNIX使用套接口作為主要的IPC形式。Linux系統(tǒng)同時支持這兩個版本。
[目錄]
msgget()系統(tǒng)調(diào)用msgget()
??? 如果希望創(chuàng)建一個新的消息隊列,或者希望存取一個已經(jīng)存在的消息隊列,你可以使用系統(tǒng)調(diào)用msgget()。
系統(tǒng)調(diào)用:msgget();
原型:intmsgget(key_t key,int msgflg);
返回值:如果成功,返回消息隊列標識符
如果失敗,則返回-1:errno=EACCESS(權(quán)限不允許)
EEXIST(隊列已經(jīng)存在,無法創(chuàng)建)
EIDRM(隊列標志為刪除)
ENOENT(隊列不存在)
ENOMEM(創(chuàng)建隊列時內(nèi)存不夠)
ENOSPC(超出最大隊列限制)
??? 系統(tǒng)調(diào)用msgget()中的第一個參數(shù)是關(guān)鍵字值(通常是由ftok()返回的)。然后此關(guān)鍵字值將會和其他已經(jīng)存在于系統(tǒng)內(nèi)核中的關(guān)鍵字值比較。這時,打開和存取操作是和參數(shù)msgflg中的內(nèi)容相關(guān)的。
IPC_CREAT如果內(nèi)核中沒有此隊列,則創(chuàng)建它。
IPC_EXCL當(dāng)和IPC_CREAT一起使用時,如果隊列已經(jīng)存在,則失敗。
??? 如果單獨使用IPC_CREAT,則msgget()要么返回一個新創(chuàng)建的消息隊列的標識符,要么返回具有相同關(guān)鍵字值的隊列的標識符。如果IPC_EXCL和IPC_CREAT一起使用,則msgget()要么創(chuàng)建一個新的消息隊列,要么如果隊列已經(jīng)存在則返回一個失敗值-1。IPC_EXCL單獨使用是沒有用處的。
下面看一個打開和創(chuàng)建一個消息隊列的例子:
intopen_queue(key_t keyval)
{
intqid;
if((qid=msgget(keyval,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(qid);
}
[目錄]
msgsnd()系統(tǒng)調(diào)用msgsnd()
??? 一旦我們得到了隊列標識符,我們就可以在隊列上執(zhí)行我們希望的操作了。如果想要往隊列中發(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(隊列已滿,并且使用了IPC_NOWAIT)
EACCES(沒有寫的權(quán)限)
EFAULT(msgp地址無效)
EIDRM(消息隊列已經(jīng)刪除)
EINTR(當(dāng)?shù)却龑懖僮鲿r,收到一個信號)
EINVAL(無效的消息隊列標識符,非正數(shù)的消息類型,或
者無效的消息長度)
ENOMEM(沒有足夠的內(nèi)存復(fù)制消息緩沖區(qū))
??? 系統(tǒng)調(diào)用msgsnd()的第一個參數(shù)是消息隊列標識符,它是由系統(tǒng)調(diào)用msgget返回的。第二個參數(shù)是msgp,是指向消息緩沖區(qū)的指針。參數(shù)msgsz中包含的是消息的字節(jié)大小,但不包括消息類型的長度(4個字節(jié))。
??? 參數(shù)msgflg可以設(shè)置為0(此時為忽略此參數(shù)),或者使用IPC_NOWAIT。
??? 如果消息隊列已滿,那么此消息則不會寫入到消息隊列中,控制將返回到調(diào)用進程中。如果沒有指明,調(diào)用進程將會掛起,直到消息可以寫入到隊列中。
??? 下面是一個發(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);
}
??? 這個小程序試圖將存儲在緩沖區(qū)qbuf中的消息發(fā)送到消息隊列qid中。下面的程序是結(jié)合了上面兩個程序的一個完整程序:
#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)建和打開消息隊列以后,我們將測試數(shù)據(jù)裝入到消息緩沖區(qū)中。最后調(diào)用send_messag把消息發(fā)送到消息隊列中。現(xiàn)在在消息隊列中有了一條消息,我們可以使用ipcs命令來查看隊列的狀態(tài)。下面討論如何從隊列中獲取消息。可以使用系統(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(消息的長度大于msgsz,沒有MSG_NOERROR)
EACCES(沒有讀的權(quán)限)
EFAULT(msgp指向的地址是無效的)
EIDRM(隊列已經(jīng)被刪除)
EINTR(被信號中斷)
EINVAL(msgqid無效,或者msgsz小于0)
ENOMSG(使用IPC_NOWAIT,同時隊列中的消息無法滿足要求)
??? 很明顯,第一個參數(shù)用來指定將要讀取消息的隊列。第二個參數(shù)代表要存儲消息的消息緩沖區(qū)的地址。第三個參數(shù)是消息緩沖區(qū)的長度,不包括mtype的長度,它可以按照如下的方法計算:
??????? msgsz=sizeof(structmymsgbuf)-sizeof(long);
??? 第四個參數(shù)是要從消息隊列中讀取的消息的類型。如果此參數(shù)的值為0,那么隊列中最長時間的一條消息將返回,而不論其類型是什么。
如果調(diào)用中使用了IPC_NOWAIT作為標志,那么當(dāng)沒有數(shù)據(jù)可以使用時,調(diào)用將把ENOMSG返回到調(diào)用進程中。否則,調(diào)用進程將會掛起,直到隊列中的一條消息滿足msgrcv()的參數(shù)要求。如果當(dāng)客戶端等待一條消息的時候隊列為空,將會返回EIDRM。如果進程在等待消息的過程中捕捉到一個信號,則返回EINTR。
??? 下面就是一個從隊列中讀取消息的程序:
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);
}
??? 在成功地讀取了一條消息以后,隊列中的這條消息的入口將被刪除。
??? 參數(shù)msgflg中的MSG_NOERROR位提供一種額外的用途。如果消息的實際長度大于msgsz,同時使用了MSG_NOERROR,那么消息將會被截斷,只有與msgsz長度相等的消息返回。一般情況下,系統(tǒng)調(diào)用msgrcv()會返回-1,而這條消息將會繼續(xù)保存在隊列中。我們可以利用這個特點編制一個程序,利用這個程序可以查看消息隊列的情況,看看符合我們條件的消息是否已經(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ū)的地址和長度。這樣,系統(tǒng)調(diào)用將會失敗。盡管如此,我們可以檢查返回的E2BIG值,它說明符合條件的消息確實存在。
[目錄]
msgctl()系統(tǒng)調(diào)用msgctl()
??? 下面我們繼續(xù)討論如何使用一個給定的消息隊列的內(nèi)部數(shù)據(jù)結(jié)構(gòu)。我們可以使用系統(tǒng)調(diào)用msgctl ( )來控制對消息隊列的操作。
系統(tǒng)調(diào)用: msgctl( ) ;
調(diào)用原型: int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
返回值: 0 ,如果成功。
- 1,如果失敗:errno = EACCES (沒有讀的權(quán)限同時cmd 是IPC_STAT )
EFAULT (buf 指向的地址無效)
EIDRM (在讀取中隊列被刪除)
EINVAL (msgqid無效, 或者msgsz 小于0 )
EPERM (IPC_SET或者IPC_RMID 命令被使用,但調(diào)用程序沒有寫的權(quán)限)
下面我們看一下可以使用的幾個命令:
IPC_STAT
讀取消息隊列的數(shù)據(jù)結(jié)構(gòu)msqid_ds,并將其存儲在b u f指定的地址中。
IPC_SET
設(shè)置消息隊列的數(shù)據(jù)結(jié)構(gòu)msqid_ds中的ipc_perm元素的值。這個值取自buf參數(shù)。
IPC_RMID
從系統(tǒng)內(nèi)核中移走消息隊列。
??? 我們在前面討論過了消息隊列的數(shù)據(jù)結(jié)構(gòu)(msqid_ds)。系統(tǒng)內(nèi)核中為系統(tǒng)中的每一個消息隊列保存一個此數(shù)據(jù)結(jié)構(gòu)的實例。通過使用IPC_STAT命令,我們可以得到一個此數(shù)據(jù)結(jié)構(gòu)的副本。下面的程序就是實現(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)用進程將返回-1。如果調(diào)用成功,則返回0。緩沖區(qū)中應(yīng)該包括消息隊列中的數(shù)據(jù)結(jié)構(gòu)。
??? 消息隊列中的數(shù)據(jù)結(jié)構(gòu)中唯一可以改動的元素就是ipc_perm。它包括隊列的存取權(quán)限和關(guān)于隊列創(chuàng)建者和擁有者的信息。你可以改變用戶的id、用戶的組id以及消息隊列的存取權(quán)限。
??? 下面是一個修改隊列存取模式的程序:
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來讀取隊列的內(nèi)部數(shù)據(jù)結(jié)構(gòu)。然后,我們調(diào)用sscanf( )修改數(shù)據(jù)結(jié)構(gòu)msg_perm中的mode 成員的值。但直到調(diào)用msgctl()時,權(quán)限的改變才真正完成。在這里msgctl()使用的是IPC_SET命令。
??? 最后,我們使用系統(tǒng)調(diào)用msgctl ( )中的IPC_RMID命令刪除消息隊列:
int remove_queue(int qid )
{
if( msgctl( qid, IPC_RMID, 0) == -1)
{
return(-1);
}
return(0);
}
};
[目錄]
信號量??? 信號量是一個可以用來控制多個進程存取共享資源的計數(shù)器。它經(jīng)常作為一種鎖定機制來防止當(dāng)一個進程正在存取共享資源時,另一個進程也存取同一資源。下面先簡要地介紹一下信號量中涉及到的數(shù)據(jù)結(jié)構(gòu)。
1.內(nèi)核中的數(shù)據(jù)結(jié)構(gòu)semid_ds
和消息隊列一樣,系統(tǒng)內(nèi)核為內(nèi)核地址空間中的每一個信號量集都保存了一個內(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的一個實例。它保存有信號量集的存取權(quán)限的信息,以及信號量集創(chuàng)建者的有關(guān)信息。
sem_otime最后一次semop()操作的時間。
sem_ctime最后一次改動此數(shù)據(jù)結(jié)構(gòu)的時間。
sem_base指向數(shù)組中第一個信號量的指針。
sem_undo數(shù)組中沒有完成的請求的個數(shù)。
sem_nsems信號量集(數(shù)組)中的信號量的個數(shù)。
2.內(nèi)核中的數(shù)據(jù)結(jié)構(gòu)sem
在數(shù)據(jù)結(jié)構(gòu)semid_ds中包含一個指向信號量數(shù)組的指針。此數(shù)組中的每一個元素都是一個
數(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最后一個操作的PID(進程ID)。
sem_semval信號量的當(dāng)前值。
sem_semncnt等待資源的進程數(shù)目。
sem_semzcnt等待資源完全空閑的進程數(shù)目。
[目錄]
semget()??? 我們可以使用系統(tǒng)調(diào)用semget()創(chuàng)建一個新的信號量集,或者存取一個已經(jīng)存在的信號量集:
系統(tǒng)調(diào)用:semget();
原型:intsemget(key_t key,int nsems,int semflg);
返回值:如果成功,則返回信號量集的IPC標識符。如果失敗,則返回-1:errno=EACCESS(沒有權(quán)限)
EEXIST(信號量集已經(jīng)存在,無法創(chuàng)建)
EIDRM(信號量集已經(jīng)刪除)
ENOENT(信號量集不存在,同時沒有使用IPC_CREAT)
ENOMEM(沒有足夠的內(nèi)存創(chuàng)建新的信號量集)
ENOSPC(超出限制)
??? 系統(tǒng)調(diào)用semget()的第一個參數(shù)是關(guān)鍵字值(一般是由系統(tǒng)調(diào)用ftok()返回的)。系統(tǒng)內(nèi)核將此值和系統(tǒng)中存在的其他的信號量集的關(guān)鍵字值進行比較。打開和存取操作與參數(shù)semflg中的內(nèi)容相關(guān)。IPC_CREAT如果信號量集在系統(tǒng)內(nèi)核中不存在,則創(chuàng)建信號量集。IPC_EXCL當(dāng)和IPC_CREAT一同使用時,如果信號量集已經(jīng)存在,則調(diào)用失敗。如果單獨使用IPC_CREAT,則semget()要么返回新創(chuàng)建的信號量集的標識符,要么返回系統(tǒng)中已經(jīng)存在的同樣的關(guān)鍵字值的信號量的標識符。如果IPC_EXCL和IPC_CREAT一同使用,則要么返回新創(chuàng)建的信號量集的標識符,要么返回-1。IPC_EXCL單獨使用沒有意義。參數(shù)nsems指出了一個新的信號量集中應(yīng)該創(chuàng)建的信號量的個數(shù)。信號量集中最多的信號量的個數(shù)是在linux/sem.h中定義的:
#defineSEMMSL32/*<=512maxnumofsemaphoresperid*/
下面是一個打開和創(chuàng)建信號量集的程序:
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ù)進行)
EFAULT(sops指向的地址無效)
EIDRM(信號量集已經(jīng)刪除)
EINTR(當(dāng)睡眠時接收到其他信號)
EINVAL(信號量集不存在,或者semid無效)
ENOMEM(使用了SEM_UNDO,但無足夠的內(nèi)存創(chuàng)建所需的數(shù)據(jù)結(jié)構(gòu))
ERANGE(信號量值超出范圍)
??? 第一個參數(shù)是關(guān)鍵字值。第二個參數(shù)是指向?qū)⒁僮鞯臄?shù)組的指針。第三個參數(shù)是數(shù)組中的操作的個數(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將要處理的信號量的個數(shù)。
sem_op要執(zhí)行的操作。
sem_flg操作標志。
??? 如果sem_op是負數(shù),那么信號量將減去它的值。這和信號量控制的資源有關(guān)。如果沒有使用IPC_NOWAIT,那么調(diào)用進程將進入睡眠狀態(tài),直到信號量控制的資源可以使用為止。如果sem_op是正數(shù),則信號量加上它的值。這也就是進程釋放信號量控制的資源。最后,如果sem_op是0,那么調(diào)用進程將調(diào)用sleep(),直到信號量的值為0。這在一個進程等待完全空閑的資源時使用。
[目錄]
semctl()系統(tǒng)調(diào)用:semctl();
原型:int semctl(int semid,int semnum,int cmd,union semunarg);
返回值:如果成功,則為一個正數(shù)。
如果失敗,則為-1:errno=EACCESS(權(quán)限不夠)
EFAULT(arg指向的地址無效)
EIDRM(信號量集已經(jīng)刪除)
EINVAL(信號量集不存在,或者semid無效)
EPERM(EUID沒有cmd的權(quán)利)
ERANGE(信號量值超出范圍)
??? 系統(tǒng)調(diào)用semctl用來執(zhí)行在信號量集上的控制操作。這和在消息隊列中的系統(tǒng)調(diào)用msgctl是十分相似的。但這兩個系統(tǒng)調(diào)用的參數(shù)略有不同。因為信號量一般是作為一個信號量集使用的,而不是一個單獨的信號量。所以在信號量集的操作中,不但要知道IPC關(guān)鍵字值,也要知道信號量集中的具體的信號量。這兩個系統(tǒng)調(diào)用都使用了參數(shù)cmd,它用來指出要操作的具體命令。兩個系統(tǒng)調(diào)用中的最后一個參數(shù)也不一樣。在系統(tǒng)調(diào)用msgctl中,最后一個參數(shù)是指向內(nèi)核中使用的數(shù)據(jù)結(jié)構(gòu)的指針。我們使用此數(shù)據(jù)結(jié)構(gòu)來取得有關(guān)消息隊列的一些信息,以及設(shè)置或者改變隊列的存取權(quán)限和使用者。但在信號量中支持額外的可選的命令,這樣就要求有一個更為復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。
系統(tǒng)調(diào)用semctl()的第一個參數(shù)是關(guān)鍵字值。第二個參數(shù)是信號量數(shù)目。
??? 參數(shù)cmd中可以使用的命令如下:
??? ·IPC_STAT讀取一個信號量集的數(shù)據(jù)結(jié)構(gòu)semid_ds,并將其存儲在semun中的buf參數(shù)中。
??? ·IPC_SET設(shè)置信號量集的數(shù)據(jù)結(jié)構(gòu)semid_ds中的元素ipc_perm,其值取自semun中的buf參數(shù)。
??? ·IPC_RMID將信號量集從內(nèi)存中刪除。
??? ·GETALL用于讀取信號量集中的所有信號量的值。
??? ·GETNCNT返回正在等待資源的進程數(shù)目。
??? ·GETPID返回最后一個執(zhí)行semop操作的進程的PID。
??? ·GETVAL返回信號量集中的一個單個的信號量的值。
??? ·GETZCNT返回這在等待完全空閑的資源的進程數(shù)目。
??? ·SETALL設(shè)置信號量集中的所有的信號量的值。
??? ·SETVAL設(shè)置信號量集中的一個單獨的信號量的值。
??? 參數(shù)arg代表一個semun的實例。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命令時使用。buf在IPC_STAT/IPC_SET命令中使用。代表了內(nèi)核中使用的信號量的數(shù)據(jù)結(jié)構(gòu)。array在使用GETALL/SETALL命令時使用的指針。
??? 下面的程序返回信號量的值。當(dāng)使用GETVAL命令時,調(diào)用中的最后一個參數(shù)被忽略:
intget_sem_val(intsid,intsemnum)
{
return(semctl(sid,semnum,GETVAL,0));
}
??? 下面是一個實際應(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));
}
??? 下面的程序可以用來初始化一個新的信號量值:
void init_semaphore(int sid,int semnum,int initval)
{
union semunsemopts;
semopts.val=initval;
semctl(sid,semnum,SETVAL,semopts);
}
??? 注意系統(tǒng)調(diào)用semctl中的最后一個參數(shù)是一個聯(lián)合類型的副本,而不是一個指向聯(lián)合類型的指針。
[目錄]
共享內(nèi)存??? 共享內(nèi)存就是由幾個進程共享一段內(nèi)存區(qū)域。這可以說是最快的IPC形式,因為它無須任何的中間操作(例如,管道、消息隊列等)。它只是把內(nèi)存段直接映射到調(diào)用進程的地址空間中。這樣的內(nèi)存段可以是由一個進程創(chuàng)建的,然后其他的進程可以讀寫此內(nèi)存段。
??? 每個系統(tǒng)的共享內(nèi)存段在系統(tǒng)內(nèi)核中也保持著一個內(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的一個實例。這里保存的是內(nèi)存段的存取權(quán)限,和其他的有關(guān)內(nèi)存段創(chuàng)建者的信息。
shm_segsz 內(nèi)存段的字節(jié)大小。
shm_atime 最后一個進程存取內(nèi)存段的時間。
shm_dtime 最后一個進程離開內(nèi)存段的時間。
shm_ctime 內(nèi)存段最后改動的時間。
shm_cpid 內(nèi)存段創(chuàng)建進程的P I D。
shm_lpid 最后一個使用內(nèi)存段的進程的P I D。
shm_nattch 當(dāng)前使用內(nèi)存段的進程總數(shù)。
[目錄]
shmget()系統(tǒng)調(diào)用:shmget();
原型:int shmget(key_t key,int size,int shmflg);
返回值:如果成功,返回共享內(nèi)存段標識符。如果失敗,則返回-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()中的第一個參數(shù)是關(guān)鍵字值(它是用系統(tǒng)調(diào)用ftok()返回的)。其他的操作都要依據(jù)shmflg中的命令進行。
??? ·IPC_CREAT如果系統(tǒng)內(nèi)核中沒有共享的內(nèi)存段,則創(chuàng)建一個共享的內(nèi)存段。
??? ·IPC_EXCL當(dāng)和IPC_CREAT一同使用時,如果共享內(nèi)存段已經(jīng)存在,則調(diào)用失敗。
??? 當(dāng)IPC_CREAT單獨使用時,系統(tǒng)調(diào)用shmget()要么返回一個新創(chuàng)建的共享內(nèi)存段的標識符,要么返回一個已經(jīng)存在的共享內(nèi)存段的關(guān)鍵字值。如果IPC_EXCL和IPC_CREAT一同使用,則要么系統(tǒng)調(diào)用新創(chuàng)建一個共享的內(nèi)存段,要么返回一個錯誤值-1。IPC_EXCL單獨使用沒有意義。
??? 下面是一個定位和創(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);
}
??? 一旦一個進程擁有了一個給定的內(nèi)存段的有效IPC標識符,它的下一步就是將共享的內(nèi)存段映射到自己的地址空間中。
[目錄]
shmat()系統(tǒng)調(diào)用: shmat();
原型:int shmat ( int shmid, char *shmaddr, int shmflg);
返回值:如果成功,則返回共享內(nèi)存段連接到進程中的地址。如果失敗,則返回- 1:errno = EINVAL (無效的IPC ID 值或者無效的地址)
ENOMEM (沒有足夠的內(nèi)存)
EACCES (存取權(quán)限不夠)
??? 如果參數(shù)a d d r的值為0,那么系統(tǒng)內(nèi)核則試圖找出一個沒有映射的內(nèi)存區(qū)域。我們推薦使用這種方法。你可以指定一個地址,但這通常是為了加快對硬件設(shè)備的存取,或者解決和其他程序的沖突。
??? 下面的程序中的調(diào)用參數(shù)是一個內(nèi)存段的I P C標識符,返回內(nèi)存段連接的地址:
char *attach_segment(int shmid)
{
return(shmat(shmid, 0, 0));
}
??? 一旦內(nèi)存段正確地連接到進程以后,進程中就有了一個指向該內(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)限,同時命令是IPC_STAT)
EFAULT(buf指向的地址無效,同時命令是IPC_SET和IPC_STAT )
EIDRM (內(nèi)存段被移走)
EINVAL (shmqid 無效)
EPERM (使用IPC_SET 或者IPC_RMID 命令,但調(diào)用進程沒有寫的權(quán)限)
IPC_STAT 讀取一個內(nèi)存段的數(shù)據(jù)結(jié)構(gòu)shmid_ds,并將它存儲在buf參數(shù)指向的地址中。
IPC_SET 設(shè)置內(nèi)存段的數(shù)據(jù)結(jié)構(gòu)shmid_ds中的元素ipc_perm的值。從參數(shù)buf中得到要設(shè)置的值。
IPC_RMID 標志內(nèi)存段為移走。
??? 命令I(lǐng)PC_RMID并不真正從系統(tǒng)內(nèi)核中移走共享的內(nèi)存段,而是把內(nèi)存段標記為可移除。進程調(diào)用系統(tǒng)調(diào)用shmdt()脫離一個共享的內(nèi)存段。
[目錄]
shmdt()系統(tǒng)調(diào)用:shmdt();
調(diào)用原型:int shmdt ( char *shmaddr );
返回值:如果失敗,則返回- 1:errno = EINVAL (無效的連接地址)
??? 當(dāng)一個進程不在需要共享的內(nèi)存段時,它將會把內(nèi)存段從其地址空間中脫離。但這不等于將共享內(nèi)存段從系統(tǒng)內(nèi)核中移走。當(dāng)進程脫離成功后,數(shù)據(jù)結(jié)構(gòu)shmid_ds中元素shm_nattch將減1。當(dāng)此數(shù)值減為0以后,系統(tǒng)內(nèi)核將物理上把內(nèi)存段從系統(tǒng)內(nèi)核中移走。
[目錄]
線程??? 線程通常叫做輕型的進程。雖然這個叫法有些簡單化,但這有利于了解線程的概念。因為線程和進程比起來很小,所以相對來說,線程花費更少的CPU資源。進程往往需要它們自己的資源,但線程之間可以共享資源,所以線程更加節(jié)省內(nèi)存。Mach的線程使得程序員可以編寫并發(fā)運行的程序,而這些程序既可以運行在單處理器的機器上,也可以運行在多處理器的機器中。另外,在單處理器環(huán)境中,當(dāng)應(yīng)用程序執(zhí)行容易引起阻塞和延遲的操作時,線程可以提高效率。
??? 用子函數(shù)pthread_create創(chuàng)建一個新的線程。它有四個參數(shù):一個用來保存線程的線程變量、一個線程屬性、當(dāng)線程執(zhí)行時要調(diào)用的函數(shù)和一個此函數(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)建的進程,它們和它們的父進程使用同一個執(zhí)行點,線程使用在pthread_create中的參數(shù)指明要開始執(zhí)行的函數(shù)。
??? 現(xiàn)在我們可以編制第一個程序了。我們編制一個多線程的應(yīng)用程序,在標準輸出中打印“Hello Wo r l d”。首先我們需要兩個線程變量,一個新線程開始執(zhí)行時可以調(diào)用的函數(shù)。我們還需要指明每一個線程應(yīng)該打印的信息。一個做法是把要打印的字符串分開,給每一個線程一個字符串作為開始的參數(shù)。請看下面的代碼:
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)建第一個線程,并將“Hello”作為它的啟動參數(shù)。第二個線程的參數(shù)是“World”。當(dāng)?shù)谝粋€線程開始執(zhí)行時,它使用參數(shù)“Hello”執(zhí)行函數(shù)print_message_function。它在標準輸出中打印“Hello”,然后結(jié)束對函數(shù)的調(diào)用。線程當(dāng)離開它的初始化函數(shù)時就將終止,所以第一個線程在打印完“Hello”后終止。當(dāng)?shù)诙€線程執(zhí)行時,它打印“World”然后終止。但這個程序有兩個主要的缺陷。
??? 首先也是最重要的是線程是同時執(zhí)行的。這樣就無法保證第一個線程先執(zhí)行打印語句。所以你很可能在屏幕上看到“World Hello”,而不是“Hello World”。請注意對exit的調(diào)用是父線程在主程序中使用的。這樣,如果父線程在兩個子線程調(diào)用打印語句之前調(diào)用exit,那么將不會有任何的打印輸出。這是因為exit函數(shù)將會退出進程,同時釋放任務(wù),所以結(jié)束了所有的線程。任何線程(不論是父線程或者子線程)調(diào)用exit 都會終止所有其他線程。如果希望線程分別終止,可以使用pthread_exit函數(shù)。
我們可以使用一個辦法彌補此缺陷。我們可以在父線程中插入一個延遲程序,給子線程足夠的時間完成打印的調(diào)用。同樣,在調(diào)用第二個之前也插入一個延遲程序保證第一個線程在第二個線程執(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) ;
}
??? 這樣是否達到了我們的要求了呢?不盡如此,因為依靠時間的延遲執(zhí)行同步是不可靠的。這里遇到的情形和一個分布程序和共享資源的情形一樣。共享的資源是標準的輸出設(shè)備,分布計算的程序是三個線程。
其實這里還有另外一個錯誤。函數(shù)sleep和函數(shù)e x i t一樣和進程有關(guān)。當(dāng)線程調(diào)用sleep時,整個的進程都處于睡眠狀態(tài),也就是說,所有的三個線程都進入睡眠狀態(tài)。這樣我們實際上沒有解決任何的問題。希望使一個線程睡眠的函數(shù)是pthread_delay_np。例如讓一個線程睡眠2秒鐘,用如下程序:
struct timespec delay;
delay.tv_sec = 2;
delay.tv_nsec = 0;
pthread_delay_np( &delay );
}
[目錄]
線程同步POSIX提供兩種線程同步的方法,mutex和條件變量。mutex是一種簡單的加鎖的方法來控制對共享資源的存取。我們可以創(chuàng)建一個讀/寫程序,它們共用一個共享緩沖區(qū),使用mutex來控制對緩沖區(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ū)只有兩個狀態(tài),有一條信息或者沒有信息。使用延遲是為了避免一個線程永遠占有mutex。
但mutex的缺點在于它只有兩個狀態(tài),鎖定和非鎖定。POSIX的條件變量通過允許線程阻塞和等待另一個線程的信號方法,從而彌補了mutex的不足。當(dāng)接受到一個信號時,阻塞線程將會被喚起,并試圖獲得相關(guān)的mutex的鎖。
[目錄]
使用信號量協(xié)調(diào)程序我們可以使用信號量重新看一下上面的讀/寫程序。涉及信號量的操作是semaphore_up、semaphore_down、semaphore_init、semaphore_destroy和semaphore_decrement。所有這些操作都只有一個參數(shù),一個指向信號量目標的指針。
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 );
}
}
這個例子也沒有完全地利用一般信號量的所有函數(shù)。我們可以使用信號量重新編寫“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 ) ;
}
信號量c h i l d _ c o u n t e r用來強迫父線程阻塞,直到兩個子線程執(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));
}
[目錄]
[ 本文件由良友·收藏家自動生成 ]