今天我們來講講進程間使用共享內存通信時為了確保數據的正確,如何進行同步?
在Linux中,進程間的共享內存通信需要通過同步機制來保證數據的正確性和一致性,常用的同步機制包括信號量、互斥鎖、條件變量等。
其中,使用信號量來同步進程間的共享內存訪問是一種常見的方法。每個共享內存區域可以關聯一個或多個信號量,以保護共享內存區域的讀寫操作。在訪問共享內存之前,進程需要獲取信號量的使用權,當完成讀寫操作后,再釋放信號量的使用權,以便其他進程可以訪問共享內存區域。
1、信號量同步
下面是一個簡單的示例程序,展示了如何使用信號量來同步共享內存區域的讀寫操作:
?
#include?#include? #include? #include? #include? #include? #define?SHM_SIZE?1024 #define?SEM_KEY?0x123456 //?定義聯合體,用于信號量操作 union?semun?{ ????int?val; ????struct?semid_ds?*buf; ????unsigned?short?*array; }; int?main()?{ ????int?shmid,?semid; ????char?*shmaddr; ????struct?sembuf?semops[2]; ????union?semun?semarg; ????//?創建共享內存區域 ????shmid?=?shmget(IPC_PRIVATE,?SHM_SIZE,?IPC_CREAT?|?0666); ????if?(shmid?==?-1)?{ ????????perror("shmget"); ????????exit(1); ????} ????//?將共享內存區域附加到進程地址空間中 ????shmaddr?=?shmat(shmid,?NULL,?0); ????if?(shmaddr?==?(char?*)?-1)?{ ????????perror("shmat"); ????????exit(1); ????} ????//?創建信號量 ????semid?=?semget(SEM_KEY,?1,?IPC_CREAT?|?0666); ????if?(semid?==?-1)?{ ????????perror("semget"); ????????exit(1); ????} ????//?初始化信號量值為1 ????semarg.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); ????} ????//?在共享內存中寫入數據 ????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); ????} ????//?從共享內存中讀取數據 ????printf("Received?message:?%s ",?shmaddr); ????//?釋放共享內存區域 ????if?(shmdt(shmaddr)?==?-1)?{ ????????perror("shmdt"); ????????exit(1); ????} ????//?刪除共享內存區域 ????if?(shmctl(shmid,?IPC_RMID,?NULL)?==?-1)?{ ????????perror("shmctl"); ????????exit(1); ????} ????//?刪除信號量 ????if?(semctl(semid,?0,?IPC_RMID,?semarg)?==?-1)?{ ????????perror("semctl"); ????????exit(1); ????} ????return?0;
?
在這個示例程序中,使用了System V信號量來同步共享內存的讀寫操作。程序首先創建一個共享內存區域,并將其附加到進程地址空間中。然后,使用semget()函數創建一個信號量,并將其初始化為1。在寫入共享內存數據之前,程序使用semop()函數等待信號量。一旦獲取了信號量的使用權,程序就可以在共享內存區域中寫入數據。寫入數據完成后,程序再次使用semop()函數釋放信號量的使用權。在讀取共享內存數據時,程序同樣需要等待信號量的使用權,讀取數據完成后,再次釋放信號量的使用權。
需要注意的是,使用信號量來同步共享內存訪問時,需要確保每個進程都按照一定的順序進行讀寫操作。否則,就可能出現死鎖等問題。因此,在設計進程間共享內存通信時,需要仔細考慮數據的讀寫順序,并采取合適的同步機制來確保數據的正確性和一致性。
2、互斥鎖同步
互斥量也是一種常用的同步機制,可以用來實現多個進程之間的共享內存訪問。在Linux中,可以使用pthread庫中的互斥量來實現進程間共享內存的同步。
下面是一個使用互斥量實現共享內存同步的示例程序:
?
#include?#include? #include? #include? #include? #include? #include? #define?SHM_SIZE?1024 //?共享內存結構體 typedef?struct?{ ????pthread_mutex_t?mutex; ????char?data[SHM_SIZE]; }?shm_data_t; int?main()?{ ????int?fd; ????shm_data_t?*shm_data; ????pthread_mutexattr_t?mutex_attr; ????pthread_mutex_t?*mutex; ???? ????//?打開共享內存文件 ????if?((fd?=?shm_open("/my_shm",?O_CREAT?|?O_RDWR,?0666))?==?-1)?{ ????????perror("shm_open"); ????????exit(1); ????} ????//?調整共享內存文件大小 ????if?(ftruncate(fd,?sizeof(shm_data_t))?==?-1)?{ ????????perror("ftruncate"); ????????exit(1); ????} ????//?將共享內存映射到進程地址空間中 ????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); ????//?創建互斥量 ????mutex?=?&(shm_data->mutex); ????pthread_mutex_init(mutex,?&mutex_attr); ????//?在共享內存中寫入數據 ????pthread_mutex_lock(mutex); ????sprintf(shm_data->data,?"Hello,?world!"); ????pthread_mutex_unlock(mutex); ????//?在共享內存中讀取數據 ????pthread_mutex_lock(mutex); ????printf("Received?message:?%s ",?shm_data->data); ????pthread_mutex_unlock(mutex); ????//?解除共享內存映射 ????if?(munmap(shm_data,?sizeof(shm_data_t))?==?-1)?{ ????????perror("munmap"); ????????exit(1); ????} ????//?刪除共享內存文件 ????if?(shm_unlink("/my_shm")?==?-1)?{ ????????perror("shm_unlink"); ????????exit(1); ????} ????return?0; }
?
在這個示例程序中,使用了pthread庫中的互斥量來同步共享內存的讀寫操作。程序首先創建一個共享內存文件,并將其映射到進程地址空間中。然后,使用pthread_mutex_init()函數創建一個互斥量,并將其初始化為共享內存中的一部分。在寫入共享內存數據之前,程序使用pthread_mutex_lock()函數等待互斥量。一旦獲取了互斥量的使用權,程序就可以在共享內存區域中寫入數據。寫入數據完成后,程序再次使用pthread_mutex_unlock()函數釋放互斥量的使用權。在讀取共享內存數據之前,程序再次使用pthread_mutex_lock()函數等待互斥量。一旦獲取了互斥量的使用權,程序就可以在共享內存區域中讀取數據。讀取數據完成后,程序再次使用pthread_mutex_unlock()函數釋放互斥量的使用權。最后,程序解除共享內存映射,并刪除共享內存文件。
使用互斥量來同步共享內存訪問有以下幾點注意事項:
1、互斥量需要初始化。在創建互斥量之前,需要使用pthread_mutexattr_init()函數初始化互斥量屬性,并使用pthread_mutexattr_setpshared()函數將互斥量屬性設置為PTHREAD_PROCESS_SHARED,以便多個進程可以共享互斥量。
2、在訪問共享內存之前,需要使用pthread_mutex_lock()函數獲取互斥量的使用權。一旦獲取了互斥量的使用權,程序才能訪問共享內存。在完成共享內存的訪問之后,需要使用pthread_mutex_unlock()函數釋放互斥量的使用權,以便其他進程可以訪問共享內存。
3、互斥量必須存儲在共享內存區域中。在創建互斥量時,需要將其初始化為共享內存區域中的一部分,以便多個進程可以訪問同一個互斥量。
4、程序必須保證互斥量的一致性。多個進程共享同一個互斥量時,必須保證互斥量的一致性。否則,可能會導致多個進程同時訪問共享內存區域,導致數據錯誤或者系統崩潰。
總之,使用互斥量來同步共享內存訪問可以有效地避免多個進程同時訪問共享內存區域的問題,從而保證數據的一致性和程序的穩定性。在實際編程中,需要根據具體的需求選擇不同的同步機制,以保證程序的正確性和效率。
3、條件變量同步
在Linux下,可以使用條件變量(Condition Variable)來實現多進程之間的同步。條件變量通常與互斥量(Mutex)結合使用,以便在共享內存區域中對數據進行同步訪問。
條件變量是一種線程同步機制,用于等待或者通知某個事件的發生。當某個進程需要等待某個事件發生時,它可以通過調用pthread_cond_wait()函數來阻塞自己,并將互斥量釋放。一旦事件發生,其他進程就可以通過調用pthread_cond_signal()或pthread_cond_broadcast()函數來通知等待線程。等待線程接收到通知后,會重新獲取互斥量,并繼續執行。
在共享內存通信中,可以使用條件變量來實現進程之間的同步。具體操作步驟如下:
初始化互斥量和條件變量。在創建共享內存之前,需要使用pthread_mutexattr_init()和pthread_condattr_init()函數分別初始化互斥量屬性和條件變量屬性。然后,需要使用pthread_mutexattr_setpshared()和pthread_condattr_setpshared()函數將互斥量屬性和條件變量屬性設置為PTHREAD_PROCESS_SHARED,以便多個進程可以共享它們。
等待條件變量。在讀取共享內存之前,程序可以使用pthread_cond_wait()函數等待條件變量。調用pthread_cond_wait()函數會自動釋放互斥量,并阻塞當前進程。一旦其他進程發送信號通知條件變量發生變化,等待線程就會重新獲得互斥量,并繼續執行。
發送信號通知條件變量變化。在向共享內存中寫入數據之后,程序可以使用pthread_cond_signal()或pthread_cond_broadcast()函數發送信號通知條件變量發生變化。調用pthread_cond_signal()函數會發送一個信號通知等待線程條件變量發生變化,而調用pthread_cond_broadcast()函數會向所有等待線程發送信號通知條件變量發生變化。
使用條件變量來同步共享內存訪問有以下幾點注意事項:
1、程序必須使用互斥量來保護共享內存。在使用條件變量之前,程序必須先獲取互斥量的使用權,以便保護共享內存區域中的數據不被多個進程同時訪問。
2、程序必須保證條件變量的一致性。多個進程共享同一個條件變量時,必須保證條件變量的一致性。否則,可能會導致多個進程同時訪問共享內存區域,導致數據錯誤或者系統崩潰。
3、程序必須正確使用條件變量。在使用條件變量時,需要正確地使用pthread_cond_wait()、pthread_cond_signal()和pthread_cond_broadcast()函數,否則可能會導致死鎖或者其他問題。
4、程序必須正確處理信號。當調用pthread_cond_wait()函數時,程序可能會因為接收到信號而提前返回,此時程序需要正確地處理信號。
下面是一個使用條件變量實現進程間共享內存同步的示例代碼:
?
#include?#include? #include? #include? #include? #include? #include? #define?SHM_SIZE?4096 #define?SHM_NAME?"/myshm" #define?SEM_NAME?"/mysem" typedef?struct?{ ????pthread_mutex_t?mutex; ????pthread_cond_t?cond; ????char?buffer[SHM_SIZE]; }?shm_t; int?main(int?argc,?char?*argv[])?{ ????int?fd,?pid; ????shm_t?*shm; ????pthread_mutexattr_t?mutex_attr; ????pthread_condattr_t?cond_attr; ????//?創建共享內存區域 ????fd?=?shm_open(SHM_NAME,?O_CREAT?|?O_RDWR,?S_IRUSR?|?S_IWUSR); ????if?(fd?0)?{ ????????perror("shm_open"); ????????exit(1); ????} ????//?設置共享內存大小 ????if?(ftruncate(fd,?sizeof(shm_t))?0)?{ ????????perror("ftruncate"); ????????exit(1); ????} ????//?將共享內存映射到進程地址空間 ????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); ????//?創建子進程 ????pid?=?fork(); ????if?(pid?0)?{ ????????perror("fork"); ????????exit(1); ????} ????if?(pid?==?0)?{ ????????//?子進程寫入共享內存 ????????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?{ ????????//?父進程讀取共享內存 ????????pthread_mutex_lock(&shm->mutex); ????????pthread_cond_wait(&shm->cond,?&shm->mutex); ????????printf("Received?message:?%s ",?shm->buffer); ????????pthread_mutex_unlock(&shm->mutex); ????} ????//?刪除共享內存 ????if?(shm_unlink(SHM_NAME)?0)?{ ????????perror("shm_unlink"); ????????exit(1); ????} ????return?0; }
?
在這個示例中,程序創建了一個名為"/myshm"的共享內存區域,并將其映射到進程地址空間中。然后,程序使用互斥量和條件變量來同步進程之間的訪問共享內存區域。具體來說,父進程首先鎖定互斥量,然后等待條件變量的信號。子進程等待一秒鐘后,鎖定互斥量,將"Hello, world!"字符串寫入共享內存區域,然后發送條件變量信號,并釋放互斥量。此時,父進程將收到條件變量信號并鎖定互斥量,讀取共享內存區域中的內容,并釋放互斥量。
需要注意的是,在使用條件變量時,我們需要遵循一些規則來保證程序的正確性,如在等待條件變量時必須鎖定互斥量,并使用while循環來檢查條件變量的值是否滿足要求,等待條件變量信號的線程必須在等待之前鎖定互斥量,在等待之后解鎖互斥量,等待條件變量信號的線程可能會因為接收到信號而提前返回等等。
總之,使用互斥量和條件變量來實現進程間共享內存通信的同步,需要我們仔細考慮程序中所有可能出現的情況,并正確地使用互斥量和條件變量函數來同步進程之間的訪問。
小結
好了,這次我們通過Linux下進程間共享內存通信方式講解了常用的同步機制:信號量、互斥鎖、條件變量。希望對小伙伴們在日常的編程當中有所幫助。
評論
查看更多