共享庫的初始化和~初始化函式分析
Win32下可以通過DllMain來初始化和~初始化動態庫,而Linux下則沒有與之完全對應的函式,但可以通過一些方法模擬它的部分功能。有人會說,很簡單,實現_init/_fini兩個函式就行了。好,我們來看看事實是不是這樣的。
很多資料上都說可以利用_init/_fini來實現,而我從來沒有測試成功過,原因是這兩個函式都已經被gcc佔用了。比如:
test.c
#include <stdio.h> void _init(void) { printf("%s", __func__); } void _fini(void) { printf("%s", __func__); } |
編譯結果:
[[email protected] soinit]# gcc -g test.c -shared -o libtest.so
/tmp/cc4DUw68.o(.text+0x0): In function `_init':
/work/test/soinit/test.c:5: multiple definition of `_init'
/usr/lib/gcc/i386-redhat-linux/4.0.0/../../../crti.o(.init+0x0): first defined here
/tmp/cc4DUw68.o(.text+0x1d): In function `_fini':
/work/test/soinit/test.c:10: multiple definition of `_fini'
/usr/lib/gcc/i386-redhat-linux/4.0.0/../../../crti.o(.fini+0x0): first defined here
collect2: ld returned 1 exit status
由此可見,這兩個符號已經被編譯器的腳手架程式碼佔用了,我們不能再使用。編譯器用這兩個函式做什麼?我們能不能搶佔這兩個函式,不用編譯器提供的,而用我們自己的呢?先看看這兩個的實現:
00000594 <_fini>: 594: 55 push %ebp 595: 89 e5 mov %esp,%ebp 597: 53 push %ebx 598: 50 push %eax 599: e8 00 00 00 00 call 59e <_fini+0xa> 59e: 5b pop %ebx 59f: 81 c3 02 11 00 00 add $0x1102,%ebx 5a5: e8 de fe ff ff call 488 <__do_global_dtors_aux> 5aa: 58 pop %eax 5ab: 5b pop %ebx 5ac: c9 leave 5ad: c3 ret 0000041c <_init>: 41c: 55 push %ebp 41d: 89 e5 mov %esp,%ebp 41f: 83 ec 08 sub $0x8,%esp 422: e8 3d 00 00 00 call 464 <call_gmon_start> 427: e8 b8 00 00 00 call 4e4 <frame_dummy> 42c: e8 2b 01 00 00 call 55c <__do_global_ctors_aux> 431: c9 leave 432: c3 ret |
從以上程式碼中可以看出,這兩個函式是用來初始化/~初始化全域性變數/物件的,搶佔這兩個函式可能導致初始化/~初始化全域性變數/物件出錯。所以不能再打_init/_fini的主意,那怎麼辦呢?
使用全域性物件
test.cpp
#include <stdio.h> class InitFini { public: InitFini() { printf("%s/n", __func__); } ~InitFini() { printf("%s/n", __func__); } }; static InitFini aInitFini; extern "C" int test(int n) { return n; } |
Main.c
int test(int n); int main(int argc, char* argv[]) { test(1); return 0; } |
測試結果:
[[email protected] soinit]# ./t.exe
InitFini
~InitFini
那麼這兩個函式是怎麼被呼叫的呢?我們在gdb裡看看:
Breakpoint 3, InitFini (this=0xa507bc) at test.cpp:7
7 printf("%s/n", __func__);
Current language: auto; currently c++
(gdb) bt
#0 InitFini (this=0xa507bc) at test.cpp:7
#1 0x00a4f5e0 in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at test.cpp:15
#2 0x00a4f611 in global constructors keyed to test () at test.cpp:21
#3 0x00a4f66a in __do_global_ctors_aux () from ./libtest.so
#4 0x00a4f4a9 in _init () from ./libtest.so
#5 0x002c8b4b in call_init () from /lib/ld-linux.so.2
#6 0x002c8c4a in _dl_init_internal () from /lib/ld-linux.so.2
#7 0x002bb83f in _dl_start_user () from /lib/ld-linux.so.2
Breakpoint 4, ~InitFini (this=0x0) at test.cpp:9
9 ~InitFini()
(gdb) bt
#0 ~InitFini (this=0x0) at test.cpp:9
#1 0x00a4f5b3 in __tcf_0 () at test.cpp:15
#2 0x00303e6f in __cxa_finalize () from /lib/libc.so.6
#3 0x00a4f532 in __do_global_dtors_aux () from ./libtest.so
#4 0x00a4f692 in _fini () from ./libtest.so
#5 0x002c9058 in _dl_fini () from /lib/ld-linux.so.2
#6 0x00303c69 in exit () from /lib/libc.so.6
#7 0x002eddee in __libc_start_main () from /lib/libc.so.6
#8 0x080483b5 in _start ()
從以上資訊可以看出,正是從_init/_fini兩個函式呼叫過來的。
使用gcc擴充套件
毫無疑問,以上方法可行,但是在C++中才行。而C語言中,根本沒有構造和解構函式。怎麼辦呢?這時我們可以使用gcc的擴充套件:
#include <stdio.h> __attribute ((constructor)) void test_init(void) { printf("%s/n", __func__); } __attribute ((destructor)) void test_fini(void) { printf("%s/n", __func__); } int test(int n) { return n; } |
測試結果:
[[email protected] soinit]# ./t.exe
test_init
test_fini
我們不防也看看這兩個函式是怎麼被調到的:
Breakpoint 3, test_init () at test.c:6
6 printf("%s/n", __func__);
(gdb) bt
#0 test_init () at test.c:6
#1 0x00860586 in __do_global_ctors_aux () from ./libtest.so
#2 0x00860439 in _init () from ./libtest.so
#3 0x002c8b4b in call_init () from /lib/ld-linux.so.2
#4 0x002c8c4a in _dl_init_internal () from /lib/ld-linux.so.2
#5 0x002bb83f in _dl_start_user () from /lib/ld-linux.so.2
(gdb) c
Breakpoint 4, test_fini () at test.c:11
11 printf("%s/n", __func__);
(gdb) bt
#0 test_fini () at test.c:11
#1 0x008604d3 in __do_global_dtors_aux () from ./libtest.so
#2 0x008605ae in _fini () from ./libtest.so
#3 0x002c9058 in _dl_fini () from /lib/ld-linux.so.2
#4 0x00303c69 in exit () from /lib/libc.so.6
#5 0x002eddee in __libc_start_main () from /lib/libc.so.6
#6 0x080483b5 in _start ()
從以上資訊可以看出,也是從_init/_fini兩個函式呼叫過來的。
總結:正如一些資料上所說的,在linux下,_init/_fini是共享庫的初始化和~初始化函式。但這兩個函式是給gcc用的,我們不能直接使用它們,但可以用本文中提到另外兩種方法來實現。
相關推薦
共享庫的初始化和~初始化函式分析
Win32下可以通過DllMain來初始化和~初始化動態庫,而Linux下則沒有與之完全對應的函式,但可以通過一些方法模擬它的部分功能。有人會說,很簡單,實現_init/_fini兩個函式就行了。好,我們來看看事實是不是這樣的。 很多資料上都說可以利用_init/_fini來實現,而我從來沒有測試成功
SQLAlchemy數據庫連接和初始化數據庫
數據 即使 conf 執行 int rop windows use VR 查看版本 >>> import sqlalchemy >>> sqlalchemy.__version__ ‘1.0.9‘ 創建連接 from sqlclach
Linux驅動開發(三)——模組初始化和解除安裝函式
在(一)中,主要講述了模組的基本組成,載入,解除安裝和檢視工具的使用。本篇中,主要講述module_init()和module_exit()這兩個函式的使用。 在(一)中給的原始碼檔案中,模組初始化和解除安裝函式為:init_module()
第4章 初始化和結束化過程
每一個程式碼塊都具有一個BEGIN和一個END,儘管在BASM中用ASM關鍵字代替了BEGIN,但它仍然起著相同的作用。 有些情況下,BEGIN和END只決定一個語法結構的開始和結束,例如記錄和物件型別定義中使用的BEGIN和END。但更多的時候(例如例程定義中),BEGIN與END代表初始化與結束化過
Spring Bean的初始化和例項化的區別
準確的說,標題中的初始化指的是Bean Definition的初始化,所以是完全不同的兩個概念。 普通的Java類如果想被SpringIOC容器託管,擁有強大的擴充套件功能和更強大的生命週期,使用者(程式設計師)要做的只是寫配置或者寫註解,然後Spring會做這些事:
Java類初始化和例項化
摘要: Java有以下幾種方式建立類物件: 利用new關鍵字 利用反射Class.newInstance 利用Constructor.newIntance(相比Class.newInstance多了有參和私有建構函式) 利用Cloneable/Object.clone() 利
python關於初始化和例項化之----log日誌列印兩次的問題綜述
我在寫appium-desktop自動化框架的時候,我發現在我執行demo的時候,日誌會執行兩次,我查了一堆資料也沒有發現我錯在哪裡。 一。開始我以為是 __init__的問題,我檢查了__init__方法,發現他們屬於同一物件,沒有多餘的物件。 二。然後我
Java基礎學習系列-Java類初始化和例項化
Java有以下幾種方式建立類物件: 利用new關鍵字 利用反射Class.newInstance 利用Constructor.newIntance(相比Class.newInstance多了有參和私有建構函式) 利用Cloneable/Object.clon
STM32的HAL庫的 I2C和UART使用函式,幾個好用的
void I2C_Write(uint8_t* pBuffer, uint8_t DeviceAddr, uint8_t RegisterAddr,uint16_t NumByteToWrite){ //HAL_I2C_Master_Transmit(&hi2c1
什麼是序列化和反序列化 什麼是序列化和並行化
當兩個程序在進行遠端通訊時,彼此可以傳送各種型別的資料。無論是何種型別的資料,都會以二進位制序列的形式在網路上傳送。傳送方需要把這個物件轉換為位元組序列,才能在網路上傳送;接收方則需要把位元組序列再恢復為物件。 1、把物件轉換為位元組序列的過程稱為物件的序列化。
前端框架___元件化和模組化
元件化和模組化 元件化和模組化有利於封裝以及複用。 元件化: 1.生命週期。 前端框架都有一個重要的詞語,生命週期,都大部分從幾個方面來看,初始化,渲染,存活期,銷燬。 2.資料通訊 元件之間的通訊,父傳
Python學習【第21篇】:程序池以及回撥函式 python併發程式設計之多程序2-------------資料共享及程序池和回撥函式
python併發程式設計之多程序2-------------資料共享及程序池和回撥函式 一、資料共享 1.程序間的通訊應該儘量避免共享資料的方式 2.程序
JAVA 序列化和序列化
狀態 NPU serializa output 上傳 讀取 mil 一個 輸出流 序列化 把對象轉換為字節碼序列化的過程 反序列化 把字節序列恢復為對象的過程 用途: 把對象的字節碼序列永遠地保存到硬盤上,通常存放在一個文件中或在網絡上傳輸對象的字節序列 是Java提供的一
Android模組化和元件化開發簡單理解(一)
模組化和元件化可以理解為同一個概念: 將一個app分成多個模組,每個模組都是一個元件(module),開發過程中讓這些元件相互依賴或者單獨除錯某個元件。在釋出的時候將這些元件合併成一個apk。 Android元件化我的理解是 application與library之間相互
前端面試題(二)----前端模組化和元件化的區別和聯絡
前端元件化開發和模組化開發的區別 之前一直以為模組化開發和元件化開發是一個意思,有次看到了類似這樣的題,發現自己還是太年輕,現在整理一點出來。 首先,元件化和模組化的意義都在於實現了分治,目前我們開發的專案複雜度不斷的上升,早已不是我們一個人能完成的工作,團
模組化、元件化和外掛化
單工程模式 移動開發誕生,我們開發移動專案,我相信大多用的是單工程單任務的開發模式,二話不說,直接就開始寫起,是不是這樣呢? new Project -> 分包 -> 寫起。我相信都經歷過,也寫的比較爽,為什麼呢? 這種模式不涉及亂七八糟的處理方式,
Android元件化和外掛化
元件化開發就是將一個app分成多個模組,每個模組都是一個元件(Module),開發的過程中我們可以讓這些元件相互依賴或者單獨除錯部分元件等,但是最終釋出的時候是將這些元件合併統一成一個apk元件化優勢:稍微改動一個模組的一點程式碼都要編譯整個工程,耗時耗力公共資源、業務、模組
微服務化之前需要先無狀態化和容器化
本文是微服務實戰系列文章的第四篇,前三篇連結如下:一、為什麼要做無狀態化和容器化很多應用拆分成微
Redis中物件的序列化和序列化的使用
最近專案開發用到Redis,然後使用到了將物件進行序列化和反序列化的方法,總結如下:package com.lz.test; import java.nio.charset.Charset; im
linux0.11程序睡眠sleep_on函式和喚醒wake_up函式分析
核心中的這兩個函式主要用於訪問資源時的同步操作。高速緩衝區的訪問就是其中的一個例子:如果兩個程序都要訪問同一個緩衝塊,那麼其中的一個程序就必然睡眠等待,直到該緩衝塊被釋放才可訪問。趙炯博士所著的linux0.11核心完全註釋一書中也是對該問題進行詳細的討論,但是我在閱讀這部