• <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>

            網(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. 信號量
                1. semget()
                2. semop()
                3. semctl()
              5. 共享內(nèi)存
                1. shmget()
                2. shmat()
                3. shmctl()
                4. shmdt()
              6. 線程
                1. 線程同步
                2. 使用信號量協(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中對應(yīng)于一個(gè)任務(wù)和一個(gè)單獨(dú)的線程。
            [目錄]


            原始管道

            ??? 使用C語言創(chuàng)建管道要比在shell下使用管道復(fù)雜一些。如果要使用C語言創(chuàng)建一個(gè)簡單的管道,可以使用系統(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()使用的是號碼最小的空閑的文件描述符。

            再看下面的程序:
            ..
            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)行,也就是說操作不能被其他的信號中斷。這個(gè)操作將會(huì)在返回系統(tǒng)內(nèi)核之前完成。如果使用前一個(gè)系統(tǒng)調(diào)用dup(),程序員不得不在此之前執(zhí)行一個(gè)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()

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

            庫函數(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ù)的用法很簡單,但也有一些不利的地方。例如它失去了使用系統(tǒng)調(diào)用pipe()時(shí)可以有的對系統(tǒng)的控制。盡管這樣,因?yàn)榭梢灾苯拥厥褂胹hell命令,所以shell中的一些通配符和其他的一些擴(kuò)展符號都可以在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è)管道,然后對一個(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);
            }
            最后,我們再看一個(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信號。當(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|

            ??? 請注意在文件名后面的管道符號“|”。
            ??? 我們可以使用系統(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(路徑名太長)
            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è)備號和從設(shè)備號。
            }
            }


            [目錄]


            操作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)閉它。
            ??? 請看下面的簡單的服務(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ù),所以你可以在后臺運(yùn)行此程序:
            $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管道上將會(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ì)列、信號量、以及共享內(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)識符
            如果失敗,則返回-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)識符,要么返回具有相同關(guān)鍵字值的隊(duì)列的標(biāo)識符。如果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)識符,我們就可以在隊(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è)信號)
            EINVAL(無效的消息隊(duì)列標(biāo)識符,非正數(shù)的消息類型,或
            者無效的消息長度)
            ENOMEM(沒有足夠的內(nèi)存復(fù)制消息緩沖區(qū))

            ??? 系統(tǒng)調(diào)用msgsnd()的第一個(gè)參數(shù)是消息隊(duì)列標(biāo)識符,它是由系統(tǒng)調(diào)用msgget返回的。第二個(gè)參數(shù)是msgp,是指向消息緩沖區(qū)的指針。參數(shù)msgsz中包含的是消息的字節(jié)大小,但不包括消息類型的長度(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è)小程序試圖將存儲在緩沖區(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ì)列以后,我們將測試數(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(消息的長度大于msgsz,沒有MSG_NOERROR)
            EACCES(沒有讀的權(quán)限)
            EFAULT(msgp指向的地址是無效的)
            EIDRM(隊(duì)列已經(jīng)被刪除)
            EINTR(被信號中斷)
            EINVAL(msgqid無效,或者msgsz小于0)
            ENOMSG(使用IPC_NOWAIT,同時(shí)隊(duì)列中的消息無法滿足要求)

            ??? 很明顯,第一個(gè)參數(shù)用來指定將要讀取消息的隊(duì)列。第二個(gè)參數(shù)代表要存儲消息的消息緩沖區(qū)的地址。第三個(gè)參數(shù)是消息緩沖區(qū)的長度,不包括mtype的長度,它可以按照如下的方法計(jì)算:
            ??????? msgsz=sizeof(structmymsgbuf)-sizeof(long);
            ??? 第四個(gè)參數(shù)是要從消息隊(duì)列中讀取的消息的類型。如果此參數(shù)的值為0,那么隊(duì)列中最長時(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è)信號,則返回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í)際長度大于msgsz,同時(shí)使用了MSG_NOERROR,那么消息將會(huì)被截?cái)啵挥信cmsgsz長度相等的消息返回。一般情況下,系統(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ū)的地址和長度。這樣,系統(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ì)列的操作。

            系統(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,并將其存儲在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ì)列。
            ??? 我們在前面討論過了消息隊(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);
            }
            };

            [目錄]


            信號量

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

            1.內(nèi)核中的數(shù)據(jù)結(jié)構(gòu)semid_ds
            和消息隊(duì)列一樣,系統(tǒng)內(nèi)核為內(nèi)核地址空間中的每一個(gè)信號量集都保存了一個(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í)例。它保存有信號量集的存取權(quán)限的信息,以及信號量集創(chuàng)建者的有關(guān)信息。
            sem_otime最后一次semop()操作的時(shí)間。
            sem_ctime最后一次改動(dòng)此數(shù)據(jù)結(jié)構(gòu)的時(shí)間。
            sem_base指向數(shù)組中第一個(gè)信號量的指針。
            sem_undo數(shù)組中沒有完成的請求的個(gè)數(shù)。
            sem_nsems信號量集(數(shù)組)中的信號量的個(gè)數(shù)。

            2.內(nèi)核中的數(shù)據(jù)結(jié)構(gòu)sem
            在數(shù)據(jù)結(jié)構(gòu)semid_ds中包含一個(gè)指向信號量數(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信號量的當(dāng)前值。
            sem_semncnt等待資源的進(jìn)程數(shù)目。
            sem_semzcnt等待資源完全空閑的進(jìn)程數(shù)目。

            [目錄]


            semget()

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

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

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

            #defineSEMMSL32/*<=512maxnumofsemaphoresperid*/
            下面是一個(gè)打開和創(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ù)進(jìn)行)
            EFAULT(sops指向的地址無效)
            EIDRM(信號量集已經(jīng)刪除)
            EINTR(當(dāng)睡眠時(shí)接收到其他信號)
            EINVAL(信號量集不存在,或者semid無效)
            ENOMEM(使用了SEM_UNDO,但無足夠的內(nèi)存創(chuàng)建所需的數(shù)據(jù)結(jié)構(gòu))
            ERANGE(信號量值超出范圍)

            ??? 第一個(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將要處理的信號量的個(gè)數(shù)。
            sem_op要執(zhí)行的操作。
            sem_flg操作標(biāo)志。

            ??? 如果sem_op是負(fù)數(shù),那么信號量將減去它的值。這和信號量控制的資源有關(guān)。如果沒有使用IPC_NOWAIT,那么調(diào)用進(jìn)程將進(jìn)入睡眠狀態(tài),直到信號量控制的資源可以使用為止。如果sem_op是正數(shù),則信號量加上它的值。這也就是進(jìn)程釋放信號量控制的資源。最后,如果sem_op是0,那么調(diào)用進(jìn)程將調(diào)用sleep(),直到信號量的值為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(信號量集已經(jīng)刪除)
            EINVAL(信號量集不存在,或者semid無效)
            EPERM(EUID沒有cmd的權(quán)利)
            ERANGE(信號量值超出范圍)

            ??? 系統(tǒng)調(diào)用semctl用來執(zhí)行在信號量集上的控制操作。這和在消息隊(duì)列中的系統(tǒng)調(diào)用msgctl是十分相似的。但這兩個(gè)系統(tǒng)調(diào)用的參數(shù)略有不同。因?yàn)樾盘柫恳话闶亲鳛橐粋€(gè)信號量集使用的,而不是一個(gè)單獨(dú)的信號量。所以在信號量集的操作中,不但要知道IPC關(guān)鍵字值,也要知道信號量集中的具體的信號量。這兩個(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)限和使用者。但在信號量中支持額外的可選的命令,這樣就要求有一個(gè)更為復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。
            系統(tǒng)調(diào)用semctl()的第一個(gè)參數(shù)是關(guān)鍵字值。第二個(gè)參數(shù)是信號量數(shù)目。

            ??? 參數(shù)cmd中可以使用的命令如下:
            ??? ·IPC_STAT讀取一個(gè)信號量集的數(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返回正在等待資源的進(jìn)程數(shù)目。
            ??? ·GETPID返回最后一個(gè)執(zhí)行semop操作的進(jìn)程的PID。
            ??? ·GETVAL返回信號量集中的一個(gè)單個(gè)的信號量的值。
            ??? ·GETZCNT返回這在等待完全空閑的資源的進(jìn)程數(shù)目。
            ??? ·SETALL設(shè)置信號量集中的所有的信號量的值。
            ??? ·SETVAL設(shè)置信號量集中的一個(gè)單獨(dú)的信號量的值。

            ??? 參數(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)核中使用的信號量的數(shù)據(jù)結(jié)構(gòu)。array在使用GETALL/SETALL命令時(shí)使用的指針。
            ??? 下面的程序返回信號量的值。當(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è)新的信號量值:

            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)識符。如果失敗,則返回-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)識符,要么返回一個(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)識符,它的下一步就是將共享的內(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è)地址,但這通常是為了加快對硬件設(shè)備的存取,或者解決和其他程序的沖突。
            ??? 下面的程序中的調(diào)用參數(shù)是一個(gè)內(nèi)存段的I P C標(biāo)識符,返回內(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,并將它存儲在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è)叫法有些簡單化,但這有利于了解線程的概念。因?yàn)榫€程和進(jìn)程比起來很小,所以相對來說,線程花費(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ù)。請看下面的代碼:
            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é)束對函數(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”。請注意對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是一種簡單的加鎖的方法來控制對共享資源的存取。我們可以創(chuàng)建一個(gè)讀/寫程序,它們共用一個(gè)共享緩沖區(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ū)只有兩個(gè)狀態(tài),有一條信息或者沒有信息。使用延遲是為了避免一個(gè)線程永遠(yuǎn)占有mutex。
            但mutex的缺點(diǎn)在于它只有兩個(gè)狀態(tài),鎖定和非鎖定。POSIX的條件變量通過允許線程阻塞和等待另一個(gè)線程的信號方法,從而彌補(bǔ)了mutex的不足。當(dāng)接受到一個(gè)信號時(shí),阻塞線程將會(huì)被喚起,并試圖獲得相關(guān)的mutex的鎖。

            [目錄]


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

            我們可以使用信號量重新看一下上面的讀/寫程序。涉及信號量的操作是semaphore_up、semaphore_down、semaphore_init、semaphore_destroy和semaphore_decrement。所有這些操作都只有一個(gè)參數(shù),一個(gè)指向信號量目標(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è)例子也沒有完全地利用一般信號量的所有函數(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用來強(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 閱讀(595) 評論(0)  編輯 收藏 引用 所屬分類: linux
            久久久久亚洲?V成人无码| 亚洲伊人久久精品影院| 精品久久久久久无码国产| 一级女性全黄久久生活片免费 | 国产视频久久| 色欲综合久久躁天天躁| 久久精品国产久精国产果冻传媒| 伊人久久精品无码av一区| 狠狠色丁香久久综合五月| 久久久艹| 久久精品亚洲一区二区三区浴池| 国产叼嘿久久精品久久| 久久精品国产乱子伦| 久久av免费天堂小草播放| 亚洲国产精品无码久久一线| 亚洲国产成人久久综合碰碰动漫3d| 中文精品久久久久人妻| 国产精品天天影视久久综合网| 蜜桃麻豆www久久国产精品| 久久精品一本到99热免费| 日韩久久无码免费毛片软件| 国产成人精品白浆久久69| 中文国产成人精品久久亚洲精品AⅤ无码精品| 欧美黑人激情性久久| 亚洲国产精品无码久久久久久曰| 国内精品久久久久久99蜜桃| 亚洲国产精品狼友中文久久久| 国产成人精品久久亚洲| 久久91精品国产91久久麻豆| 亚洲国产精品久久久天堂| 污污内射久久一区二区欧美日韩 | 亚洲国产成人久久一区久久| 97精品久久天干天天天按摩| 亚洲精品无码久久久久| 久久99热这里只频精品6| 久久久久婷婷| 久久久久综合国产欧美一区二区| 青青草原综合久久大伊人精品| 久久精品国产亚洲AV无码麻豆| 久久亚洲私人国产精品vA| 久久九九久精品国产免费直播|