之前在寫程式時,有用到dlopen()/dlclose()動態載入libdingo.so來使用。所以好奇他的運作原理是什麼@@?
Trace Code:
主要是了解基本的概念,及什麼時候load libdingo.so和unload libdingo.so。
dlfnc.c dlopen()
void *dlopen(const char *filename, int flag) { soinfo *ret; // 這個struct很重要,主要用來看裡面的refcount數量,來知道是不是有一直遞減的現像。 pthread_mutex_lock(&dl_lock); ret = find_library(filename); // 先看一下這裡做了什麼? if (unlikely(ret == NULL)) { set_dlerror(DL_ERR_CANNOT_LOAD_LIBRARY); } else { ret->refcount++; // 看樣子,如果沒有問題,就會把refcount+1,代表現在有1個地方在使用。 } pthread_mutex_unlock(&dl_lock); return ret; } |
linker.c find_library()
soinfo *find_library(const char *name) { soinfo *si; const char *bname; #if ALLOW_SYMBOLS_FROM_MAIN if (name == NULL) return somain; #else if (name == NULL) return NULL; #endif bname = strrchr(name, '/'); bname = bname ? bname + 1 : name; for(si = solist; si != 0; si = si->next){ if(!strcmp(bname, si->name)) { if(si->flags & FLAG_ERROR) { DL_ERR("%5d '%s' failed to load previously", pid, bname); return NULL; } if(si->flags & FLAG_LINKED) {//這裡很重要,他告訴我們,如果libapi.1.so存在,他就直接return si。 DL_ERR("[ %5d '%s' is exist!! refcount=%d *si=%p]\n", pid, bname, (si->refcount + 1), si); return si; } DL_ERR("OOPS: %5d recursive link to '%s'", pid, si->name); return NULL; } } DL_ERR("[ %5d '%s' has not been loaded yet. Locating...]\n", pid, name); si = load_library(name); if(si == NULL) return NULL; return init_library(si); } |
dlfnc.c dlclose()
int dlclose(void *handle) { pthread_mutex_lock(&dl_lock); (void)unload_library((soinfo*)handle);//直接看一下內容 pthread_mutex_unlock(&dl_lock); return 0; } |
linker.c unload_library()
unsigned unload_library(soinfo *si) { unsigned *d; if (si->refcount == 1) { //重點,如果refcount只剩下你的話,library就會就會被unload了。 TRACE("%5d unloading '%s'\n", pid, si->name); call_destructors(si); for(d = si->dynamic; *d; d += 2) { if(d[0] == DT_NEEDED){ soinfo *lsi = (soinfo *)d[1]; d[1] = 0; if (validate_soinfo(lsi)) { TRACE("%5d %s needs to unload %s\n", pid, si->name, lsi->name); unload_library(lsi); } else DL_ERR("%5d %s: could not unload dependent library", pid, si->name); } } munmap((char *)si->base, si->size); notify_gdb_of_unload(si); free_info(si); si->refcount = 0; } else { // 如果還有人在引用,就只是把count減1而已。 si->refcount--; PRINT("%5d not unloading '%s', decrementing refcount to %d\n", pid, si->name, si->refcount); } return si->refcount; } |
Comment:
1. dlopen() / dlclose() 只會用到1次I/O,分別是第一次使用時載入,及沒有人去引用時移除(release memory)。
2. 呼叫dlopen() / dlclose() 一定要成雙成對,不然會有下列2問題。
(1) dlopen()比較多,即呼叫的次數 dlopen() > dlclose(),就會無法unload沒有使用的libapi.so
(2) dlclose()比較多,即呼叫的次數dlclose() > dlopen(),就會造成segmentation fault。