?? 11.html
字號:
if((send_message(qid,&msg))==-1){<br>perror("send_message");<br>exit(1);<br>}<br>}<br> 在創建和打開消息隊列以后,我們將測試數據裝入到消息緩沖區中。最后調用send_messag把消息發送到消息隊列中。現在在消息隊列中有了一條消息,我們可以使用ipcs命令來查看隊列的狀態。下面討論如何從隊列中獲取消息。可以使用系統調用msgrcv():<p><p><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I249" ID="I249"></A><center><b><font size=+2>msgrcv()</font></b></center><br>系統調用:msgrcv();<br>原型:intmsgrcv(intmsqid,structmsgbuf*msgp,intmsgsz,longmtype,intmsgflg);<br>返回值:如果成功,則返回復制到消息緩沖區的字節數。<br>如果失敗,則返回-1:errno=E2BIG(消息的長度大于msgsz,沒有MSG_NOERROR)<br>EACCES(沒有讀的權限)<br>EFAULT(msgp指向的地址是無效的)<br>EIDRM(隊列已經被刪除)<br>EINTR(被信號中斷)<br>EINVAL(msgqid無效,或者msgsz小于0)<br>ENOMSG(使用IPC_NOWAIT,同時隊列中的消息無法滿足要求)<p> 很明顯,第一個參數用來指定將要讀取消息的隊列。第二個參數代表要存儲消息的消息緩沖區的地址。第三個參數是消息緩沖區的長度,不包括mtype的長度,它可以按照如下的方法計算:<br> msgsz=sizeof(structmymsgbuf)-sizeof(long);<br> 第四個參數是要從消息隊列中讀取的消息的類型。如果此參數的值為0,那么隊列中最長時間的一條消息將返回,而不論其類型是什么。<br>如果調用中使用了IPC_NOWAIT作為標志,那么當沒有數據可以使用時,調用將把ENOMSG返回到調用進程中。否則,調用進程將會掛起,直到隊列中的一條消息滿足msgrcv()的參數要求。如果當客戶端等待一條消息的時候隊列為空,將會返回EIDRM。如果進程在等待消息的過程中捕捉到一個信號,則返回EINTR。<br> 下面就是一個從隊列中讀取消息的程序:<p>intread_message(int qid,long type,struct mymsgbuf*qbuf)<br>{<br>intresult,length;<br>/*The length is essentially the size of the structure minus sizeof(mtype)*/<br>length=sizeof(structmymsgbuf)-sizeof(long);<br>if((result=msgrcv(qid,qbuf,length,type,0))==-1)<br>{<br>return(-1);<br>}<br>return(result);<br>}<br> 在成功地讀取了一條消息以后,隊列中的這條消息的入口將被刪除。<br> 參數msgflg中的MSG_NOERROR位提供一種額外的用途。如果消息的實際長度大于msgsz,同時使用了MSG_NOERROR,那么消息將會被截斷,只有與msgsz長度相等的消息返回。一般情況下,系統調用msgrcv()會返回-1,而這條消息將會繼續保存在隊列中。我們可以利用這個特點編制一個程序,利用這個程序可以查看消息隊列的情況,看看符合我們條件的消息是否已經到來:<p>intpeek_message(int qid,long type)<br>{<br>intresult,length;<br>if((result=msgrcv(qid,NULL,0,type,IPC_NOWAIT))==-1)<br>{<br>if(errno==E2BIG)<br>return(TRUE);<br>}<br>return(FALSE);<br>}<br> 在上面的程序中,我們忽略了緩沖區的地址和長度。這樣,系統調用將會失敗。盡管如此,我們可以檢查返回的E2BIG值,它說明符合條件的消息確實存在。<p><p><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I250" ID="I250"></A><center><b><font size=+2>msgctl()</font></b></center><br>系統調用msgctl()<p> 下面我們繼續討論如何使用一個給定的消息隊列的內部數據結構。我們可以使用系統調用msgctl ( )來控制對消息隊列的操作。<p>系統調用: msgctl( ) ;<br>調用原型: int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );<br>返回值: 0 ,如果成功。<br>- 1,如果失敗:errno = EACCES (沒有讀的權限同時cmd 是IPC_STAT )<br>EFAULT (buf 指向的地址無效)<br>EIDRM (在讀取中隊列被刪除)<br>EINVAL (msgqid無效, 或者msgsz 小于0 )<br>EPERM (IPC_SET或者IPC_RMID 命令被使用,但調用程序沒有寫的權限)<br>下面我們看一下可以使用的幾個命令:<br>IPC_STAT<br>讀取消息隊列的數據結構msqid_ds,并將其存儲在b u f指定的地址中。<br>IPC_SET<br>設置消息隊列的數據結構msqid_ds中的ipc_perm元素的值。這個值取自buf參數。<br>IPC_RMID<br>從系統內核中移走消息隊列。<br> 我們在前面討論過了消息隊列的數據結構(msqid_ds)。系統內核中為系統中的每一個消息隊列保存一個此數據結構的實例。通過使用IPC_STAT命令,我們可以得到一個此數據結構的副本。下面的程序就是實現此函數的過程:<p>int get_queue_ds( int qid, struct msgqid_ds *qbuf )<br>{<br>if( msgctl( qid, IPC_STAT, qbuf) == -1)<br>{<br>return(-1);<br>}<br>return(0);<br>}<p> 如果不能復制內部緩沖區,調用進程將返回-1。如果調用成功,則返回0。緩沖區中應該包括消息隊列中的數據結構。<br> 消息隊列中的數據結構中唯一可以改動的元素就是ipc_perm。它包括隊列的存取權限和關于隊列創建者和擁有者的信息。你可以改變用戶的id、用戶的組id以及消息隊列的存取權限。<br> 下面是一個修改隊列存取模式的程序:<p>int change_queue_mode(int qid, char *mode )<br>{<br>struct msqid_ds tmpbuf;<br>/* Retrieve a current copy of the internal data structure */<br>get_queue_ds( qid, &tmpbuf);<br>/* Change the permissions using an old trick */<br>sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);<br>/* Update the internal data structure */<br>if( msgctl( qid, IPC_SET, &tmpbuf) == -1)<br>{<br>return(-1);<br>}<br>return(<br>}<p> 我們通過調用get_queue_ds來讀取隊列的內部數據結構。然后,我們調用sscanf( )修改數據結構msg_perm中的mode 成員的值。但直到調用msgctl()時,權限的改變才真正完成。在這里msgctl()使用的是IPC_SET命令。<br> 最后,我們使用系統調用msgctl ( )中的IPC_RMID命令刪除消息隊列:<p>int remove_queue(int qid )<br>{<br>if( msgctl( qid, IPC_RMID, 0) == -1)<br>{<br>return(-1);<br>}<br>return(0);<br>}<br>};<p><p><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I251" ID="I251"></A><center><b><font size=+2>信號量</font></b></center><br> 信號量是一個可以用來控制多個進程存取共享資源的計數器。它經常作為一種鎖定機制來防止當一個進程正在存取共享資源時,另一個進程也存取同一資源。下面先簡要地介紹一下信號量中涉及到的數據結構。<p>1.內核中的數據結構semid_ds<br>和消息隊列一樣,系統內核為內核地址空間中的每一個信號量集都保存了一個內部的數據結構。數據結構的原型是semid_ds。它是在linux/sem.h中做如下定義的:<br>/*One semid data structure for each set of semaphores in the system.*/<br>structsemid_ds{<br>structipc_permsem_perm;/*permissions..seeipc.h*/<br>time_tsem_otime;/*last semop time*/<br>time_tsem_ctime;/*last change time*/<br>structsem*sem_base;/*ptr to first semaphore in array*/<br>structwait_queue*eventn;<br>structwait_queue*eventz;<br>structsem_undo*undo;/*undo requestson this array*/<br>ushortsem_nsems;/*no. of semaphores in array*/<br>};<br>sem_perm是在linux/ipc.h定義的數據結構ipc_perm的一個實例。它保存有信號量集的存取權限的信息,以及信號量集創建者的有關信息。<br>sem_otime最后一次semop()操作的時間。<br>sem_ctime最后一次改動此數據結構的時間。<br>sem_base指向數組中第一個信號量的指針。<br>sem_undo數組中沒有完成的請求的個數。<br>sem_nsems信號量集(數組)中的信號量的個數。<p>2.內核中的數據結構sem<br>在數據結構semid_ds中包含一個指向信號量數組的指針。此數組中的每一個元素都是一個<br>數據結構sem。它也是在linux/sem.h中定義的:<br>/*One semaphore structure for each semaphore in the system.*/<br>structsem{<br>shortsempid;/*pid of las toperation*/<br>ushortsemval;/*current value*/<br>ushortsemncnt;/*num procs awaiting increase in semval*/<br>ushortsemzcnt;/*num procs awaiting semval=0*/<br>};<br>sem_pid最后一個操作的PID(進程ID)。<br>sem_semval信號量的當前值。<br>sem_semncnt等待資源的進程數目。<br>sem_semzcnt等待資源完全空閑的進程數目。<p><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I252" ID="I252"></A><center><b><font size=+2>semget()</font></b></center><br> 我們可以使用系統調用semget()創建一個新的信號量集,或者存取一個已經存在的信號量集:<p>系統調用:semget();<br>原型:intsemget(key_t key,int nsems,int semflg);<br>返回值:如果成功,則返回信號量集的IPC標識符。如果失敗,則返回-1:errno=EACCESS(沒有權限)<br>EEXIST(信號量集已經存在,無法創建)<br>EIDRM(信號量集已經刪除)<br>ENOENT(信號量集不存在,同時沒有使用IPC_CREAT)<br>ENOMEM(沒有足夠的內存創建新的信號量集)<br>ENOSPC(超出限制)<p> 系統調用semget()的第一個參數是關鍵字值(一般是由系統調用ftok()返回的)。系統內核將此值和系統中存在的其他的信號量集的關鍵字值進行比較。打開和存取操作與參數semflg中的內容相關。IPC_CREAT如果信號量集在系統內核中不存在,則創建信號量集。IPC_EXCL當和IPC_CREAT一同使用時,如果信號量集已經存在,則調用失敗。如果單獨使用IPC_CREAT,則semget()要么返回新創建的信號量集的標識符,要么返回系統中已經存在的同樣的關鍵字值的信號量的標識符。如果IPC_EXCL和IPC_CREAT一同使用,則要么返回新創建的信號量集的標識符,要么返回-1。IPC_EXCL單獨使用沒有意義。參數nsems指出了一個新的信號量集中應該創建的信號量的個數。信號量集中最多的信號量的個數是在linux/sem.h中定義的:<p>#defineSEMMSL32/*<=512maxnumofsemaphoresperid*/<br>下面是一個打開和創建信號量集的程序:<br>intopen_semaphore_set(key_t keyval,int numsems)<br>{<br>intsid;<br>if(!numsems)<br>return(-1);<br>if((sid=semget(mykey,numsems,IPC_CREAT|0660))==-1)<br>{<br>return(-1);<br>}<br>return(sid);<br>}<br>};<p><p><p><br><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I253" ID="I253"></A><center><b><font size=+2>semop()</font></b></center><br>系統調用:semop();<br>調用原型:int semop(int semid,struct sembuf*sops,unsign ednsops);<br>返回值:0,如果成功。-1,如果失敗:errno=E2BIG(nsops大于最大的ops數目)<br>EACCESS(權限不夠)<br>EAGAIN(使用了IPC_NOWAIT,但操作不能繼續進行)<br>EFAULT(sops指向的地址無效)<br>EIDRM(信號量集已經刪除)<br>EINTR(當睡眠時接收到其他信號)<br>EINVAL(信號量集不存在,或者semid無效)<br>ENOMEM(使用了SEM_UNDO,但無足夠的內存創建所需的數據結構)<br>ERANGE(信號量值超出范圍)<p> 第一個參數是關鍵字值。第二個參數是指向將要操作的數組的指針。第三個參數是數組中的操作的個數。參數sops指向由sembuf組成的數組。此數組是在linux/sem.h中定義的:<p>/*semop systemcall takes an array of these*/<br>structsembuf{<br>ushortsem_num;/*semaphore index in array*/<br>shortsem_op;/*semaphore operation*/<br>shortsem_flg;/*operation flags*/<br>sem_num將要處理的信號量的個數。<br>sem_op要執行的操作。<br>sem_flg操作標志。<p> 如果sem_op是負數,那么信號量將減去它的值。這和信號量控制的資源有關。如果沒有使用IPC_NOWAIT,那么調用進程將進入睡眠狀態,直到信號量控制的資源可以使用為止。如果sem_op是正數,則信號量加上它的值。這也就是進程釋放信號量控制的資源。最后,如果sem_op是0,那么調用進程將調用sleep(),直到信號量的值為0。這在一個進程等待完全空閑的資源時使用。<p><p><p><br><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I254" ID="I254"></A><center><b><font size=+2>semctl()</font></b></center><br>系統調用:semctl();<br>原型:int semctl(int semid,int semnum,int cmd,union semunarg);<br>返回值:如果成功,則為一個正數。<br>如果失敗,則為-1:errno=EACCESS(權限不夠)<br>EFAULT(arg指向的地址無效)<br>EIDRM(信號量集已經刪除)<br>EINVAL(信號量集不存在,或者semid無效)<br>EPERM(EUID沒有cmd的權利)<br>ERANGE(信號量值超出范圍)<p> 系統調用semctl用來執行在信號量集上的控制操作。這和在消息隊列中的系統調用msgctl是十分相似的。但這兩個系統調用的參數略有不同。因為信號量一般是作為一個信號量集使用的,而不是一個單獨的信號量。所以在信號量集的操作中,不但要知道IPC關鍵字值,也要知道信號量集中的具體的信號量。這兩個系統調用都使用了參數cmd,它用來指出要操作的具體命令。兩個系統調用中的最后一個參數也不一樣。在系統調用msgctl中,最后一個參數是指向內核中使用的數據結構的指針。我們使用此數據結構來取得有關消息隊列的一些信息,以及設置或者改變隊列的存取權限和使用者。但在信號量中支持額外的可選的命令,這樣就要求有一個更為復雜的數據結構。<br>系統調用semctl()的第一個參數是關鍵字值。第二個參數是信號量數目。<p> 參數cmd中可以使用的命令如下:<br> ·IPC_STAT讀取一個信號量集的數據結構semid_ds,并將其存儲在semun中的buf參數中。<br> ·IPC_SET設置信號量集的數據結構semid_ds中的元素ipc_perm,其值取自semun中的buf參數。<br> ·IPC_RMID將信號量集從內存中刪除。<br> ·GETALL用于讀取信號量集中的所有信號量的值。<br> ·GETNCNT返回正在等待資源的進程數目。<br> ·GETPID返回最后一個執行semop操作的進程的PID。<br> ·GETVAL返回信號量集中的一個單個的信號量的值。<br> ·GETZCNT返回這在等待完全空閑的資源的進程數目。<br> ·SETALL設置信號量集中的所有的信號量的值。<br> ·SETVAL設置信號量集中的一個單獨的信號量的值。<p> 參數arg代表一個semun的實例。semun是在linux/sem.h中定義的:<br>/*arg for semctl systemcalls.*/<br>unionsemun{<br>intval;/*value for SETVAL*/<br>structsemid_ds*buf;/*buffer for IPC_STAT&IPC_SET*/<br>ushort*array;/*array for GETALL&SETALL*/<br>structseminfo*__buf;/*buffer for IPC_INFO*/<br>void*__pad;<p> val當執行SETVAL命令時使用。buf在IPC_STAT/IPC_SET命令中使用。代表了內核中使用的信號量的數據結構。array在使用GETALL/SETALL命令時使用的指針。<br> 下面的程序返回信號量的值。當使用GETVAL命令時,調用中的最后一個參數被忽略:<p>intget_sem_val(intsid,intsemnum)<br>{<br>return(semctl(sid,semnum,GETVAL,0));<br>}<p> 下面是一個實際應用的例子:<p>#defineMAX_PRINTERS5<br>printer_usage()<br>{<br>int x;<br>for(x=0;x<MAX_PRINTERS;x++)<br>printf("Printer%d:%d\n\r",x,get_sem_val(sid,x));<br>}<p> 下面的程序可以用來初始化一個新的信號量值:<p>void init_semaphore(int sid,int semnum,int initval)<br>{<br>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -