linux調試工具ipcs的深入分析
來源:互聯網
上載者:User
1)system v系統共用記憶體用ipcs調試共用記憶體測試來源程式如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
void error_out(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}int main (int argc, char *argv[])
{
key_t mykey = 12345678;
const size_t region_size = sysconf(_SC_PAGE_SIZE);
int smid = shmget(mykey, region_size, IPC_CREAT|0666);
if(smid == -1)
error_out("shmget");
void *ptr;
ptr = shmat(smid, NULL, 0);
if (ptr == (void *) -1)
error_out("shmat");
pid_t pid = fork();
if (pid == 0){
u_long *d = (u_long *)ptr;
*d = 0xdeadbeef;
exit(0);
}
else{
int status;
waitpid(pid, &status, 0);
printf("child wrote %#lx\n", *(u_long *)ptr);
}
sleep(30);
int r = shmdt(ptr);
if (r == -1)
error_out("shmdt");
r = shmctl(smid, IPC_RMID, NULL);
if (r == -1)
error_out("shmdt");
return 0;
}編譯:
gcc smem.c -o smem注:這個程式會申請共用記憶體,父子進程都會向共用記憶體寫資料,達到IPC通訊的目的.
終端1)
./smem
child wrote 0xdeadbeef終端2)
ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00bc614e 18874397 root 666 4096 1 注:
key欄中列出的資訊是應用程式定義的索引值,如果是私人對象的索引值則為0,在這裡我們定義索引值為12345678,也就是輸出的0x00bc614e(十六進位)
shmid欄中列出共用記憶體的ID,這個值是唯一的.
owner欄中列出建立共用記憶體的使用者是root.
perms欄中列出共用記憶體的許可權.
bytes欄中列出這塊共用記憶體的大小,我們通過調用sysconf(_SC_PAGE_SIZE)得到要建立的共用記憶體大小為4096個位元組.
nattch欄中列出串連在關聯的共用記憶體段的進程數.
status欄中列出當前共用記憶體的狀態,當該段記憶體的mode欄位設定了SHM_DEST位時就會顯示"dest"字樣,
當使用者調用shmctl的IPC_RMID時,核心首先看有多少個進程還和這段記憶體關聯著,如果關聯數為0,就會銷毀(釋放)這段記憶體,否則就設定這段記憶體的mode位SHM_DEST,
並設定它的key為IPC_PRIVATE,這意味著關聯著的進程仍可合法存取這端記憶體,但是它不能再被新的進程關聯了.在上面的輸出中,我們沒有看到smem用到的共用記憶體有dest的狀態,而此時我們用ipcrm -m 18874397手工刪除該段共用記憶體時,
此時該段的共用記憶體索引值將會是0x00000000(IPC_PRIVATE),而程式通過調用shmdt來釋放該段共用記憶體時,這段共用記憶體才會真正的消失.
為完成這個測試,我們修改上面的程式,在shmdt()後面增加:
printf("shmdt function run finished\n");
sleep(30);
在shmctl函數後面增加:
printf("shmctl function run finished\n");終端1,重新編譯,運行
gcc smem.c -o smem
./smem
child wrote 0xdeadbeef終端2
運行ipcs -m查看共用記憶體,程式進入第一個sleep(30);,此時status為空白
ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00bc614e 0 root 666 4096 1 刪除shmid為32768的共用記憶體,此時status為dest,而key變為0x00000000
ipcrm -m 32768
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 32768 root 666 4096 1 dest 過30秒後,此時程式運行了shmdt函數釋放共用記憶體,我們用ipcs -m再看不到該共用記憶體,雖然它沒有運行到shmctl(smid, IPC_RMID, NULL);
最後smem再過30秒後,運行了shmctl(smid, IPC_RMID, NULL);刪除共用記憶體,這時會報錯shmdt: Invalid argument,因為我們手工刪除了共用記憶體,
又程式到最後再去刪除共用記憶體,所以報錯.我們通過ipcs -mi 32768可以看到更詳細的資訊,如下:Shared memory Segment shmid=327680
uid=0 gid=0 cuid=0 cgid=0
mode=0666 access_perms=0666
bytes=4096 lpid=3263 cpid=3263 nattch=0
att_time=Mon Mar 14 09:42:52 2011
det_time=Mon Mar 14 09:43:22 2011
change_time=Mon Mar 14 09:42:52 2011 注:
cuid=0代表建立這個共用記憶體的使用者ID為0
cgid=0代表建立這個共用記憶體的組ID為0
lpid=3263代表最後一次訪問這個共用記憶體段的PID為3263
cpid=3263代表最後一產建立這個共用記憶體段的PID為3263
att_time=Mon Mar 14 09:42:52 2011代表最後一次調用shmat()的時間
det_time=Mon Mar 14 09:43:22 2011代表最後一次調用shmdt()的時間
change_time=Mon Mar 14 09:42:52 2011代表最後一次用shmctl()修改共用記憶體段的時間.最後system v共用記憶體的最大值可以通過修改/proc/sys/kernel/shmmax進行調整.
2)system v系統訊息佇列用ipcs調試訊息佇列.測試來源程式如下:#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/msg.h>
#include <sys/ipc.h>struct message {
long int mtype;
char mtext[128];
};int send_msg(int qid, int mtype, const char text[]){
struct message msg = {
.mtype = mtype
};
strncpy (msg.mtext, text, sizeof(msg.mtext)); int r = msgsnd(qid, &msg, sizeof(msg), 0);
if (r == -1){
perror("msgsnd");
}
}void producer(int mqid)
{
send_msg(mqid, 1, "type 1 - first");
send_msg(mqid, 2, "type 2 - second");
send_msg(mqid, 1, "type 1 - third");
}void consumer(int qid)
{
struct message msg;
int r;
int i;
for (i = 0;i<3; i++){
r = msgrcv(qid, &msg, sizeof(struct message), -2, 0);
printf("'%s'\n", msg.mtext);
}
}int main (int argc, char *argv[])
{
int mqid;
mqid = msgget (IPC_PRIVATE, S_IREAD|S_IWRITE);
if (mqid == -1) {
perror("msgget");
exit (1);
} pid_t pid = fork();
if (pid == 0){
sleep(60);
consumer(mqid);
exit (0);
}
else{
int status;
producer(mqid);
wait(&status);
}
int r = msgctl(mqid, IPC_RMID, 0);
if (r)
perror("msgctl");
return 0;
}編譯mesg.c
gcc mesg.c -o mesg注:這個程式中,父進程會將三條訊息發送到訊息佇列,子進程在等待60秒後,再收接訊息.
在60秒中,訊息存在於訊息佇列,以便於我們查看.執行mesg
./mesg&ipcs -q------ Message Queues --------
key msqid owner perms used-bytes messages
0x00000000 229376 root 600 408 3注:
key欄中列出的資訊是應用程式定義的索引值.
msgid欄中列出的值是系統定義的索引值.
正如所期望的,系統定義的索引值是唯一的,而在本例中應用程式定義的索引值全部是0,這意味著這些訊息佇列是使用IPC_PRIVATE索引值建立的.
owner欄中列出建立訊息佇列的使用者是root.
perms欄中列出這個訊息佇列的許可權.
used-bytes欄中列出這個訊息佇列所佔用的空間大小,在這裡我們的結構體:
struct message {
long int mtype;
char mtext[128];
};
long int mtype佔用8個位元組,因為它是64位系統,如果是32位系統,它佔用的位元組為4個,
char mtext[128]佔用128個位元組,也就是一條訊息就是136,三條訊息正好是408.
messages欄中列出這條訊息佇列中有幾條訊息,我們發送了三條訊息,所以這裡正好是3.用ipcs -q -i PID的方式可以看到更詳細的資訊,如下面:
ipcs -q -i 294912Message Queue msqid=294912
uid=0 gid=0 cuid=0 cgid=0 mode=0600
cbytes=408 qbytes=16384 qnum=3 lspid=4036 lrpid=0
send_time=Fri Mar 11 20:52:21 2011
rcv_time=Not set
change_time=Fri Mar 11 20:52:21 2011 注:
cuid一欄列出建立這個訊息佇列的使用者ID
cgid一欄列出建立這個訊息佇列的組ID
qbytes一欄列出SYSTEM V訊息佇列的最大值,可以通過修改/proc/sys/kernel/msgmnb和/proc/sys/kernel/msgmax進行調整.
lspid一欄列出最後一個發送訊息到這個訊息佇列的進程.
lrpid一欄列出最後一個從這個訊息佇列接收訊息的進程.
send_time一欄列出發送訊息到這個訊息佇列的最後時間.
rcv_time一欄列出從這個訊息佇列接收訊息的最後時間.
change_time一欄列出更改這個訊息佇列的最後時間.最後可以用ipcrm -q 來刪除訊息佇列
3)system v系統的訊號量用ipcs調試訊號量測試來源程式如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/sem.h>int
main (int argc, char *argv[])
{
key_t semkey = ftok("/tmp", 'a'); int semid =
semget(semkey, 1, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR); if(semid != -1){
printf("Created new semaphore\n");
}
else
if(errno == EEXIST){
printf("semaphore exists\n");
semid = semget(semkey, 1, 0);
} assert(semid != -1);
if (argc == 2){
int op = atoi(argv[1]); struct sembuf sb={
.sem_num = 0,
.sem_op = op,
.sem_{敏感詞} = 0
}; int r = semop (semid,&sb,1); assert(r != -1); printf("Operation %d done\n", op);
}
else {
printf("no operation \n");
} printf("semid %d value %d\n", semid ,semctl(semid,0,GETVAL));
return 0;
}編譯sysv_sem.c
gcc sysv_sem.c -o sysv_sem注:
這個程式通過semget函數建立了一個訊號量集,semop函數操作了訊號量集中的一個集號,這樣來增加或減少訊號量中含的值,從而達到程式同步和資源互斥的目的.執行程式,此時建立了一個訊號量,初始值.sem_num為0,所以它通過semctl函數擷取的值為0.
./sysv_sem 0
Created new semaphore
Operation 0 done
semid 196608 value 0執行程式,將參數換成1,此時它的值為1,如下:
./sysv_sem 1
semaphore exists
Operation 1 done
semid 196608 value 1用ipcs -s來查看訊號量資訊,如下:
ipcs -s------ Semaphore Arrays --------
key semid owner perms nsems
0x61018001 196608 root 600 1注:
key欄中列出的資訊是應用程式定義的索引值,這裡我們用ftok來產生它的ID.
semid欄中列出系統定義的索引值.
owner欄中列出建立該訊號量集的使用者是root
perms欄中列出這個訊號量集的許可權.
nsems欄中列出這個訊號量集中指定了多少個訊號量,我們的例子中指定了1個,可以通過semget函數指定多個,如:
segmet(semkey, 5, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR);
這樣就在這個訊號集中指定了5個訊號量. 用-i參數可以看到更詳細的資訊,如下;
ipcs -s -i 196608Semaphore Array semid=196608
uid=0 gid=0 cuid=0 cgid=0
mode=0600, access_perms=0600
nsems = 1
otime = Tue Mar 15 11:44:49 2011
ctime = Tue Mar 15 11:43:38 2011
semnum value ncount zcount pid
0 1 0 0 2791 注:
cuid=0列出建立這個訊號量集的使用者ID.
cgid=0列出建立這個訊號量集的組ID.
mode=0600列出建立這個訊號量集時的許可權.
access_perms=0600列出這個訊號量集的存取權限.
otime = Tue Mar 15 11:44:49 2011列出這個訊號量集的訪問操作時間,如semop函數對訊號量集的操作.
ctime = Tue Mar 15 11:43:38 2011列出這個訊號量集的建立時間,如semget函數建立這個訊號量集.
semnum列出了訊號量集中訊號量的序列,如果我們在semget函數中指定了兩個訊號量,這裡的輸出,將會是下面的資訊:
semnum value ncount zcount pid
0 8 0 0 3270
1 0 0 0 0
ncount列出等待訊號量增加的進程的個數.
例如我們指定op為負值,此時負值的絕對值大於當前的訊號量值,這時將會阻塞,也就是等待資源的進程數會增加,如下:
./sysv_sem -6
semaphore exists
此時阻塞.我們在另一個終端下查看當前的訊號量集,如下:
ipcs -s -i 425984Semaphore Array semid=425984
uid=0 gid=0 cuid=0 cgid=0
mode=0600, access_perms=0600
nsems = 2
otime = Tue Mar 15 12:14:06 2011
ctime = Tue Mar 15 12:08:15 2011
semnum value ncount zcount pid
0 0 1 0 3337
1 0 0 0 0
此時等待訊號量增加的進程個數為1,即ncount為1,表示有一個進程等待訊號量值增加.zcount列出正在等待訊號量變成零的進程的個數
例如我們使當前的訊號量值大於0,此時指定op的值為0,這時將會阻塞,直到這個訊號量變為0,在阻塞期間等待訊號量變成零的進程個數就是zcount,如下:
增加訊號量值為1.
./sysv_sem 1
semaphore exists
Operation 1 done
semid 425984 value 0再次運行sysv_sem程式,指定op為0
./sysv_sem 0
semaphore exists
此時阻塞.我們在另一個終端查看當前的訊號量集,如下:
ipcs -s -i 425984Semaphore Array semid=425984
uid=0 gid=0 cuid=0 cgid=0
mode=0600, access_perms=0600
nsems = 2
otime = Tue Mar 15 12:23:19 2011
ctime = Tue Mar 15 12:08:15 2011
semnum value ncount zcount pid
0 1 0 1 3499
1 0 0 0 0
此時等待訊號量變成零的進程個數為1,即zcount為1,表示有一個進程等待訊號量值變為零.最後我們可以修改/proc/sys/kernel/sem,來達到修改訊號量最大數及相關限制的目的.例如:
cat /proc/sys/kernel/sem
250 32000 32 128
第一列,表示每個訊號集中的最大訊號量數目.
第二列,表示系統範圍內的最大訊號量總數目.
第三列,表示每個訊號發生時的最大系統運算元目.
第四列,表示系統範圍內的最大訊號集總數目.