今天我們來講講進(jìn)程間使用共享內(nèi)存通信時為了確保數(shù)據(jù)的正確,如何進(jìn)行同步?
在Linux中,進(jìn)程間的共享內(nèi)存通信需要通過同步機制來保證數(shù)據(jù)的正確性和一致性,常用的同步機制包括信號量、互斥鎖、條件變量等。
其中,使用信號量來同步進(jìn)程間的共享內(nèi)存訪問是一種常見的方法。每個共享內(nèi)存區(qū)域可以關(guān)聯(lián)一個或多個信號量,以保護(hù)共享內(nèi)存區(qū)域的讀寫操作。在訪問共享內(nèi)存之前,進(jìn)程需要獲取信號量的使用權(quán),當(dāng)完成讀寫操作后,再釋放信號量的使用權(quán),以便其他進(jìn)程可以訪問共享內(nèi)存區(qū)域。
(資料圖片僅供參考)
1、信號量同步
下面是一個簡單的示例程序,展示了如何使用信號量來同步共享內(nèi)存區(qū)域的讀寫操作:
#include#include #include #include #include #include #defineSHM_SIZE1024#defineSEM_KEY0x123456//定義聯(lián)合體,用于信號量操作unionsemun{intval;structsemid_ds*buf;unsignedshort*array;};intmain(){intshmid,semid;char*shmaddr;structsembufsemops[2];unionsemunsemarg;//創(chuàng)建共享內(nèi)存區(qū)域shmid=shmget(IPC_PRIVATE,SHM_SIZE,IPC_CREAT|0666);if(shmid==-1){perror("shmget");exit(1);}//將共享內(nèi)存區(qū)域附加到進(jìn)程地址空間中shmaddr=shmat(shmid,NULL,0);if(shmaddr==(char*)-1){perror("shmat");exit(1);}//創(chuàng)建信號量semid=semget(SEM_KEY,1,IPC_CREAT|0666);if(semid==-1){perror("semget");exit(1);}//初始化信號量值為1semarg.val=1;if(semctl(semid,0,SETVAL,semarg)==-1){perror("semctl");exit(1);}//等待信號量semops[0].sem_num=0;semops[0].sem_op=0;semops[0].sem_flg=0;if(semop(semid,semops,1)==-1){perror("semop");exit(1);}//在共享內(nèi)存中寫入數(shù)據(jù)strncpy(shmaddr,"Hello,world!",SHM_SIZE);//釋放信號量semops[0].sem_num=0;semops[0].sem_op=1;semops[0].sem_flg=0;if(semop(semid,semops,1)==-1){perror("semop");exit(1);}//等待信號量semops[0].sem_num=0;semops[0].sem_op=0;semops[0].sem_flg=0;if(semop(semid,semops,1)==-1){perror("semop");exit(1);}//從共享內(nèi)存中讀取數(shù)據(jù)printf("Receivedmessage:%s",shmaddr);//釋放共享內(nèi)存區(qū)域if(shmdt(shmaddr)==-1){perror("shmdt");exit(1);}//刪除共享內(nèi)存區(qū)域if(shmctl(shmid,IPC_RMID,NULL)==-1){perror("shmctl");exit(1);}//刪除信號量if(semctl(semid,0,IPC_RMID,semarg)==-1){perror("semctl");exit(1);}return0;
在這個示例程序中,使用了System V信號量來同步共享內(nèi)存的讀寫操作。程序首先創(chuàng)建一個共享內(nèi)存區(qū)域,并將其附加到進(jìn)程地址空間中。然后,使用semget()函數(shù)創(chuàng)建一個信號量,并將其初始化為1。在寫入共享內(nèi)存數(shù)據(jù)之前,程序使用semop()函數(shù)等待信號量。一旦獲取了信號量的使用權(quán),程序就可以在共享內(nèi)存區(qū)域中寫入數(shù)據(jù)。寫入數(shù)據(jù)完成后,程序再次使用semop()函數(shù)釋放信號量的使用權(quán)。在讀取共享內(nèi)存數(shù)據(jù)時,程序同樣需要等待信號量的使用權(quán),讀取數(shù)據(jù)完成后,再次釋放信號量的使用權(quán)。
需要注意的是,使用信號量來同步共享內(nèi)存訪問時,需要確保每個進(jìn)程都按照一定的順序進(jìn)行讀寫操作。否則,就可能出現(xiàn)死鎖等問題。因此,在設(shè)計進(jìn)程間共享內(nèi)存通信時,需要仔細(xì)考慮數(shù)據(jù)的讀寫順序,并采取合適的同步機制來確保數(shù)據(jù)的正確性和一致性。
2、互斥鎖同步
互斥量也是一種常用的同步機制,可以用來實現(xiàn)多個進(jìn)程之間的共享內(nèi)存訪問。在Linux中,可以使用pthread庫中的互斥量來實現(xiàn)進(jìn)程間共享內(nèi)存的同步。
下面是一個使用互斥量實現(xiàn)共享內(nèi)存同步的示例程序:
#include#include #include #include #include #include #include #defineSHM_SIZE1024//共享內(nèi)存結(jié)構(gòu)體typedefstruct{pthread_mutex_tmutex;chardata[SHM_SIZE];}shm_data_t;intmain(){intfd;shm_data_t*shm_data;pthread_mutexattr_tmutex_attr;pthread_mutex_t*mutex;//打開共享內(nèi)存文件if((fd=shm_open("/my_shm",O_CREAT|O_RDWR,0666))==-1){perror("shm_open");exit(1);}//調(diào)整共享內(nèi)存文件大小if(ftruncate(fd,sizeof(shm_data_t))==-1){perror("ftruncate");exit(1);}//將共享內(nèi)存映射到進(jìn)程地址空間中if((shm_data=mmap(NULL,sizeof(shm_data_t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0))==MAP_FAILED){perror("mmap");exit(1);}//初始化互斥量屬性pthread_mutexattr_init(&mutex_attr);pthread_mutexattr_setpshared(&mutex_attr,PTHREAD_PROCESS_SHARED);//創(chuàng)建互斥量mutex=&(shm_data->mutex);pthread_mutex_init(mutex,&mutex_attr);//在共享內(nèi)存中寫入數(shù)據(jù)pthread_mutex_lock(mutex);sprintf(shm_data->data,"Hello,world!");pthread_mutex_unlock(mutex);//在共享內(nèi)存中讀取數(shù)據(jù)pthread_mutex_lock(mutex);printf("Receivedmessage:%s",shm_data->data);pthread_mutex_unlock(mutex);//解除共享內(nèi)存映射if(munmap(shm_data,sizeof(shm_data_t))==-1){perror("munmap");exit(1);}//刪除共享內(nèi)存文件if(shm_unlink("/my_shm")==-1){perror("shm_unlink");exit(1);}return0;}
在這個示例程序中,使用了pthread庫中的互斥量來同步共享內(nèi)存的讀寫操作。程序首先創(chuàng)建一個共享內(nèi)存文件,并將其映射到進(jìn)程地址空間中。然后,使用pthread_mutex_init()函數(shù)創(chuàng)建一個互斥量,并將其初始化為共享內(nèi)存中的一部分。在寫入共享內(nèi)存數(shù)據(jù)之前,程序使用pthread_mutex_lock()函數(shù)等待互斥量。一旦獲取了互斥量的使用權(quán),程序就可以在共享內(nèi)存區(qū)域中寫入數(shù)據(jù)。寫入數(shù)據(jù)完成后,程序再次使用pthread_mutex_unlock()函數(shù)釋放互斥量的使用權(quán)。在讀取共享內(nèi)存數(shù)據(jù)之前,程序再次使用pthread_mutex_lock()函數(shù)等待互斥量。一旦獲取了互斥量的使用權(quán),程序就可以在共享內(nèi)存區(qū)域中讀取數(shù)據(jù)。讀取數(shù)據(jù)完成后,程序再次使用pthread_mutex_unlock()函數(shù)釋放互斥量的使用權(quán)。最后,程序解除共享內(nèi)存映射,并刪除共享內(nèi)存文件。
使用互斥量來同步共享內(nèi)存訪問有以下幾點注意事項:
1、互斥量需要初始化。在創(chuàng)建互斥量之前,需要使用pthread_mutexattr_init()函數(shù)初始化互斥量屬性,并使用pthread_mutexattr_setpshared()函數(shù)將互斥量屬性設(shè)置為PTHREAD_PROCESS_SHARED,以便多個進(jìn)程可以共享互斥量。
2、在訪問共享內(nèi)存之前,需要使用pthread_mutex_lock()函數(shù)獲取互斥量的使用權(quán)。一旦獲取了互斥量的使用權(quán),程序才能訪問共享內(nèi)存。在完成共享內(nèi)存的訪問之后,需要使用pthread_mutex_unlock()函數(shù)釋放互斥量的使用權(quán),以便其他進(jìn)程可以訪問共享內(nèi)存。
3、互斥量必須存儲在共享內(nèi)存區(qū)域中。在創(chuàng)建互斥量時,需要將其初始化為共享內(nèi)存區(qū)域中的一部分,以便多個進(jìn)程可以訪問同一個互斥量。
4、程序必須保證互斥量的一致性。多個進(jìn)程共享同一個互斥量時,必須保證互斥量的一致性。否則,可能會導(dǎo)致多個進(jìn)程同時訪問共享內(nèi)存區(qū)域,導(dǎo)致數(shù)據(jù)錯誤或者系統(tǒng)崩潰。
總之,使用互斥量來同步共享內(nèi)存訪問可以有效地避免多個進(jìn)程同時訪問共享內(nèi)存區(qū)域的問題,從而保證數(shù)據(jù)的一致性和程序的穩(wěn)定性。在實際編程中,需要根據(jù)具體的需求選擇不同的同步機制,以保證程序的正確性和效率。
3、條件變量同步
在Linux下,可以使用條件變量(Condition Variable)來實現(xiàn)多進(jìn)程之間的同步。條件變量通常與互斥量(Mutex)結(jié)合使用,以便在共享內(nèi)存區(qū)域中對數(shù)據(jù)進(jìn)行同步訪問。
條件變量是一種線程同步機制,用于等待或者通知某個事件的發(fā)生。當(dāng)某個進(jìn)程需要等待某個事件發(fā)生時,它可以通過調(diào)用pthread_cond_wait()函數(shù)來阻塞自己,并將互斥量釋放。一旦事件發(fā)生,其他進(jìn)程就可以通過調(diào)用pthread_cond_signal()或pthread_cond_broadcast()函數(shù)來通知等待線程。等待線程接收到通知后,會重新獲取互斥量,并繼續(xù)執(zhí)行。
在共享內(nèi)存通信中,可以使用條件變量來實現(xiàn)進(jìn)程之間的同步。具體操作步驟如下:
初始化互斥量和條件變量。在創(chuàng)建共享內(nèi)存之前,需要使用pthread_mutexattr_init()和pthread_condattr_init()函數(shù)分別初始化互斥量屬性和條件變量屬性。然后,需要使用pthread_mutexattr_setpshared()和pthread_condattr_setpshared()函數(shù)將互斥量屬性和條件變量屬性設(shè)置為PTHREAD_PROCESS_SHARED,以便多個進(jìn)程可以共享它們。
等待條件變量。在讀取共享內(nèi)存之前,程序可以使用pthread_cond_wait()函數(shù)等待條件變量。調(diào)用pthread_cond_wait()函數(shù)會自動釋放互斥量,并阻塞當(dāng)前進(jìn)程。一旦其他進(jìn)程發(fā)送信號通知條件變量發(fā)生變化,等待線程就會重新獲得互斥量,并繼續(xù)執(zhí)行。
發(fā)送信號通知條件變量變化。在向共享內(nèi)存中寫入數(shù)據(jù)之后,程序可以使用pthread_cond_signal()或pthread_cond_broadcast()函數(shù)發(fā)送信號通知條件變量發(fā)生變化。調(diào)用pthread_cond_signal()函數(shù)會發(fā)送一個信號通知等待線程條件變量發(fā)生變化,而調(diào)用pthread_cond_broadcast()函數(shù)會向所有等待線程發(fā)送信號通知條件變量發(fā)生變化。
使用條件變量來同步共享內(nèi)存訪問有以下幾點注意事項:
1、程序必須使用互斥量來保護(hù)共享內(nèi)存。在使用條件變量之前,程序必須先獲取互斥量的使用權(quán),以便保護(hù)共享內(nèi)存區(qū)域中的數(shù)據(jù)不被多個進(jìn)程同時訪問。
2、程序必須保證條件變量的一致性。多個進(jìn)程共享同一個條件變量時,必須保證條件變量的一致性。否則,可能會導(dǎo)致多個進(jìn)程同時訪問共享內(nèi)存區(qū)域,導(dǎo)致數(shù)據(jù)錯誤或者系統(tǒng)崩潰。
3、程序必須正確使用條件變量。在使用條件變量時,需要正確地使用pthread_cond_wait()、pthread_cond_signal()和pthread_cond_broadcast()函數(shù),否則可能會導(dǎo)致死鎖或者其他問題。
4、程序必須正確處理信號。當(dāng)調(diào)用pthread_cond_wait()函數(shù)時,程序可能會因為接收到信號而提前返回,此時程序需要正確地處理信號。
下面是一個使用條件變量實現(xiàn)進(jìn)程間共享內(nèi)存同步的示例代碼:
#include#include #include #include #include #include #include #defineSHM_SIZE4096#defineSHM_NAME"/myshm"#defineSEM_NAME"/mysem"typedefstruct{pthread_mutex_tmutex;pthread_cond_tcond;charbuffer[SHM_SIZE];}shm_t;intmain(intargc,char*argv[]){intfd,pid;shm_t*shm;pthread_mutexattr_tmutex_attr;pthread_condattr_tcond_attr;//創(chuàng)建共享內(nèi)存區(qū)域fd=shm_open(SHM_NAME,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);if(fd<0){perror("shm_open");exit(1);}//設(shè)置共享內(nèi)存大小if(ftruncate(fd,sizeof(shm_t))<0){perror("ftruncate");exit(1);}//將共享內(nèi)存映射到進(jìn)程地址空間shm=mmap(NULL,sizeof(shm_t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if(shm==MAP_FAILED){perror("mmap");exit(1);}//初始化互斥量屬性和條件變量屬性pthread_mutexattr_init(&mutex_attr);pthread_condattr_init(&cond_attr);pthread_mutexattr_setpshared(&mutex_attr,PTHREAD_PROCESS_SHARED);pthread_condattr_setpshared(&cond_attr,PTHREAD_PROCESS_SHARED);//初始化互斥量和條件變量pthread_mutex_init(&shm->mutex,&mutex_attr);pthread_cond_init(&shm->cond,&cond_attr);//創(chuàng)建子進(jìn)程pid=fork();if(pid<0){perror("fork");exit(1);}if(pid==0){//子進(jìn)程寫入共享內(nèi)存sleep(1);pthread_mutex_lock(&shm->mutex);sprintf(shm->buffer,"Hello,world!");pthread_cond_signal(&shm->cond);pthread_mutex_unlock(&shm->mutex);exit(0);}else{//父進(jìn)程讀取共享內(nèi)存pthread_mutex_lock(&shm->mutex);pthread_cond_wait(&shm->cond,&shm->mutex);printf("Receivedmessage:%s",shm->buffer);pthread_mutex_unlock(&shm->mutex);}//刪除共享內(nèi)存if(shm_unlink(SHM_NAME)<0){perror("shm_unlink");exit(1);}return0;}
在這個示例中,程序創(chuàng)建了一個名為"/myshm"的共享內(nèi)存區(qū)域,并將其映射到進(jìn)程地址空間中。然后,程序使用互斥量和條件變量來同步進(jìn)程之間的訪問共享內(nèi)存區(qū)域。具體來說,父進(jìn)程首先鎖定互斥量,然后等待條件變量的信號。子進(jìn)程等待一秒鐘后,鎖定互斥量,將"Hello, world!"字符串寫入共享內(nèi)存區(qū)域,然后發(fā)送條件變量信號,并釋放互斥量。此時,父進(jìn)程將收到條件變量信號并鎖定互斥量,讀取共享內(nèi)存區(qū)域中的內(nèi)容,并釋放互斥量。
需要注意的是,在使用條件變量時,我們需要遵循一些規(guī)則來保證程序的正確性,如在等待條件變量時必須鎖定互斥量,并使用while循環(huán)來檢查條件變量的值是否滿足要求,等待條件變量信號的線程必須在等待之前鎖定互斥量,在等待之后解鎖互斥量,等待條件變量信號的線程可能會因為接收到信號而提前返回等等。
總之,使用互斥量和條件變量來實現(xiàn)進(jìn)程間共享內(nèi)存通信的同步,需要我們仔細(xì)考慮程序中所有可能出現(xiàn)的情況,并正確地使用互斥量和條件變量函數(shù)來同步進(jìn)程之間的訪問。
小結(jié)
好了,這次我們通過Linux下進(jìn)程間共享內(nèi)存通信方式講解了常用的同步機制:信號量、互斥鎖、條件變量。希望對小伙伴們在日常的編程當(dāng)中有所幫助。
審核編輯:湯梓紅標(biāo)簽: