linux調試工具ipcs的深入分析

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 294912
Message 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 196608
Semaphore 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 425984
Semaphore 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 425984
Semaphore 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
第一列,表示每個信號集中的最大信號量數目.
第二列,表示系統範圍內的最大信號量總數目.
第三列,表示每個信號發生時的最大系統操作數目.
第四列,表示系統範圍內的最大信號集總數目.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章