<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 練習29:庫和鏈接 > 原文:[Exercise 29: Libraries And Linking](http://c.learncodethehardway.org/book/ex29.html) > 譯者:[飛龍](https://github.com/wizardforcel) C語言編程的核心能力之一就是鏈接OS所提供的庫。鏈接是一種為你的程序天機額外特性的方法,這些特性有其它人在系統中創建并打包。你已經使用了一些自動包含的標準庫,但是我打算對哭的不同類型和它們的作用做個解釋。 首先,庫在每個語言中都沒有良好的設計。我不知道為什么,但是似乎語言的設計者都將鏈接視為不是特別重要的東西。它們通常令人混亂,難以使用,不能正確進行版本控制,并以不同的方式鏈接到各種地方。 C沒有什么不同,但是C中的庫和鏈接是Unix操作系統的組件,并且可執行的格式在一些年前就設計好了。學習C如何鏈接庫有助于理解OS如何工作,以及它如何運行你的程序。 C中的庫有兩種基本類型: 靜態 你可以使用`ar`和`ranlib`來構建它,就像上個練習中的`libYOUR_LIBRARY.a`那樣(Windows下后綴為`.lib`)。這種庫可以當做一系列`.o`對象文件和函數的容器,以及當你構建程序時,可以當做是一個大型的`.o`文件。 動態 它們通常以`.so`(Linux)或`.dll`(Windows)結尾。在OSX中,差不多有一百萬種后綴,取決于版本和編寫它的人。嚴格來講,OSX中的`.dylib`,`.bundle`和`framework`與前面這個三個沒什么不同。這些文件都被構建好并且放置到指定的地方。當你運行程序時,OS會動態加載這些文件并且“憑空”鏈接到你的程序中。 我傾向于對于小型或中性項目使用靜態的庫,因為它們易于使用,并且工作在在更多操作系統上。我也喜歡將所有代碼當如靜態庫中,之后鏈接它來執行單元測試,或者鏈接到所需的程序中。 動態庫適用于大型系統,其中空間十分有限,或者大量程序都使用相同的功能。這種情況下不應該為每個程序的共同特性靜態鏈接所有代碼,而是應該將它放到動態庫中,這樣它僅僅會為所有程序加載一份。 在上一個練習中,我講解了如何構建靜態庫(`.a`),我會在本書的剩余部分用到它。這個練習中我打算向你展示如何構建一個簡單的`.so`庫,并且如何使用Unix系統的`dlopen`動態加載它。我會手動執行它,以便你可以理解每件實際發生的事情。之后,附加題這部分會使用c項目框架來創建它。 ## 動態加載動態庫 我創建了兩個源文件里完成它。一個用于侯建`libex29.so`庫,另一個是個叫做`ex29`的程序,它可以加載這個庫并運行其中的程序、 ```c #include <stdio.h> #include <ctype.h> #include "dbg.h" int print_a_message(const char *msg) { printf("A STRING: %s\n", msg); return 0; } int uppercase(const char *msg) { int i = 0; // BUG: \0 termination problems for(i = 0; msg[i] != '\0'; i++) { printf("%c", toupper(msg[i])); } printf("\n"); return 0; } int lowercase(const char *msg) { int i = 0; // BUG: \0 termination problems for(i = 0; msg[i] != '\0'; i++) { printf("%c", tolower(msg[i])); } printf("\n"); return 0; } int fail_on_purpose(const char *msg) { return 1; } ``` 這里面沒什么神奇之處。其中故意留了一些bug,看你是否注意到了。你會在隨后修復它們。 我們打算使用`dlopen`,`dlsym`,和`dlclose`函數來處理上面的函數。 ```c #include <stdio.h> #include "dbg.h" #include <dlfcn.h> typedef int (*lib_function)(const char *data); int main(int argc, char *argv[]) { int rc = 0; check(argc == 4, "USAGE: ex29 libex29.so function data"); char *lib_file = argv[1]; char *func_to_run = argv[2]; char *data = argv[3]; void *lib = dlopen(lib_file, RTLD_NOW); check(lib != NULL, "Failed to open the library %s: %s", lib_file, dlerror()); lib_function func = dlsym(lib, func_to_run); check(func != NULL, "Did not find %s function in the library %s: %s", func_to_run, lib_file, dlerror()); rc = func(data); check(rc == 0, "Function %s return %d for data: %s", func_to_run, rc, data); rc = dlclose(lib); check(rc == 0, "Failed to close %s", lib_file); return 0; error: return 1; } ``` 我現在會拆分這個程序,便于你理解這一小段代碼其中的原理。 ex29.c:5 我在隨后使用這個函數指針定義,來調用庫中的函數。這沒什么新東西,確保你理解了它的作用。 ex29.c:17 在為一個小型程序做必要的初始化后,我使用了`dlopen`函數來加載由`lib_file`表示的庫。這個函數返回一個句柄,我們隨后會用到它,就像來打開文件那樣。 ex29.c:18 如果出現錯誤,我執行了通常的檢查并退出,但是要注意最后我使用了`dlerror`來查明發生了什么錯誤。 ex29.c:20 我使用了`dlsym`來獲取`lib`中的函數,通過它的字面名稱`func_to_run`。它是最強大的部分,因為我動態獲取了一個函數指針,基于我從命令行`argv`獲得的字符串。 ex29.c:23 接著我調用`func`函數,獲得返回值并檢查。 ex29.c:26 最后,我像關閉文件那樣關閉了庫。通常你需要在程序的整個運行時保持它們打開,所以關閉操作并不非常實用,我只是在這里演示它。 > 譯者注:由于能夠使用系統調用加載,動態庫可以被多種語言的程序調用,而靜態庫只能被C及兼容C的程序調用。 ## 你會看到什么 既然你已經知道這些文件做什么了,下面是我的shell會話,用于構建`libex29.so`和`ex29`并隨后運行它。下面的代碼中你可以學到如何手動構建: ```shell # compile the lib file and make the .so # you may need -fPIC here on some platforms. add that if you get an error $ cc -c libex29.c -o libex29.o $ cc -shared -o libex29.so libex29.o # make the loader program $ cc -Wall -g -DNDEBUG ex29.c -ldl -o ex29 # try it out with some things that work $ ex29 ./libex29.so print_a_message "hello there" -bash: ex29: command not found $ ./ex29 ./libex29.so print_a_message "hello there" A STRING: hello there $ ./ex29 ./libex29.so uppercase "hello there" HELLO THERE $ ./ex29 ./libex29.so lowercase "HELLO tHeRe" hello there $ ./ex29 ./libex29.so fail_on_purpose "i fail" [ERROR] (ex29.c:23: errno: None) Function fail_on_purpose return 1 for data: i fail # try to give it bad args $ ./ex29 ./libex29.so fail_on_purpose [ERROR] (ex29.c:11: errno: None) USAGE: ex29 libex29.so function data # try calling a function that is not there $ ./ex29 ./libex29.so adfasfasdf asdfadff [ERROR] (ex29.c:20: errno: None) Did not find adfasfasdf function in the library libex29.so: dlsym(0x1076009b0, adfasfasdf): symbol not found # try loading a .so that is not there $ ./ex29 ./libex.so adfasfasdf asdfadfas [ERROR] (ex29.c:17: errno: No such file or directory) Failed to open the library libex.so: dlopen(libex.so, 2): image not found $ ``` 需要注意,你可能需要在不同OS、不同OS的不同版本,以及不同OS的不同版本的不同編譯器上執行構建,則需要修改構建共享庫的方式。如果我構建`libex29.so`的方式在你的平臺上不起作用,請告訴我,我會為其它平臺添加一些注解。 > 譯者注:到處編寫、到處調試、到處編譯、到處發布。--vczh &zwj; > 注 > 有時候你會通常運行`cc -Wall -g -DNDEBUG -ldl ex29.c -o ex29`,并且認為它能夠正常工作,但是沒有。在一些平臺上,參數的順序會影響到它是否生效,這也沒什么理由。在Debian或者Ubuntu中你需要執行`cc -Wall -g -DNDEBUG ex29.c -ldl -o ex29`。它是唯一的方式,所以雖然我在這里使用了OSX,但是以后如果你鏈接動態庫的時候它找不到某個函數,要試著自己解決問題。 > 這里面比較麻煩的事情是,實際平臺的不同會影響到命令參數的順序。將`-ldl`放到某個位置沒有理由與其它位置不同。他只是一個選項,還需要了解這些簡直是太氣人了。 ## 如何使它崩潰 打開`lbex29.so`,并且使用能夠處理二進制的編輯器編輯它。修改一些字節,然后關閉。看看你是否能使用`dlopen`函數來打開它,即使你修改了它。 ## 附加題 + 你注意到我在`libex29.c`中寫的不良代碼了嗎?我使用了一個`for`循環來檢查`'\0'`的結尾,修改它們使這些函數總是接收字符串長度,并在函數內部使用。 + 使用項目框架目錄,并且為這個練習創建新的項目。將`libex29.c`放入`src/`目錄,修改`Makefile`使它能夠構建`build/libex29.so`。 + 將`ex29.c`改為`tests/ex29_tests.c`,使它做為單元測試執行。使它能夠正常工作,意思是你需要修改它讓它加載`build/libex29.so`文件,并且運行上面我手寫的測試。 + 閱讀`man dlopen`文檔,并且查詢所有有關函數。嘗試`dlopen`的其它選項,比如`RTLD_NOW`。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看