在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            博客專欄

            EEPW首頁 > 博客 > 動態(tài)庫中的函數(shù)實現(xiàn)互斥調(diào)用

            動態(tài)庫中的函數(shù)實現(xiàn)互斥調(diào)用

            發(fā)布人:電子禪石 時間:2021-12-09 來源:工程師 發(fā)布文章

            (1條消息) 動態(tài)庫中的函數(shù)實現(xiàn)互斥調(diào)用_盧平光的博客-CSDN博客_函數(shù)互斥


            一直在糾結(jié)一個問題:


            如果一個函數(shù)使用互斥鎖可以防止被調(diào)用時重入的情況,但是如果該函數(shù)以so的形式提供給使用者(其它進程),那么如何做到各進程間對于該函數(shù)的互斥調(diào)用呢?


            首先明確下前提:


            so被進程加載時,代碼段共享,但是所有變量(局部、全局、靜態(tài)變量)都是各進程copy一份私有使用。

            也就是說,想要在so內(nèi)實現(xiàn)一個不可重入的函數(shù)還是比較困難的,因為所有變量都是獨立的,但是考慮如下場景:驅(qū)動層給了一個視頻碼流錄制的接口,并且沒有在驅(qū)動層做互斥,但實際上這個接口同一時間只可能被一個進程調(diào)用,那么很明顯,串接到so中的接口必須實現(xiàn)該接口的原子調(diào)用。


            解決思路:


            由so創(chuàng)建一塊共享內(nèi)存,放置一份進程共享的互斥鎖

            第一步的動作需要尋找一個合適點自動完成,比如so加載時

            各進程在調(diào)用so接口時,接口內(nèi)部使用該共享鎖完成互斥調(diào)用

            ————————————————

            1、互斥鎖共享

            so中的互斥鎖屬于私有數(shù)據(jù),加載該so的進程都會拷貝一份,那么就無法在進程間共享使用該鎖。所以必須將鎖放到進程間的共享內(nèi)存中,確保鎖只有一把。首先定義一個鎖結(jié)構(gòu)體

            // 定義進程鎖結(jié)構(gòu)體
            typedef struct Mutex_Info {
                // 鎖以及狀態(tài)
                pthread_mutex_t lock;
                pthread_mutexattr_t lock_attr;
                // 在共享內(nèi)存中的標(biāo)識符
                int FLAG;
            } mutex_info_t;

            要想把鎖放到共享內(nèi)存,那么先創(chuàng)建一塊內(nèi)存

            /**
             *  返回一片共享內(nèi)存標(biāo)識符,用于后續(xù)獲取該共享內(nèi)存,以及銷毀該共享內(nèi)存
             *  INDEX_OF_KEY —— 自定義的該共享內(nèi)存序號
             *  LENGTH —— 共享內(nèi)存大小
             */
            const int create_sharemem(const int INDEX_OF_KEY, const unsigned int LENGTH) {
                // 生成key
                const char* FILE_PATH = "./";
                key_t key = ftok(FILE_PATH, INDEX_OF_KEY);
            
                // 創(chuàng)建共享內(nèi)存空間 
                //多個進程調(diào)用該函數(shù),只要確保KEY相同,那么只會創(chuàng)建同一塊內(nèi)存
                const int FLAG = shmget(key, LENGTH, IPC_CREAT | 0666);
                return FLAG;
            }

            其中的init函數(shù)是鎖的初始化,注意需要設(shè)置其進程間共享屬性

            const int init_mutex(void* pthis) {
                mutex_info_t* mp = (mutex_info_t*)pthis;
                // 初始化鎖狀態(tài),設(shè)置狀態(tài)狀態(tài)為——進程共享
                pthread_mutexattr_init(&(mp->lock_attr));
                pthread_mutexattr_setpshared(&(mp->lock_attr), PTHREAD_PROCESS_SHARED);
                // 用鎖狀態(tài)來初始化鎖
                pthread_mutex_init(&(mp->lock), &(mp->lock_attr));
                return 0;
            }

            有了以上兩個函數(shù),其實就可以寫一個so被加載時自動執(zhí)行的初始化函數(shù),這樣可以保證so的使用者不必關(guān)心內(nèi)存、鎖的創(chuàng)建、銷毀和使用。所以先插講下so加載時自動執(zhí)行函數(shù)的方法。


            2、so加載時自動執(zhí)行函數(shù)



            GNU C 的一大特色就是__attribute__ 機制。attribute 是一個編譯指令,可以設(shè)置函數(shù)屬性(Function Attribute )、變量屬性(Variable Attribute )和類型屬性(Type Attribute )。

            attribute 書寫特征是:attribute 前后都有兩個下劃線,并切后面會緊跟一對原括弧,括弧里面是相應(yīng)的__attribute__ 參數(shù)。

            若將某一函數(shù)的聲明中添加 __ attribute__((constructor)) 屬性,那么它具有兩種運行時機


            若函數(shù)所在源文件被編譯為可執(zhí)行文件,那么該函數(shù)可以在main函數(shù)執(zhí)行前被調(diào)用

            ————————————————

            #include <stdio.h>
            #include <stdlib.h>
            static int * g_count = NULL;
            __attribute__((constructor)) void load_file()
            {
                printf("Constructor is called.\n");
            }
            __attribute__((destructor)) void unload_file()
            {
                printf("destructor is called.\n");
            }
            
            int main()
            {
               printf ("this is main function\n");
                return 0;
            }

             注意正常退出才會自動執(zhí)行destructor 的修飾的函數(shù)。


            • 若函數(shù)所在源文件被編譯為共享庫,那么該函數(shù)可以在共享庫被其它進程顯式dlopen或者隱式由操作系統(tǒng)加載時都會優(yōu)先執(zhí)行

            • //process source file
              #include <stdio.h>
              #include <string.h>
              #include <dlfcn.h>
              
              int main(int argc, char **argv)
              {
              	void (*print)();
              	int (*add)(int, int);
              	void *handle;
              	
              	if (argc < 2)
              		return -1;
              	
              	handle = dlopen(argv[1], RTLD_LAZY);
              	if (!handle) {
              		printf("dlopen failed: %s\n", dlerror());
              		return -1;
              	}
              	
              	print = dlsym(handle, "print");
              	if (!print) {
              		printf("dlsym failed: %s\n", dlerror());
              		return -1;
              	}
              	print();
              	
              	add = dlsym(handle, "add");
              	if (!add) {
              		printf("dlsym failed: %s\n", dlerror());
              		return -1;
              	}
              	add(1, 2);
              	
              	dlclose(handle);
              	
              	return 0;
              }
              
              
              //libss.so source file
              
              #include <stdio.h>
              #include <string.h>
              
              void print() 
              {
              	printf("I am print\n");
              }
              
              int add(int a, int b)
              {
              	printf("Sum %d and %d is %d\n", a, b, a + b);
              	return 0;
              }
              //static void king() __attribute__((constructor(101))); the following is also right
              static __attribute__((constructor(101))) void king()
              {
              	printf("I am king\n");
              }


            gcc -Wall -shared -fPIC -o libss.so libss.c -ldl
            
            gcc -Wall -o udlopen udlopen.c
            
            ./udlopen libss.so
            
            I am king
            I am print
            Sum 1 and 2 is 3

            __ attribute__((constructor))的這一特性可以被用在許多場合。比如某個so的功能實現(xiàn)需要預(yù)先映射一塊共享內(nèi)存,如果要求所有使用該so的進程在加載時手動去做這一步驟是非常不合理的,此時可以將內(nèi)存映射實現(xiàn)放在__ attribute__((constructor))屬性聲明的函數(shù)中,這樣每次so被加載就會自動完成共享內(nèi)存的創(chuàng)建映射,對so的使用者完全透明,確實夠巧妙!

            ————————————————

            3、so加載時自動創(chuàng)建共享內(nèi)存與互斥鎖

            //全局變量
            mutex_info_t tPtr = NULL;
            __ attribute__((constructor)) static void pre_init(void)
            {
                  int flag = create_sharemem(127, sizeof(mutex_info_t ));
                  //NULL參數(shù)代表由操作系統(tǒng)選擇共享內(nèi)存中的合適位置返回給內(nèi)存申請者,測試發(fā)現(xiàn)由于共享內(nèi)存一共就只有mutex_info_t 大小,
                  //所以每次返回的地址相同 這也能保證so被多個進程加載使用的是同一把鎖
                  tPtr = (mutex_info_t *)shmat(FLAG, NULL, SHM_R | SHM_W);
                  //內(nèi)存內(nèi)結(jié)構(gòu)體的FLAG如果不是共享內(nèi)存的索引,那么表示是第一次申請內(nèi)存,需要對鎖初始化
                  //避免多次加載so時多次init鎖
                  if(tPtr->FLAG != flag)
                  {
            		 	tPtr->FLAG == flag;
            		 	init_mutex(tPtr);
            		 	printf("first make mem, init lock");
            	   }
            	   else
            	   {
            			printf("mem, lock has been init");
            	    }
            }
            
            __ attribute__((destructor)) static void late_destory(void)
            {
            	  struct shmid_ds shminfo;
                  //共享內(nèi)存有引用計數(shù),所以多次寫在so調(diào)用釋放共享內(nèi)存時,只有最后計數(shù)為0時才會真正釋放
            	  shmctl(tPtr->FLAG, IPC_RMID,NULL);
                  shmctl(tPtr->FLAG, IPC_STAT,&shminfo);
                  //雖然tPtr不能被進程共享,但是每個進程的SO唄加載時都會重新更新tPtr的值,所以可放心使用
                  //當(dāng)內(nèi)存引用計數(shù)變?yōu)?時(實際測試是1),代表so不被使用,可銷毀鎖
                  if(shminfo.shm_nattch == 1)
                  {
                        pthread_mutex_destory(&(tPtr->lock));
                        pthread_mutexattr_destory(&(tPtr->lock_attr));
                  }
            }

            有了保護機制,可以再寫一個接口,接口假設(shè)不可重入:

            void Asyncprint()
            {
            	pthread_mutex_lock(&(mp->lock));
            	printf("now you can call me");
            	sleep(10);
            	printf("all me finish!");
            	pthread_mutex_unlock(&(mp->lock));
            }

            以上函數(shù)模擬的場景是:Asyncprint執(zhí)行一次需要10s,中間不允許重入,so編譯方法:

            gcc lock.c -fPIC -shared -o liblock.so

            可以同時跑兩個相同進程調(diào)用該so的接口,看看接口是否互斥調(diào)用

            //main.c
            //gcc main.c -L. -llock -lpthread -o 1.exe
            //gcc main.c -L. -llock -lpthread -o 2.exe
            //可同時使用ipcs -m c查看共享內(nèi)存信息
            #include <stdio.h>
            #include "lock.h"
            int main()
            {
               Asyncprint();
            }

            同時運行1.exe 2.exe看打?。?/span>

            在這里插入圖片描述

            完整源碼:

            //main.c 
            //gcc lock.c -fPIC -shared -o liblock.so    編譯動態(tài)庫
            //gcc main.c -L.  -llock  -lpthread -o 1.out 編譯可執(zhí)行程序1
            //gcc main.c -L.  -llock  -lpthread -o 2.out 編譯可執(zhí)行程序2
            //ipcs -m shell中查看當(dāng)前shm情況
            #include <stdio.h>
            #include <sys/shm.h>
            #include "lock.h"
            int main() {
            
                //方式1 可執(zhí)行程序自己調(diào)用函數(shù)分配共享內(nèi)存,創(chuàng)建互斥鎖
            	//此時需刪除lock.c中的constructor與desstructor函數(shù)
                /*mutex_info_t * mp = create_mutex_package(111);
                shmctl(557060, IPC_RMID, NULL)
                asyncprint();
                destory_mutex_package(mp); 
            	*/
            	//方式2 內(nèi)存與互斥鎖均由so加載時,其自己的constructor與desstructor函數(shù)負責(zé)創(chuàng)建與銷毀
            	asyncprint();
            
            	return 0;
            
            }


            //lock.h
            #ifndef __LOCK_H_
            #define __LOCK_H_
            
            #include <pthread.h>
            
            
            typedef struct MUTEX_PACKAGE {
            
                pthread_mutex_t lock;
                pthread_mutexattr_t lock_attr;
            
                int FLAG;
            } mutex_info_t ;
            
            extern const void asyncprint();
            #endif


            lock.c
            
            //gcc lock.c -fPIC -shared -o liblock.so
            
            #include <unistd.h>
            #include <sys/types.h>
            #include <sys/ipc.h>
            #include <sys/shm.h>
            #include <assert.h>
            #include <pthread.h>
            #include <stdio.h>
            #include <stdlib.h>
            
            #include "lock.h"
            
            mutex_info_t * mp = NULL;
            int FLAG =0;
            /**
             *  返回一片共享內(nèi)存標(biāo)識符,用于后續(xù)獲取該共享內(nèi)存,以及銷毀該共享內(nèi)存
             *  INDEX_OF_KEY —— 自定義的該共享內(nèi)存序號
             *  LENGTH —— 共享內(nèi)存大小
             */
            const int create_sharemem(const int INDEX_OF_KEY, const unsigned int LENGTH) {
                // 生成key
                const char* FILE_PATH = "./";
                key_t key = ftok(FILE_PATH, INDEX_OF_KEY);
            
                // 創(chuàng)建共享內(nèi)存空間
                const int FLAG = shmget(key, LENGTH, IPC_CREAT | 0666);
            
                return FLAG;
            }
            
            
            
            // 初始化進程鎖結(jié)構(gòu)體
            const int init_mutex(void* pthis) {
                mutex_info_t * mp = (mutex_info_t *)pthis;
                // 初始化鎖狀態(tài),設(shè)置狀態(tài)狀態(tài)為——進程共享
                pthread_mutexattr_init(&(mp->lock_attr));
                pthread_mutexattr_setpshared(&(mp->lock_attr), PTHREAD_PROCESS_SHARED);
                // 用鎖狀態(tài)來初始化鎖
                pthread_mutex_init(&(mp->lock), &(mp->lock_attr));
            
                return 0;
            }
            
            
            __ attribute__((constructor)) static void pre_init(void)
            {
                  int flag = create_sharemem(127, sizeof(mutex_info_t ));
                  //NULL參數(shù)代表由操作系統(tǒng)選擇共享內(nèi)存中的合適位置返回給內(nèi)存申請者,測試發(fā)現(xiàn)由于共享內(nèi)存一共就只有mutex_info_t 大小,
                  //所以每次返回的地址相同 這也能保證so被多個進程加載使用的是同一把鎖
                  tPtr = (mutex_info_t *)shmat(FLAG, NULL, SHM_R | SHM_W);
                  //內(nèi)存內(nèi)結(jié)構(gòu)體的FLAG如果不是共享內(nèi)存的索引,那么表示是第一次申請內(nèi)存,需要對鎖初始化
                  //避免多次加載so時多次init鎖
                  if(tPtr->FLAG != flag)
                  {
            		 	tPtr->FLAG == flag;
            		 	init_mutex(tPtr);
            		 	printf("first make mem, init lock");
            	   }
            	   else
            	   {
            			printf("mem, lock has been init");
            	    }
            }
            
            __ attribute__((destructor)) static void late_destory(void)
            {
            	  struct shmid_ds shminfo;
                  //共享內(nèi)存有引用計數(shù),所以多次寫在so調(diào)用釋放共享內(nèi)存時,只有最后計數(shù)為0時才會真正釋放
            	  shmctl(tPtr->FLAG, IPC_RMID,NULL);
                  shmctl(tPtr->FLAG, IPC_STAT,&shminfo);
                  //雖然tPtr不能被進程共享,但是每個進程的SO唄加載時都會重新更新tPtr的值,所以可放心使用
                  //當(dāng)內(nèi)存引用計數(shù)變?yōu)?時(實際測試是1),代表so不被使用,可銷毀鎖
                  if(shminfo.shm_nattch == 1)
                  {
                        pthread_mutex_destory(&(tPtr->lock));
                        pthread_mutexattr_destory(&(tPtr->lock_attr));
                  }
            }



            原文鏈接:https://blog.csdn.net/ludashei2/article/details/115061988


            *博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點,如有侵權(quán)請聯(lián)系工作人員刪除。

            模擬電路文章專題:模擬電路基礎(chǔ)


            關(guān)鍵詞: linux

            相關(guān)推薦

            技術(shù)專區(qū)

            關(guān)閉