/* Obsolete, used only for backwards compatibility and libc5 compiles */ struct semid_ds { struct ipc_perm sem_perm; /* 對信號操作的許可權 */ __kernel_time_t sem_otime; /*對信號操作的最後時間 */ __kernel_time_t sem_ctime; /*對信號進行修改的最後時間 */ struct sem *sem_base; /*指向第一個信號 */ struct sem_queue *sem_pending; /* 等待處理的掛起操作 */ struct sem_queue **sem_pending_last; /* 最後一個正在掛起的操作 */ struct sem_undo *undo; /* 撤銷的請求 */ unsigned short sem_nsems; /* 數組中的信號數 */ };
|
(1)Linux下使用系統函數創建和打開信號集.這個函數定義在頭文件sys/sem.h中,函數原型如下:
int semget(key_t key, int nsems, int semflg);
該函數執行成功則返回一個信號集的標識符,失敗返回-1。函數第一個參數由ftok()得到鍵值。第二個參數nsems指明要創建的信號集包含的信號個數,如果只是打開信號集,把nsems設置爲0即可;第三個參數semflg爲操作標誌,可以取如下值。
IPC_CREAT:調用semget時,它會將此值與其他信號集的key進行比較,如果存在相同的Key,說明信號集已經存在,此時返回給信號集的標識符,否則新建一個信號集並返回其標識符。
IPC_EXCL:該宏須和IPC_CREAT一起使用。使用IPC_CREAT|IPC_EXCL時,表示如果發現信號集已經存在,則返回錯誤,錯誤碼是EEXIST。
(2)信號量的操作,信號量的值和相應資源的使用情況有關,當它的值大於0時,表示當前可用資源的數量,當它的值小於0時,其絕對值表示等待使用該資源的進程個數.信號量的值僅能由PV操作來改變。在Linux下,PV操作通過調用函數semop實現。該函數定義在文件/sys/sem.h,原型如下:
int semop(int semid, struct sembuf *sops, size_t nsops);
參數semid爲信號集的標識符;參數sops指向進行操作的結構體數組首地址;參數nsops指出將要進行操作的信號的個數。semop函數調用成功返回0,否則返回-1.
sops參數的定義如下:(linux/sem.h)
struct sembuf {
ushort sem_num; //信號在信號集的索引
short sem_op; //操作類型
short sem_flg; //操作標誌
};
表 1 |
sem_op的取值和意義 |
取值範圍 |
操作意義 |
sem_op > 0 |
信號加上sem_op.表示進程釋放控制的資源 |
sem_op = 0 |
如果沒有設置IPC_NOWAIT,則進程進入睡眠,直到信號值爲0;否則進程不會睡眠,直接返回EAGAIN |
sem_op < 0 |
信號加上sem_op的值,若沒有設置IPC_NOWAIT,則進程進入睡眠,直到資源可用。否則直接返回EAGAIN |
(3)信號量的控制,使用信號量時,往往需要對信號集進行一些控制操作,比如刪除信號集、對內核維護的信號集的數據結構semid_ds進行設置,獲取信號集中信號值等。通過semctl可以操作:(sys/sem.h)
int semctl(int semid, int semnum, int cmd,...);
函數中,參數semid爲信號集的標識符,參數semnum標識一個特定的信號;cmd指明控制操作的類型。最後的“...”說明函數的參數是可選的,它依賴於第3個參數cmd,它通過共用體變量semun選擇要操作的參數.semun定義在Linux/sem.h:
/* arg for semctl system calls. */ union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ unsigned short *array; /* array for GETALL & SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ void *__pad; };
|
以上各個字段含義如下:
1)val:僅用於SETVAL操作類型,設置某個信號的值等於val
2)buf:用於IPC_STAT和IPC_SET,存取semid_ds結構
3)array:用於SETALL和GETALL操作
4)buf:爲控制IPC_INFO提供的緩存
cmd的宏含義如下:
IPC_SET:對信號量的屬性進行設置
IPC_RNID:刪除semid指定的信號集
GETPID:返回最後一個執行semop操作的進程ID
GETVAL:返回信號集semnum指定信號的值。
GETALL:返回信號集中所用信號的值.
GETNCNT:返回正在等待資源的進程的數量.
GETZCNT:返回正在等待完全空閒資源的進程的數量.
SETVAL:設置信號集中semnum指定的信號的值
SETALL:設置信號集中所用信號的值.
共享內存:
共享內存就是分配一塊能被其它進程訪問的內存。每個共享內存段在內核中維護着一個內部結構shmid_ds:(linux/shm.h)
/* Obsolete, used only for backwards compatibility and libc5 compiles */ struct shmid_ds { struct ipc_perm shm_perm; /* 操作許可 */ int shm_segsz; /* 共享內存大小,字節爲單位 */ __kernel_time_t shm_atime; /* 最後一個進程訪問共享內存的時間 */ __kernel_time_t shm_dtime; /* 最後一個進程離開共享內存的時間 */ __kernel_time_t shm_ctime; /* 最後一次修改共享內存的時間 */ __kernel_ipc_pid_t shm_cpid; /* 創建共享內存的進程ID */ __kernel_ipc_pid_t shm_lpid; /* 最後操作共享內存的進程ID */ unsigned short shm_nattch; /* 當前使用該貢獻內存的進程數量 */ unsigned short shm_unused; /* compatibility */ void *shm_unused2; /* ditto - used by DIPC */ void *shm_unused3; /* unused */ };
|
(1)創建共享內存:(linux/shm.h)
int shmget(key_t key, size_t size, int shmglf);
參數key和shmflg可以參考semflg的參數。size是以字節爲單位指定的內存的大小。
(2)共享內存的操作:(linux/shm.h)
void *shmat (int shmid, const void *shmaddr, int shmflg);
在使用共享內存前,必須通過shmat函數將其附加到進程的地址空間。shmat調用成功後會返回一個指向共享內存區域的指針,使用該指針就可以訪問共享內存了。如果失敗返回-1。
shmat參數shmid是shmget的返回值。參數shmflg爲存取權限標誌;參數shmaddr爲共享內存的附加點。參數shmaddr不同取值情況說明如下:
1)如果爲空,則由內核選取一個空閒的內存區;否則,返回地址取決於調用者是否給shmflg設置了SHM_RND值,如果沒有指定,則共享內存區附加到由shmaddr指定的地址,否則附加地址爲shmaddr向下舍入一個共享內存底端邊界地址後的地址。
當進程結束使用共享內存時,要通過函數斷開與共享內存的鏈接。
(sys/shm.h):
int shmdt(const void *shmaddr);
參數shmaddr爲shmat的返回值,該函數調用成功後,返回0,否則返回-1。
(3)共享內存的控制。(sys/shm.h):
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數shmid爲共享內存區的標識符,即shmget函數的返回值.buf是指向shmid_ds結構體的指針;cmd爲操作標誌位,支持以下3種操作:
1)IPC_RMID : 從系統中刪除由shmid指向的共享內存區
2)IPC_SET:設置共享內存的shmid_ds結構
3)IPC_STAT:讀取共享內存區的shmid_ds機構,並將其存儲到buf指向的地址。
這裏是以簡單的讀者和寫者爲例,來學習信號量和共享內存。
整個程序的設計規定如下:
1、首先讓寫者獲取信號量,去寫臨界區,這是的臨界區就是共享內存。完成後釋放掉信號量。
2、當讀者獲取信號量後,就去讀臨界區的數據,讀出數據完成後,釋放掉信號量。
以上讀者和寫者分別是兩個進程,代碼如下:
src/shm_write.c (寫者代碼):
#include "shm_mem.h"
int main(int argc, char** argv) {
int semid, shmid;
char *shmaddr;
char write_str[SHM_SIZE];
char *ret;
if((shmid = creatshm(".", 57, SHM_SIZE)) == -1) //創建或者獲取共享內存
return -1;
/*建立進程和共享內存連接*/
if((shmaddr = shmat(shmid, (char*)0, 0)) == (char *)-1){
perror("attch shared memory error!\n");
exit(1);
}
if((semid = creatsem("./", 39, 1, 1)) == -1)//創建信號量
return -1;
while(1){
wait_sem(semid, 0);//等待信號量可以被獲取
sem_p(semid, 0); //獲取信號量
/***************寫共享內存***************************************************/
printf("write : ");
ret = fgets(write_str, 1024, stdin);
if(write_str[0] == '#') // '#'結束讀寫進程
break;
int len = strlen(write_str);
write_str[len] = '\0';
strcpy(shmaddr, write_str);
/****************************************************************************/
sem_v(semid, 0); //釋放信號量
usleep(1000); //本進程睡眠.
}
sem_delete(semid); //把semid指定的信號集從系統中刪除
deleteshm(shmid); //從系統中刪除shmid標識的共享內存
return 0; }
|
<script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/buttonLite.js#style=-1&uuid=&pophcol=3&lang=zh"></script>
<script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/bshareC0.js"></script>
閱讀(1210) | 評論(3) | 轉發(0) |