ipcs是Linux下顯示進程間通信設施狀態的工具。可以顯示消息隊列、共享內存和信號量的信息。對於程序員非常有用,普通的系統管理員一般用不到此指令。
ipcs -q 查看系統使用的IPC隊列資源
ipcs -m 查看系統使用的IPC共享內存資源
ipcs -s 查看系統使用的IPC信號量資源
ipcs -a命令可以查看當前使用的共享內存、消息隊列及信號量所有信息
ipcs -p命令可以得到與共享內存、消息隊列相關進程之間的消息
ipcs -u命令可以查看各個資源的使用總結信息,其中可以看到使用的信號量集的個數、信號量的個數,
以及消息隊列中當前使用的消息個數總數、占用的空間字節數。
默認不加參數時,使用的參數是 -a (all,顯示所有)
pcs -l命令可以查看各個資源的系統限制信息,可以看到系統允許的最大信號量集及信號量個數限制、最大的消息隊列中消息個數等信息。
yxg@k8s:~$ ipcs -l
------ Messages Limits --------
max queues system wide = 32000 //系統最多的消息隊列數量
max size of message (bytes) = 8192 //單個消息的最大字節數
default max size of queue (bytes) = 16384 //單個消息的最大字節數
------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 18014398509465599
max total shared memory (kbytes) = 18014398442373116
min seg size (bytes) = 1
------ Semaphore Limits --------
max number of arrays = 32000
max semaphores per array = 32000
max semaphores system wide = 1024000000
max ops per semop call = 500
semaphore max value = 32767
這個限制可以通過增加內核參數 semmni 的取值來解決,該參數定義了系統能夠擁有的信號量集合的
總數。Linux 可以動態調整大多數內核IPC 參數值的大小,也可以靜態地修改
1、創建消息隊列
消息隊列是隨着內核的存在而存在的,每個消息隊列在系統范圍內對應唯一的鍵值。要獲得一個消息隊列的描述符,
只需要提供該消息隊列的鍵值即可,該鍵值通常由函數ftok返回。
key_t ftok(const char *pathname, int proj_id);
ftok函數根據pathname和proj_id這兩個參數生成唯一的鍵值。
pathname:must refer to an existing, accessible file,在系統中一定要存在,且進程有訪問權限。
proj_id:的取值范圍為1-255
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h>
int main() { int i = 0; for(i=0;i<5;i++) { printf("key[%d] = %lu\n", i, ftok(".", i)); } return 0; }
ftok返回的鍵值可以提供給函數msgget,
msgget根據這個鍵值創建一個新的消息隊列或者訪問一個已存在的消息隊列。
int msgget(key_t key, int msgflg);
參數key即為ftok函數的返回值。msgflag是一個標志參數。
以下是msgflg的可能取值:
IPC_CREAT:如果內核中不存在鍵值與key相等的消息隊列,則新建一個消息隊列;如果存在這樣的消息隊列,返回消息隊列的描述符。
IPC_EXCL:和IPC_CREAT一起使用,如果對應鍵值的消息隊列已經存在,則出錯,返回-1
上述msgflg參數為模式標志參數,使用時需要與IPC對象存取權限(如0600)進行|運算來確定消息隊列的存取權限.
如果用msgget創建了一個新的消息隊列對象時,則msqid_ds結構成員變量的值設置如下:
msg_qnum、msg_lspid、msg_lrpid、 msg_stime、msg_rtime設置為0。
msg_ctime設置為當前時間。
msg_qbytes設成系統的限制值。
msgflg的讀寫權限寫入msg_perm.mode中。
msg_perm結構的uid和cuid成員被設置成當前進程的有效用戶ID,gid和cuid成員被設置成當前進程的有效組ID。
注意:IPC_EXCL單獨使用是沒有任何意義的。
該函數如果調用成功返回一個消息隊列的描述符,否則返回-1
2、寫消息隊列
創建了一個消息隊列后,就可以對消息隊列進行讀寫了。函數msgsnd用於向消息隊列發送(寫)數據。
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid:函數向msgid標識的消息隊列發送一個消息
msgp:指向發送的消息。
msgsz:要發送消息的大小,不包含消息類型占用的4個字節。
msgflg:操作標識位。可以設置為0或者IPC_NOWAIT。如果為0,則當消息隊列已滿的時候,msgsnd將會阻塞,直到消息可寫進消息隊列;如果msgflg
為IPC_NOWAIT,當消息隊列已滿的時候,msgsnd函數將不等待立即返回。
msgsnd函數成功返回0,失敗返回-1。常見錯誤碼有:EAGAIN,說明消息隊列已滿。
EIDRM:說明消息隊列已被刪除
EACCES:說明無權訪問消息隊列
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <stdlib.h> #include <string.h>
//用戶自定義消息緩沖
struct mymsgbuf{ long msgtype; char buf[256]; }; int main() { struct mymsgbuf mymsgbuffer; int msglen = 0; int i = 0; int msgkey = 0; int qid = 0;//消息隊列標識符 //獲取鍵值
msgkey = ftok(".", 11); qid = msgget(msgkey, IPC_CREAT|0660); printf("msgget return %d\n", qid); //填充消息結構,發送到消息隊列
mymsgbuffer.msgtype = 4; strcpy(mymsgbuffer.buf, "manman"); msglen = sizeof(struct mymsgbuf) - 4; if (msgsnd(qid, &mymsgbuffer, msglen, 0) == -1) { perror("msgsnd error\n"); exit(1); } return 0; }
root@wilson-software:~/Project/xa# ./main
msgget return 0
執行程序之后,就向消息隊列放入了一條消息,通過指令ipcs查看:
------ Message Queues --------
key msqid owner perms used-bytes messages
0x0b014424 0 root 660 256 1
3、讀消息隊列
消息隊列中放入數據后,其他進程就可以讀取其中的消息了。讀取消息的系統調用為
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
參數含義:
msqid:消息隊列描述符
msgp:讀取的消息存儲到msgp指向的消息結構中
msgsz:消息緩沖區的大小
msgtyp:為請求讀取的消息類型
msgflg:操作標志位。msgflg可以為IPC_NOWAIT, MSG_EXCEPT ,MSG_NOERROR
0:表示忽略
IPC_NOWAIT:如果沒有滿足條件的消息,調用立即返回,此時錯誤碼為ENOMSG
如果不指定這個參數,那么進程將被阻塞直到函數可以從隊列中得到符合條件的
消息為止。如果一個client 正在等待消息的時候隊列被刪除,EIDRM 就會被返回如果進
程在阻塞等待過程中收到了系統的中斷信號,EINTR 就會被返回。
MSG_EXCEPT :與msgtype配合使用,返回隊列中第一個類型不為msgtype的消息
MSG_NOERROR:如果隊列中滿足條件的消息內容大於所請求的msgsz字節,則把該消息截斷,截斷部分將被丟棄。
如果不指定這個參數,E2BIG 將被返回,而消息則留在隊列中不被
取出。當消息從隊列內取出后,相應的消息就從隊列中刪除了。
可利用ipcrm -q 294912刪除該消息隊列。(294912為msgqid)因為消息隊列是隨內核持續存在的,在程序中若不利用msgctl函數或在命令行用ipcrm命令顯式地刪除,該消息隊列就一直存在於系統中。另外信號量和共享內存也是隨內核持續存在的
在讀取前:
------ Message Queues --------
key msqid owner perms used-bytes messages
0x0b014424 0 root 660 256 1
在讀取后
key msqid owner perms used-bytes messages
0x0b014424 0 root 660 0 0
調用msgrcv函數的時候,成功會返回讀出消息的實際字節數,否則返回-1。
常見錯誤碼有:
E2BIG: 表示消息的長度大於msgsz
EIDRM:表示消息隊列已被刪除
EINVAL:說明msgqid無效或msgsz小於0
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <stdlib.h> #include <string.h>
//用戶自定義消息緩沖
struct mymsgbuf{ long msgtype; char buf[256]; }; int main() { struct mymsgbuf mymsgbuffer; int msglen = 0; int i = 0; int msgkey = 0; int qid = 0;//消息隊列標識符 //獲取鍵值
msgkey = ftok(".", 11); qid = msgget(msgkey, IPC_CREAT|0660); printf("msgget return %d\n", qid);
msglen = sizeof(struct mymsgbuf) - 4;
//上面的程序中發送的消息類型是4
//注意,msgrcv的msgflg參數可設置msgrcv函數是否是阻塞的,經測試msgflg是0的情況會阻塞,直到獲取到消息 if (msgrcv(qid, &mymsgbuffer, msglen, 4, 0) == -1) { perror("msgsnd error\n"); exit(1); } printf("get message:%s\n", mymsgbuffer.buf); return 0; }
運行結果:
root@wilson-software:~/Project/xa# ./main
msgget return 0
get message:manman
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
利用上面提到的msgrcv()對消息長度的處理,我們可以使用下面的方法來檢查隊列內
是存在符合條件的信息:
int peek_message( int qid, long type )
{
int result, length;
if((result = msgrcv( qid, NULL, 0, type, IPC_NOWAIT)) == -1)
{
if(errno == E2BIG)
return(TRUE);
}
return(FALSE);
}
這里我們將msgp 和msgsz 分別設為NULL 和零。然后檢查函數的返回值,如果是E2BIG
則說明存在符合指定類型的消息。一個要注意的地方是IPC_NOWAIT 的使用,它防止了阻塞