• <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>
            <2008年8月>
            272829303112
            3456789
            10111213141516
            17181920212223
            24252627282930
            31123456

            統(tǒng)計(jì)

            • 隨筆 - 44
            • 文章 - 0
            • 評(píng)論 - 86
            • 引用 - 0

            常用鏈接

            留言簿(6)

            隨筆分類(31)

            隨筆檔案(44)

            Mining

            最新隨筆

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            Linux下C編程 進(jìn)程通信 (IPC)

            在Linux中存在下面幾種進(jìn)程間通信方式:

            1.POSIX無(wú)名信號(hào)量
            2.System V信號(hào)量
            3.System V消息隊(duì)列
            4.System V共享內(nèi)存
            5.管道(FIFO)

            --------------------------------------------------------------------------------
            1。POSIX無(wú)名信號(hào)量
            如果你學(xué)習(xí)過操作系統(tǒng),那么肯定熟悉PV操作了.PV操作是原子操作.也就是操作是不可以中斷的,在一定的時(shí)間內(nèi),只能夠有一個(gè)進(jìn)程的代碼在CPU上面執(zhí)行.在系統(tǒng)當(dāng)中,有時(shí)候?yàn)榱隧樌氖褂煤捅Wo(hù)共享資源,大家提出了信號(hào)的概念. 假設(shè)我們要使用一臺(tái)打印機(jī), 如果在同一時(shí)刻有兩個(gè)進(jìn)程在向打印機(jī)輸出,那么最終的結(jié)果會(huì)是什么呢.為了處理這種情況,POSIX標(biāo)準(zhǔn)提出了有名信號(hào)量和無(wú)名信號(hào)量的概念,由于 Linux只實(shí)現(xiàn)了無(wú)名信號(hào)量,我們?cè)谶@里就只是介紹無(wú)名信號(hào)量了. 信號(hào)量的使用主要是用來(lái)保護(hù)共享資源,使的資源在一個(gè)時(shí)刻只有一個(gè)進(jìn)程所擁有.為此我們可以使用一個(gè)信號(hào)燈.當(dāng)信號(hào)燈的值為某個(gè)值的時(shí)候,就表明此時(shí)資源不可以使用.否則就表>示可以使用. 為了提供效率,系統(tǒng)提供了下面幾個(gè)函數(shù)?
            POSIX的無(wú)名信號(hào)量的函數(shù)有以下幾個(gè):

            int ?sem_init(sem_t? * sem, int ?pshared,unsigned? int ?value);
            int ?sem_destroy(sem_t? * sem);
            int ?sem_wait(sem_t? * sem);
            int ?sem_trywait(sem_t? * sem);
            int ?sem_post(sem_t? * sem);
            int ?sem_getvalue(sem_t? * sem);

            sem_init創(chuàng)建一個(gè)信號(hào)燈,并初始化其值為value.pshared決定了信號(hào)量能否在幾個(gè)進(jìn)程間共享.由于目前Linux還沒有實(shí)現(xiàn)進(jìn)程間共享信號(hào)燈,所以這個(gè)值只能夠取0.
            sem_destroy是用來(lái)刪除信號(hào)燈的.
            sem_wait調(diào)用將阻塞進(jìn)程,直到信號(hào)燈的值大于0.這個(gè)函數(shù)返回的時(shí)候自動(dòng)的將信號(hào)燈的值的件一.
            sem_post和sem_wait相反,是將信號(hào)燈的內(nèi)容加一同時(shí)發(fā)出信號(hào)喚醒等待的進(jìn)程..
            sem_trywait和sem_wait相同,不過不阻塞的,當(dāng)信號(hào)燈的值為0的時(shí)候返回EAGAIN,表示以后重試.
            sem_getvalue得到信號(hào)燈的值.
            由于Linux不支持,我們沒有辦法用源程序解釋了.


            2。System V信號(hào)量
            信號(hào)燈的主要用途是保護(hù)臨界資源(在一個(gè)時(shí)刻只被一個(gè)進(jìn)程所擁有). System V信號(hào)量的函數(shù)主要有下面幾個(gè).

            key_t?ftok( char ? * pathname, char ?proj);
            int ?semget(key_t?key, int ?nsems, int ?semflg);
            int ?semctl( int ?semid, int ?semnum, int ?cmd,union?semun?arg);
            int ?semop( int ?semid, struct ?sembuf? * spos, int ?nspos);

            struct ?sembuf{
            short ?sem_num;? /* ?使用那一個(gè)信號(hào)? */
            short ?sem_op;? /* ?進(jìn)行什么操作? */
            short ?sem_flg;? /* ?操作的標(biāo)志? */
            };

            ftok函數(shù)是根據(jù)pathname和proj來(lái)創(chuàng)建一個(gè)關(guān)鍵字.
            semget創(chuàng)建一個(gè)信號(hào)量.成功時(shí)返回信號(hào)的ID,key是一個(gè)關(guān)鍵字,可以是用ftok創(chuàng)建的也可以是IPC_PRIVATE表明由系統(tǒng)選用一個(gè)關(guān)鍵字. nsems表明我們創(chuàng)建的信號(hào)個(gè)數(shù).semflg是創(chuàng)建的權(quán)限標(biāo)志,和我們創(chuàng)建一個(gè)文件的標(biāo)志相同.
            semctl對(duì)信號(hào)量進(jìn)行一系列的控制.semid是要操作的信號(hào)標(biāo)志,semnum是信號(hào)的個(gè)數(shù),cmd是操作的命令.經(jīng)常用的兩個(gè)值是:SETVAL(設(shè)置信號(hào)量的值)和IPC_RMID(刪除信號(hào)燈).arg是一個(gè)給cmd的參數(shù).
            semop是對(duì)信號(hào)進(jìn)行操作的函數(shù).semid是信號(hào)標(biāo)志,spos是一個(gè)操作數(shù)組表明要進(jìn)行什么操作,nspos表明數(shù)組的個(gè)數(shù). 如果 sem_op大于0,那么操作將sem_op加入到信號(hào)量的值中,并喚醒等待信號(hào)增加的進(jìn)程. 如果為0,當(dāng)信號(hào)量的值是0的時(shí)候,函數(shù)返回,否則阻塞直到信號(hào)量的值為0. 如果小于0,函數(shù)判斷信號(hào)量的值加上這個(gè)負(fù)值.如果結(jié)果為0喚醒等待信號(hào)量為0的進(jìn)程,如果小與0函數(shù)阻塞.如果大于0,那么從信號(hào)量里面減去這個(gè)值并返回.

            下面我們一以一個(gè)實(shí)例來(lái)說(shuō)明這幾個(gè)函數(shù)的使用方法.

            #define ?PERMS?S_IRUSR|S_IWUSR
            void ?init_semaphore_struct( struct ?sembuf? * sem, int ?semnum, int ?semop, int ?semflg)
            {
            ?
            /* ?初始話信號(hào)燈結(jié)構(gòu)? */
            ?sem
            -> sem_num = semnum;
            ?sem
            -> sem_op = semop;
            ?sem
            -> sem_flg = semflg;
            }
            int ?del_semaphore( int ?semid)
            {
            ?
            /* ?信號(hào)燈并不隨程序的結(jié)束而被刪除,如果我們沒刪除的話(將1改為0)
            ?可以用ipcs命令查看到信號(hào)燈,用ipcrm可以刪除信號(hào)燈的
            ?
            */
            ?
            #if ?1
            ?
            return ?semctl(semid, 0 ,IPC_RMID);
            ?
            #endif
            }

            int ?main( int ?argc, char ? ** argv)
            {
            ?
            char ?buffer[MAX_CANON], * c;
            ?
            int ?i,n;
            ?
            int ?semid,semop_ret,status;
            ?pid_t?childpid;
            ?
            struct ?sembuf?semwait,semsignal;

            ?
            if ((argc != 2 ) || ((n = atoi(argv[ 1 ])) < 1 ))
            ?{
            ??fprintf(stderr,
            " Usage:%s?number\n\a " ,argv[ 0 ]);
            ??exit(
            1 );
            ?}
            ?
            ?
            /* ?使用IPC_PRIVATE?表示由系統(tǒng)選擇一個(gè)關(guān)鍵字來(lái)創(chuàng)建?? */
            ?
            /* ?創(chuàng)建以后信號(hào)燈的初始值為0? */
            ?
            if ((semid = semget(IPC_PRIVATE, 1 ,PERMS)) ==- 1 )
            ?{
            ??fprintf(stderr,
            " [%d]:Acess?Semaphore?Error:%s\n\a " ,
            ??getpid(),strerror(errno));
            ??exit(
            1 );
            ?}

            ?
            /* ?semwait是要求資源的操作(-1)? */
            ?init_semaphore_struct(
            & semwait, 0 , - 1 , 0 );

            ?
            /* ?semsignal是釋放資源的操作(+1)? */
            ?init_semaphore_struct(
            & semsignal, 0 , 1 , 0 );

            ?
            /* ?開始的時(shí)候有一個(gè)系統(tǒng)資源(一個(gè)標(biāo)準(zhǔn)錯(cuò)誤輸出)? */
            ?
            if (semop(semid, & semsignal, 1 ) ==- 1 )
            ?{
            ??fprintf(stderr,
            " [%d]:Increment?Semaphore?Error:%s\n\a " ,?getpid(),?strerror(errno));
            ??
            if (del_semaphore(semid) ==- 1 )
            ???fprintf(stderr,
            " [%d]:Destroy?Semaphore?Error:%s\n\a " ,?getpid(),?strerror(errno));
            ??exit(
            1 );
            ?}

            ?
            /* ?創(chuàng)建一個(gè)進(jìn)程鏈? */
            ?
            for (i = 0 ;i < n;i ++ )
            ??
            if (childpid = fork())? break ;

            ?sprintf(buffer,
            " [i=%d]-->[Process=%d]-->[Parent=%d]-->[Child=%d]\n " ,?i,getpid(),getppid(),childpid);
            ?c
            = buffer;

            ?
            /* ?這里要求資源,進(jìn)入原子操作? */
            ?
            while (((semop_ret = semop(semid, & semwait, 1 )) ==- 1 ) && (errno == EINTR));
            ?
            if (semop_ret ==- 1 )
            ?{
            ??fprintf(stderr,
            " [%d]:Decrement?Semaphore?Error:%s\n\a " ,
            ??getpid(),strerror(errno));
            ?}
            ?
            else
            ?{
            ??
            while ( * c != ' \0 ' )fputc( * c ++ ,stderr);
            ??
            /* ?原子操作完成,趕快釋放資源? */
            ??
            while (((semop_ret = semop(semid, & semsignal, 1 )) ==- 1 ) && (errno == EINTR));
            ??
            if (semop_ret ==- 1 )
            ???fprintf(stderr,
            " [%d]:Increment?Semaphore?Error:%s\n\a " ,?getpid(),strerror(errno));
            ?}

            ?
            /* ?不能夠在其他進(jìn)程反問信號(hào)燈的時(shí)候,我們刪除了信號(hào)燈? */
            ?
            while ((wait( & status) ==- 1 ) && (errno == EINTR));
            ?
            /* ?信號(hào)燈只能夠被刪除一次的? */
            ?
            if (i == 1 )
            ??
            if (del_semaphore(semid) ==- 1 )
            ?fprintf(stderr,
            " [%d]:Destroy?Semaphore?Error:%s\n\a " ,?getpid(),strerror(errno));
            ?exit(
            0 );
            }


            3。SystemV消息隊(duì)列
            為了便于進(jìn)程之間通信,我們可以使用管道通信 SystemV也提供了一些函數(shù)來(lái)實(shí)現(xiàn)進(jìn)程的通信.這就是消息隊(duì)列.

            int ?msgget(key_t?key, int ?msgflg);
            int ?msgsnd( int ?msgid, struct ?msgbuf? * msgp, int ?msgsz, int ?msgflg);
            int ?msgrcv( int ?msgid, struct ?msgbuf? * msgp, int ?msgsz,
            long ?msgtype, int ?msgflg);
            int ?msgctl(Int?msgid, int ?cmd, struct ?msqid_ds? * buf);

            struct ?msgbuf?{
            long ?msgtype;??? /* ?消息類型? */
            .?
            /* ?其他數(shù)據(jù)類型? */
            }

            msgget函數(shù)和semget一樣,返回一個(gè)消息隊(duì)列的標(biāo)志.
            msgctl和semctl是對(duì)消息進(jìn)行控制.
            msgsnd和msgrcv函數(shù)是用來(lái)進(jìn)行消息通訊的.msgid是接受或者發(fā)送的消息隊(duì)列標(biāo)志. msgp是接受或者發(fā)送的內(nèi)容.msgsz是消息的大小. 結(jié)構(gòu)msgbuf包含的內(nèi)容是至少有一個(gè)為msgtype.其他的成分是用戶定義的.對(duì)于發(fā)送函數(shù)msgflg指出緩沖區(qū)用完時(shí)候的操作.接受函數(shù)指出無(wú)消息時(shí)候的處理.一般為 0. 接收函數(shù)msgtype指出接收消息時(shí)候的操作.
            如果msgtype=0,接收消息隊(duì)列的第一個(gè)消息.大于0接收隊(duì)列中消息類型等于這個(gè)值的第一個(gè)消息.小于0接收消息隊(duì)列中小于或者等于 msgtype絕對(duì)值的所有消息中的最小一個(gè)消息. 我們以一個(gè)實(shí)例來(lái)解釋進(jìn)程通信.下面這個(gè)程序有server和client組成.先運(yùn)行服務(wù)端后運(yùn)行客戶端.

            服務(wù)端 server.c

            #define ???MSG_FILE?"server.c"
            #define ???BUFFER?255
            #define ???PERM?S_IRUSR|S_IWUSR

            struct ?msgtype?{
            long ?mtype;
            char ?buffer[BUFFER + 1 ];
            };

            int ?main()
            {
            ????
            struct ?msgtype?msg;
            ????key_t?key;
            ????
            int ?msgid;

            ????
            if ((key = ftok(MSG_FILE, ' a ' )) ==- 1 )
            ????{
            ????????fprintf(stderr,
            " Creat?Key?Error:%s\a\n " ,strerror(errno));
            ????????exit(
            1 );
            ????}

            ????
            if ((msgid = msgget(key,PERM | IPC_CREAT | IPC_EXCL)) ==- 1 )
            ????{
            ????????fprintf(stderr,
            " Creat?Message??Error:%s\a\n " ,strerror(errno));
            ????????exit(
            1 );
            ????}

            ????
            while ( 1 )
            ????{
            ????????msgrcv(msgid,
            & msg, sizeof ( struct ?msgtype), 1 , 0 );
            ????????fprintf(stderr,
            " Server?Receive:%s\n " ,msg.buffer);
            ????????msg.mtype
            = 2 ;
            ????????msgsnd(msgid,
            & msg, sizeof ( struct ?msgtype), 0 );
            ????}
            ????exit(
            0 );
            }


            --------------------------------------------------------------------------------

            客戶端(client.c)

            #define ???MSG_FILE?"server.c"
            #define ???BUFFER?255
            #define ???PERM?S_IRUSR|S_IWUSR

            struct ?msgtype?{
            long ?mtype;
            char ?buffer[BUFFER + 1 ];
            };

            int ?main( int ?argc, char ? ** argv)
            {
            ?
            struct ?msgtype?msg;
            ?key_t?key;
            ?
            int ?msgid;

            ?
            if (argc != 2 )
            ?{
            ??fprintf(stderr,
            " Usage:%s?string\n\a " ,argv[ 0 ]);
            ??exit(
            1 );
            ?}

            ?
            if ((key = ftok(MSG_FILE, ' a ' )) ==- 1 )
            ?{
            ??fprintf(stderr,
            " Creat?Key?Error:%s\a\n " ,strerror(errno));
            ??exit(
            1 );
            ?}

            ?
            if ((msgid = msgget(key,PERM)) ==- 1 )
            ?{
            ??fprintf(stderr,
            " Creat?Message??Error:%s\a\n " ,strerror(errno));
            ??exit(
            1 );
            ?}

            ?msg.mtype
            = 1 ;
            ?strncpy(msg.buffer,argv[
            1 ],BUFFER);
            ?msgsnd(msgid,
            & msg, sizeof ( struct ?msgtype), 0 );?
            ?memset(
            & msg, ' \0 ' , sizeof ( struct ?msgtype));
            ?msgrcv(msgid,
            & msg, sizeof ( struct ?msgtype), 2 , 0 );
            ?fprintf(stderr,
            " Client?receive:%s\n " ,msg.buffer);
            ?exit(
            0 );
            }??

            注意服務(wù)端創(chuàng)建的消息隊(duì)列最后沒有刪除,我們要使用ipcrm命令來(lái)刪除的.
            4。SystemV共享內(nèi)存
            還有一個(gè)進(jìn)程通信的方法是使用共享內(nèi)存.SystemV提供了以下幾個(gè)函數(shù)以實(shí)現(xiàn)共享內(nèi)存.

            int ?shmget(key_t?key, int ?size, int ?shmflg);
            void ? * shmat( int ?shmid, const ? void ? * shmaddr, int ?shmflg);
            int ?shmdt( const ? void ? * shmaddr);
            int ?shmctl( int ?shmid, int ?cmd, struct ?shmid_ds? * buf);

            shmget和shmctl沒有什么好解釋的.size是共享內(nèi)存的大小.
            shmat是用來(lái)連接共享內(nèi)存的.shmdt是用來(lái)斷開共享內(nèi)存的.
            shmaddr,shmflg我們只要用0代替就可以了.在使用一個(gè)共享內(nèi)存之前我們調(diào)用 shmat得到共享內(nèi)存的開始地址,使用結(jié)束以后我們使用shmdt斷開這個(gè)內(nèi)存.?

            #define ?PERM?S_IRUSR|S_IWUSR
            int ?main( int ?argc, char ? ** argv)
            {
            ?
            ?
            int ?shmid;
            ?
            char ? * p_addr, * c_addr;
            ?
            if (argc != 2 )
            ?{
            ??fprintf(stderr,
            " Usage:%s\n\a " ,argv[ 0 ]);
            ??exit(
            1 );
            ?}

            ?
            if ((shmid = shmget(IPC_PRIVATE, 1024 ,PERM)) ==- 1 )
            ?{
            ??fprintf(stderr,
            " Create?Share?Memory?Error:%s\n\a " ,strerror(errno));
            ??exit(
            1 );
            ?}
            ?
            if (fork())
            ?{
            ??p_addr
            = shmat(shmid, 0 , 0 );
            ??memset(p_addr,
            ' \0 ' , 1024 );
            ??strncpy(p_addr,argv[
            1 ], 1024 );
            ??exit(
            0 );
            ?}
            ?
            else
            ?{
            ??c_addr
            = shmat(shmid, 0 , 0 );
            ??printf(
            " Client?get?%s " ,c_addr);
            ??exit(
            0 );
            ?}?
            }?

            這個(gè)程序是父進(jìn)程將參數(shù)寫入到共享內(nèi)存,然后子進(jìn)程把內(nèi)容讀出來(lái).最后我們要使用ipcrm釋放資源的.先用ipcs找出ID然后用ipcrm shm ID刪除.?

            5、管道(FIFO)
            管道有無(wú)名管道和有名管道兩種,無(wú)名管道一般在父子進(jìn)程中使用。
            無(wú)名管道的使用方法一般是:
            #include?<unistd.h>
            int?pipe(int?filedes[2]);
            filedes[
            0]用于讀出數(shù)據(jù),讀取時(shí)必須關(guān)閉寫入端,即close(filedes[1]);
            filedes[
            1]用于寫入數(shù)據(jù),寫入時(shí)必須關(guān)閉讀取端,即close(filedes[0])。

            無(wú)名管道的使用方法是:
            #include?<sys/types.h>
            #include?
            <sys/stat.h>
            int?mkfifo(const?char?*pathname,?mode_t?mode);

            讀寫管道與讀寫文件的操作相同。



            posted on 2006-08-04 18:05 泡泡牛 閱讀(3433) 評(píng)論(1)  編輯 收藏 引用

            評(píng)論

            # re: Linux下C編程 進(jìn)程通信 (IPC) 2009-03-04 11:18 fuck

            你的代碼有錯(cuò)誤!
            ipc 信號(hào)量的代碼里,應(yīng)該是i=0時(shí)才能刪除sem資源

            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            久久精品国产99久久久古代| 久久AV高潮AV无码AV| 青青青青久久精品国产h| 久久综合九色综合精品| 久久久久久国产精品免费免费| 亚洲国产日韩综合久久精品| 午夜天堂精品久久久久| 精品久久国产一区二区三区香蕉| 四虎影视久久久免费| 国产Av激情久久无码天堂| 久久精品国产一区二区三区不卡 | 伊人久久大香线蕉AV色婷婷色| 国产成人精品久久一区二区三区 | 亚洲国产高清精品线久久| 无码AV波多野结衣久久| 亚洲国产小视频精品久久久三级| 久久人人爽人人爽人人AV| 色播久久人人爽人人爽人人片aV| 国内精品久久国产大陆| 囯产极品美女高潮无套久久久| 久久综合九色综合久99| 国产色综合久久无码有码| 人人狠狠综合久久亚洲高清| 精品亚洲综合久久中文字幕| 国产成人精品综合久久久| 亚洲国产一成久久精品国产成人综合| 99久久精品费精品国产一区二区| 伊人久久大香线蕉av一区| 亚洲人成无码www久久久| 久久久国产精品| 精品国产综合区久久久久久| 精品久久久久国产免费| 久久九九免费高清视频| 久久久99精品成人片中文字幕 | 2021最新久久久视精品爱| 精品久久久无码中文字幕| 久久ww精品w免费人成| 欧美与黑人午夜性猛交久久久| 国产精品熟女福利久久AV| 国产精品热久久无码av| 精品国产乱码久久久久久浪潮|