linux共享內存

linux共享內存函數說明
1 int shmget(key_t key, size_t size, int shmflg)
  函數功能:得到一個共享內存標識符或創建一個共享內存對象並返回共享內存標識符
  參數說明:key :IPC_PRIVATE 創建一個共享內存,這個共享內存智能在父子進程間共享,父進程創建共享內存必須在fork子進程之前,否則不能在父子進程之間共享。
                                          非IPC_PRIVATE,創建的共享內存可以在不同的進程間共享。
                          size :要創建的共享內存的大小
                          shmflg:IPC_CREAT:當shmflg&IPC_CREAT爲真時,如果內核中不存在鍵值與key相等的共享內存,則新建一個共享內存;如果存在這樣的共享內存,返回此共享內存的標識符
                                  IPC_CREAT|IPC_EXCL:如果內核中不存在鍵值 與key相等的共享內存,則新建一個共享內存;如果存在這樣的共享內存則報錯
                                  注意IPC_CREAT|IPC_EXCL或者IPC_CREAT必須要和訪問模式一起使用。
  返回值:成功返回共享內存的標識符,失敗返回 -1.
  
2 void *shmat(int shmid, const void *shmaddr, int shmflg)
  函數功能:連接共享內存標識符爲shmid的共享內存,連接成功後把共享內存區對象映射到調用進程的地址空間,隨後可像本地空間一樣訪問
  參數說明:shmid :共享內存標識符
            shmaddr:指定共享內存出現在進程內存地址的什麼位置,通常直接指定爲NULL讓內核自己決定一個合適的地址位置
            shmflg :SHM_RDONLY:爲只讀模式,0 爲讀寫模式
  返回值:成功返回映射的地址,失敗返回-1
  
  
3 int shmdt(const void *shmaddr)
  函數功能:與shmat函數相反,是用來斷開與共享內存附加點的地址,禁止本進程訪問此片共享內存
  參數說明:shmaddr :連接的共享內存的起始地址
  返回值:成功返回0 失敗返回-1
  注意:調用這個函數只是使之前連接的內存無效,但是不會刪除分配的共享內存。
        在進程結束的時候,即使沒有調用這個函數,也會將已經連接的地址斷開。
        
4 int shmctl(int shmid, int cmd, struct shmid_ds *buf)
  函數功能:完成對共享內存的控制
  參數說明:shmid :共享內存標識符
            cmd : IPC_STAT:得到共享內存的狀態,把共享內存的shmid_ds結構複製到buf中
                   PC_SET:改變共享內存的狀態,把buf所指的shmid_ds結構中的uid、gid、mode複製到共享內存的shmid_ds結構內
                   IPC_RMID:刪除共享內存
  返回值:成功返回0 失敗返回-1
  
示例代碼:
int FirstCreate ;
key_t key; 
int shmid;
void *ptr;
key=ftok(".",'a');
shmid = shmget(key,  MAX_SIZE, IPC_CREAT|IPC_EXCL|0666);
if(shmid<0)
{
        shmid = shmget(key, sizeof(DeviveInfo)*MAX_DEV, IPC_CREAT|0666);
        if(         shmid<0 )
        {
                return -1;
        }
        else
        {
                FirstCreate = 0;
        }
}
else
{
        FirstCreate = 1;
}
ptr = shmat(shmid, NULL, 0);
if(ptr==0xffffffff)
{
        ptr=NULL;
        printf("create share mem error\n");
        return -1;
}
if(FirstCreate)
{
        //第一次創建,初始化ptr
}
// 操作共享內存
if(ptr)
{
        shmdt(ptr);
}
shmctl(shmid,IPC_RMID,NULL);


通過上面的代碼就可以創建共享內存了。


但是在Linux中並沒有對共享內存提供同步機制,也就是說需要我們通過信號量來進行控制。
1 int semget(key_t key, int nsems, int semflg)
函數功能:得到一個信號量集標識符或創建一個信號量集對象並返回信號量集標識符
參數說明:key:IPC_PRIVATE 創建一個信號量,只能在父子進程間訪問
               非IPC_PRIVATE,創建一個信號量,可以在不同進程間訪問
          nsems:要創建信號量的個數
          msgflg:標識符,和shmget類似,參見shmget的說明
返回值:成功返回標識符,失敗返回-1


2 int semctl(int semid, int semnum, int cmd, union semun arg)
函數說明:對信號量的控制,或者獲取狀態
參數說明:semid :信號量集標識符
          semnum:信號量集數組上的下標,表示某一個信號量,例如semget的參數nsems設置爲1,semnum只能爲1。如果nsems設置爲2,semnum只能爲0、1。
          cmd :IPC_STAT 從信號量集上檢索semid_ds結構,並存到semun聯合體參數的成員buf的地址中
                IPC_SET  設置一個信號量集合的semid_ds結構中ipc_perm域的值,並從semun的buf中取出值
                IPC_RMID 從內核中刪除信號量集合
                GETALL   從信號量集合中獲得所有信號量的值,並把其整數值存到semun聯合體成員的一個指針數組中
                GETNCNT  返回當前等待資源的進程個數
                GETPID   返回最後一個執行系統調用semop()進程的PID
                GETVAL   返回信號量集合內單個信號量的值
                GETZCNT  返回當前等待100%資源利用的進程個數
                SETALL   與GETALL正好相反
                SETVAL   用聯合體中val成員的值設置信號量集合中單個信號量的值
         arg :cmd對應的參數必須是下面的聯合體
                       union semun  
                                {  
                                    int val;  
                                    struct semid_ds *buf;  
                                            unsigned short *arry;  
                                    struct seminfo *_buf;
                                };
返回值:成功返回0 失敗返回-1
                                
3 int semop(int semid, struct sembuf *sops, unsigned nsops)
函數說明:對信號量集標識符爲semid中的一個或多個信號量進行P操作或V操作
參數說明:semid :信號量集標識符
          sops  :指向進行操作的信號量集結構體數組的首地址,此結構的說明如下
               struct sembuf {
                       short semnum;  
                       short val; 
                       short flag; 
               };
               成員變量說明:
                   semnum:信號量集合中的信號量的下標
                   val:    若val>0進行V操作信號量值加val,表示進程釋放控制的資源 
                                 若val<0進行P操作信號量值減val,若(semval-val)<0(semval爲該信號量值),則調用進程阻塞,直到資源可用;若設置IPC_NOWAIT不會睡眠,進程直接返回EAGAIN錯誤
                                    若val==0時阻塞等待信號量爲0,調用進程進入睡眠狀態,直到信號值爲0;若設置IPC_NOWAIT,進程不會睡眠,直接返回EAGAIN錯誤
                  flag:    0 設置信號量的默認操作 
                                 IPC_NOWAIT設置信號量操作不等待*/
                                 SEM_UNDO 選項會讓內核記錄一個與調用進程相關的UNDO記錄,如果該進程崩潰,則根據這個進程的UNDO記錄自動恢復相應信號量的計數值。也就是相當於進程退出時會自動釋放進程佔用的鎖。
        nsops :參數sops的個數
返回值: 成功返回0
         失敗返回-1
        
4 int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout)
這個函數和semop的功能類似,只是增加了一個參數timeout,當timeout爲NULL時和semop的功能一樣,永遠不超時。如果timeout爲非0值,則超過timeout設置的時間會返回失敗。


示例代碼:
int semid;
int InitSem()
{
        key_t key; 
        key=ftok(".",'a');
        semid = semget( key, 1, IPC_CREAT|IPC_EXCL|0666 );  
        if(semid<0)
        {
                semid = semget( key, 1, IPC_CREAT|0666 );  
                if(semid<0)
                {
                        return -1;
                }
                
        }
        else
        {
                union semun para;  
  
            para.val = 1;  
            if(semctl(semid, 0, SETVAL, para) <0)
            {  
                return -1;  
        }
        }
        return 0;
}


int semLock()  
{  
    //對信號量做減1操作,即等待P(sv)  
    struct timespec ts;
    struct sembuf sem_b;  
    sem_b.sem_num = 0;  
    sem_b.sem_op = -1;//P()  
    sem_b.sem_flg = SEM_UNDO; 
    //clock_gettime(CLOCK_REALTIME, &ts);
    ts.tv_sec = 1;//設置1S超時
    ts.tv_nsec = 0;
    //if(semop(msgctrl.semid, &sem_b, 1) == -1)  
    if(semtimedop(msgctrl.semid, &sem_b, 1,&ts) !=0)  
    {  
        printf("semaphore lock timeout\n");  
        return -1;  
    }  
    return 0;  



int semUnlock()  
{  
    //這是一個釋放操作,它使信號量變爲可用,即發送信號V(sv)  
    struct sembuf sem_b;  
    sem_b.sem_num = 0;  
    sem_b.sem_op = 1;//V()  
    sem_b.sem_flg = SEM_UNDO;  
    if(semop(msgctrl.semid, &sem_b, 1) == -1)  
    {  
        printf( "semaphore_v failed\n");  
        return -1;  
    }  
    return 0;  
}  
  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章