用C寫一個web服務器(三) Linux下用GCC進行項目編譯

前言

離職前對作過的支付系統進行了一番#總結,繼續完善個人C服務器。javascript

本想着接下來大概實現一下 CGI 協議,可是實現過程當中被一個問題卡住了:php

C進程與php進程的交互數據類型問題:css

在 C 進程中我準備將服務器處理後的請求數據存儲在一個結構體內,而後將此結構體中的信息傳給 PHP,而 PHP 進程內也會有一個全局數組與之對應,但是衆所周之,結構體是 C 進程內的內存數據,是沒法直接傳給 PHP 使用的。html

這時候咱們也須要一種「協議」來解決進程數據類型的異構性。固然這個解決方案肯定起來仍是很簡單的,無非是對C結構體進行序列化,使用xml,json,protobuf(沒用過)之一,花費時間多的地方在實現過程。 原來想本身造個輪子,實現一下json類型的編解碼,以爲有些偏離了主題了,因而考慮使用一個開源庫cJSON;java

但是本身沒有過 C 大型項目的開發經驗,寫的都是小 demo,gcc -o name source.c 足以解決問題了,沒有過編譯多個文件、組織項目的經驗,下載到源碼後一臉懵逼,搜索到的編譯資料都是一些較爲零散的內容,不成體系,不過在本身的屢次嘗試下終於成功地將 cJSON 引入到項目中了,這裏稍作一下總結。python

繞了很久,終於來到了本篇文章的主題:項目編譯,主要介紹一些用 GCC 在 linux 下項目編譯連接的步驟。linux

另外,我只是測試了方案可行,還沒動手改,對方案優劣狀況的判斷還不足,望有過相似經驗的同窗給點意見什麼的。nginx


編譯步驟

先說一下一個C源文件的編譯通常步驟:git

  1. 預處理(preprocess):主要是在代碼層面的處理,包括文件的引入,展開宏定義,刪除註釋,添加行號等,生成的文件以.i結尾。github

    gcc -E test.c -o test.i

  2. 編譯(compilation):編譯是在代碼語法層面的處理,生成對應的彙編語言代碼,生成以.s爲後綴的彙編語言文件;

    gcc -S test.i -o test.s

  3. 彙編(Assembly):將彙編語言代碼生成可執行的機器碼,生成以.o爲後綴的目標文件。

    gcc -c test.s -o test.o

  4. 連接(Linking):將各個.o目標文件鏈接起來,並解決庫依賴,生成無後綴的可直接執行文件。

    gcc -o test test.o

若是咱們直接使用後面的命令,那麼前面的步驟也會自動執行。如咱們常使用的 gcc -o 其實是一次性完成了全部的步驟的。

以上的中間文件,你們可使用文本查看工具來查看其中內容來驗證其功能。


靜態庫和動態庫

庫文件有動態和靜態之分,他們的命名規範爲 lib庫名.後綴,在連接目標文件和庫時,使用 -l 庫名(空格可省略)選項,也能夠添加-L /path來規定優先搜索庫文件的目錄。

例如:C中的數學函數庫math.h的動態庫文件名爲libm.so,那麼咱們編譯鏈接文件時就須要添加-lm的選項。若是要指定庫文件路徑爲/usr/lib64/libm.so,那麼可添加-L /usr/lib64來指定庫文件優先查找目錄。

另外靜態和動態庫文件搜索目錄順序不同,下面分別詳細介紹:

靜態庫

靜態庫文件通常是以.a爲後綴的庫文件,它在編譯鏈接時會將庫文件的內容所有添加到可執行文件中,在編譯鏈接完成後,靜態庫文件便再也不影響可執行文件。

它的優勢是簡單粗暴,但若是庫文件內部有改動的話須要從新對全部引用此庫文件的可執行文件從新編譯。

通常編譯步驟以下:

gcc -c static.c -o static.o // 編譯靜態庫文件的源文件
    ar -r static.a static.o // 生成靜態庫文件
    gcc -o main -lstatic // 鏈接靜態庫文件生成可執行文件

編譯鏈接時,靜態庫文件搜索目錄順序爲:

  1. 編譯鏈接時 -L 參數指定的目錄;
  2. 環境變量目錄 LIBRARY_PATH
  3. 固定目錄 /lib、/usr/lib、/usr/local/lib等;

動態庫

動態庫文件通常以.so結尾,它在編譯鏈接時只把動態庫的文件添加到可執行文件,只在程序運行時才加載庫文件。這種方式的優勢是很是靈活,若是動態庫文件內部有變更,那麼只需重要從新編譯庫文件便可。

它的通常編譯步驟以下:

gcc -c dynamic.c -fpic -o dynamic.o // 編譯動態庫文件的源文件 -fpic 表示編譯爲位置獨立的代碼,使之能夠被放在可執行文件內存中的任何地方
    gcc -shared dynamic.o -o dynamic.so // 生成動態庫文件
    gcc -o main -L . -ldynamic // 鏈接當前文件夾下的動態庫文件

編譯鏈接時,動態庫文件搜索目錄順序爲:

  1. 編譯鏈接時 -L 參數指定目錄;
  2. 環境變量目錄 LD_LIBRARY_PATH
  3. 配置文件/etc/ld.so.conf中配置的目錄
  4. 固定目錄 /lib、/usr/lib等。

CMakeLists

寫到這裏還不是結尾,咱們要考慮若是文件很是多怎麼辦,難道每一次都要輸入n多個源文件名嗎?若是軟件完成後,用戶使用時可不想記住這些複雜的命令和文件。

自動化纔是目標,咱們考慮使用自動化編譯工具 cmake,那麼接下來咱們就要編寫適合項目文件的編譯配置文件 CMakeLists。

CMakeLists 是一個 txt 文件,它就像是項目的編譯指南,是給用 cmake 工具用的。其語法相似於 shell,但內置了許多函數,這裏咱們介紹幾個簡單的語法,編寫一個簡單的 CMakeLists.txt

當前文件結構:

|__ CMakeLists.txt
    |__ test.c
    |__ cJSON.c
    |__ include
    |   |__ cJSON.h
    |__ lib

下面是一個動態庫的編譯CmakeList,將解釋放在註釋中。

PROJECT(test)  # 項目名稱
    cmake_minimum_required(VERSION 2.8) # 選擇一個cmake版本
    
    SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) # 設定產生庫的目錄
    SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) # 設定產生的可執行文件的目錄
    
    ADD_EXECUTABLE(test test.c) # 這裏要先聲明產生的可執行文件,以便後面鏈接
    
    SET(cJSON cJSON.c)  # 設置文件變量
    ADD_LIBRARY(cJSON SHARED ${cJSON}) # 此語句用文件變量生成一個動態連接庫
    TARGET_LINK_LIBRARIES(test cJSON) # 鏈接可執行文件與動態連接庫
    
    FIND_LIBRARY(MATH_LIB libm.so /usr/lib64)  # 在/usr/lib64文件夾下找libm.so(cJSON須要)
    IF(MATH_LIB)
        TARGET_LINK_LIBRARIES(test ${MATH_LIB}) # 找到以後鏈接上
    ENDIF()
    
    MESSAGE("cmake complete, use make to compile!") # 在命令行輸出提示語句

搞了一個多小時,終於寫出來了一個能用的 CMakeLists 文件。運行 cmake . && make完成項目的構建。

此時的目錄結構爲(略過了 cmake 產生的臨時文件):

|__ CMakeLists.txt
    |__ test.c
    |__ cJSON.c
    |__ include
    |   |__ cJSON.h
    |__ lib
    |   |__ libcJSON.so
    |__ bin
        |__ test

小結

本文嚴重地說明了光會寫代碼沒什麼卵用,環境的搭建/類庫的使用也是必備技能,畢竟不能每一個輪子都本身造。

若是你也是 C 新手的話,本文可讓你大概瞭解一下編譯步驟等,不至於跟我一開始同樣一頭霧水。若是要深刻學習的話,文章的關鍵詞和下面的參考文件也能有些幫助。

若是您以爲本文對您有幫助,能夠點擊下面的 推薦 支持一下我。博客一直在更新,歡迎 關注

參考文件(精挑細選):

GCC工做過程以及動態庫靜態庫連接

Linux動態庫文件搜索路徑

cmake使用示例與整理總結

相關文章
相關標籤/搜索