• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
    • 軟件測試技術
    • 軟件測試博客
    • 軟件測試視頻
    • 開源軟件測試技術
    • 軟件測試論壇
    • 軟件測試沙龍
    • 軟件測試資料下載
    • 軟件測試雜志
    • 軟件測試人才招聘
      暫時沒有公告

    字號: | 推薦給好友 上一篇 | 下一篇

    Linux下C語言編程--進程通信、消息管理

    發布: 2007-7-04 12:06 | 作者: admin | 來源:  網友評論 | 查看: 11次 | 進入軟件測試論壇討論

    領測軟件測試網

    Linux下C語言編程--進程通信、消息管理

    Linux下C語言編程--進程通信、消息管理


    作者:hoyt 2001-05-08 11:38:03 來自:http://linuxc.51.net
    前言:Linux下的進程通信(IPC) 
        Linux下的進程通信(IPC) 

    1.POSIX無名信號量 
    2.System V信號量 
    3.System V消息隊列 
    4.System V共享內存 

    --------------------------------------------------------------------------------
    1。POSIX無名信號量     如果你學習過操作系統,那么肯定熟悉PV操作了.PV操作是原子操作.也就是操作是不可以中斷的,在一定的時間內,只能夠有一個進程的代碼在CPU上面執行.在系統當中,有時候為了順利的使用和保護共享資源,大家提出了信號的概念. 假設我們要使用一臺打印機,如果在同一時刻有兩個進程在向打印機輸出,那么最終的結果會是什么呢.為了處理這種情況,POSIX標準提出了有名信號量和無名信號量的概念,由于Linux只實現了無名信號量,我們在這里就只是介紹無名信號量了. 信號量的使用主要是用來保護共享資源,使的資源在一個時刻只有一個進程所擁有.為此我們可以使用一個信號燈.當信號燈的值為某個值的時候,就表明此時資源不可以使用.否則就表>示可以使用. 為了提供效率,系統提供了下面幾個函數 
    POSIX的無名信號量的函數有以下幾個: 

    #include

    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創建一個信號燈,并初始化其值為value.pshared決定了信號量能否在幾個進程間共享.由于目前Linux還沒有實現進程間共享信號燈,所以這個值只能夠取0. sem_destroy是用來刪除信號燈的.sem_wait調用將阻塞進程,直到信號燈的值大于0.這個函數返回的時候自動的將信號燈的值的件一.sem_post和sem_wait相反,是將信號燈的內容加一同時發出信號喚醒等待的進程..sem_trywait和sem_wait相同,不過不阻塞的,當信號燈的值為0的時候返回EAGAIN,表示以后重試.sem_getvalue得到信號燈的值. 
    由于Linux不支持,我們沒有辦法用源程序解釋了. 
    這幾個函數的使用相當簡單的.比如我們有一個程序要向一個系統打印機打印兩頁.我們首先創建一個信號燈,并使其初始值為1,表示我們有一個資源可用.然后一個進程調用sem_wait由于這個時候信號燈的值為1,所以這個函數返回,打印機開始打印了,同時信號燈的值為0 了. 如果第二個進程要打印,調用sem_wait時候,由于信號燈的值為0,資源不可用,于是被阻塞了.當第一個進程打印完成以后,調用sem_post信號燈的值為1了,這個時候系統通知第二個進程,于是第二個進程的sem_wait返回.第二個進程開始打印了. 
    不過我們可以使用線程來解決這個問題的.我們會在后面解釋什么是線程的.編譯包含上面這幾個函數的程序要加上 -lrt選賢,以連接librt.so庫 
    2。System V信號量 為了解決上面哪個問題,我們也可以使用System V信號量.很幸運的是Linux實現了System V信號量.這樣我們就可以用實例來解釋了. System V信號量的函數主要有下面幾個. 

    #include 
    #include 
    #include 

    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; /* 使用那一個信號 */
    short sem_op; /* 進行什么操作 */
    short sem_flg; /* 操作的標志 */
    };


    ftok函數是根據pathname和proj來創建一個關鍵字.semget創建一個信號量.成功時返回信號的ID,key是一個關鍵字,可以是用ftok創建的也可以是IPC_PRIVATE表明由系統選用一個關鍵字. nsems表明我們創建的信號個數.semflg是創建的權限標志,和我們創建一個文件的標志相同. 
    semctl對信號量進行一系列的控制.semid是要操作的信號標志,semnum是信號的個數,cmd是操作的命令.經常用的兩個值是:SETVAL(設置信號量的值)和IPC_RMID(刪除信號燈).arg是一個給cmd的參數. 
    semop是對信號進行操作的函數.semid是信號標志,spos是一個操作數組表明要進行什么操作,nspos表明數組的個數. 如果sem_op大于0,那么操作將sem_op加入到信號量的值中,并喚醒等待信號增加的進程. 如果為0,當信號量的值是0的時候,函數返回,否則阻塞直到信號量的值為0. 如果小于0,函數判斷信號量的值加上這個負值.如果結果為0喚醒等待信號量為0的進程,如果小與0函數阻塞.如果大于0,那么從信號量里面減去這個值并返回. 
    下面我們一以一個實例來說明這幾個函數的使用方法.這個程序用標準錯誤輸出來代替我們用的打印機. 

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 

    #define PERMS S_IRUSR|S_IWUSR

    void init_semaphore_struct(struct sembuf *sem,int semnum,
    int semop,int semflg)
    {
    /* 初始話信號燈結構 */
    sem->sem_num=semnum;
    sem->sem_op=semop;
    sem->sem_flg=semflg;
    }

    int del_semaphore(int semid)
    {
    /* 信號燈并不隨程序的結束而被刪除,如果我們沒刪除的話(將1改為0)
    可以用ipcs命令查看到信號燈,用ipcrm可以刪除信號燈的
    */
    #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 表示由系統選擇一個關鍵字來創建  */
    /* 創建以后信號燈的初始值為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);

    /* 開始的時候有一個系統資源(一個標準錯誤輸出) */
    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);
     }

    /* 創建一個進程鏈 */
    for(i=0;i if(childpid=fork()) break;

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

    /* 這里要求資源,進入原子操作 */
    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!='')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));
     }

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


    信號燈的主要用途是保護臨界資源(在一個時刻只被一個進程所擁有). 
    3。SystemV消息隊列 為了便于進程之間通信,我們可以使用管道通信 SystemV也提供了一些函數來實現進程的通信.這就是消息隊列. 

      #include 
      #include 
      #include 

      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;   /* 消息類型 */
    ....... /* 其他數據類型 */
    }

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

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 

    #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)

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #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,'',sizeof(struct msgtype));
      msgrcv(msgid,&msg,sizeof(struct msgtype),2,0);
      fprintf(stderr,"Client receive:%s\n",msg.buffer);
      exit(0);
    }  

    注意服務端創建的消息隊列最后沒有刪除,我們要使用ipcrm命令來刪除的. 
    4。SystemV共享內存 還有一個進程通信的方法是使用共享內存.SystemV提供了以下幾個函數以實現共享內存. 

    #include 
    #include 
    #include 

    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是共享內存的大小. shmat是用來連接共享內存的.shmdt是用來斷開共享內存的.不要被共享內存詞語嚇倒,共享內存其實很容易實現和使用的.shmaddr,shmflg我們只要用0代替就可以了.在使用一個共享內存之前我們調用shmat得到共享內存的開始地址,使用結束以后我們使用shmdt斷開這個內存. 

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 

    #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,'',1024);
    strncpy(p_addr,argv[1],1024);
    exit(0);
      }
     else
      {
    c_addr=shmat(shmid,0,0);
    printf("Client get %s",c_addr);
    exit(0);
      } 


    這個程序是父進程將參數寫入到共享內存,然后子進程把內容讀出來.最后我們要使用ipcrm釋放資源的.先用ipcs找出ID然后用ipcrm shm ID刪除. 
    后記: 
    進程通信(IPC)是網絡程序的基礎,在很多的網絡程序當中會大量的使用進程通信的概念和知識.其實進程通信是一件非常復雜的事情,我在這里只是簡單的介紹了一下.如果你想學習進程通信的詳細知識,最好的辦法是自己不斷的寫程序和看聯機手冊.現在網絡上有了很多的知識可以去參考.可惜我看到的很多都是英文編寫的.如果你找到了有中文的版本請盡快告訴我.謝謝! 


    延伸閱讀

    文章來源于領測軟件測試網 http://www.kjueaiud.com/


    關于領測軟件測試網 | 領測軟件測試網合作伙伴 | 廣告服務 | 投稿指南 | 聯系我們 | 網站地圖 | 友情鏈接
    版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
    北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備10010545號-5
    技術支持和業務聯系:info@testage.com.cn 電話:010-51297073

    軟件測試 | 領測國際ISTQBISTQB官網TMMiTMMi認證國際軟件測試工程師認證領測軟件測試網

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>