postgresql編譯安裝與調試(二)

來源:互聯網
上載者:User

標籤:

接前文postgresql編譯安裝與調試(一),繼續說說postgresql的編譯安裝與調試。

上一篇已經詳細說明了如何在Linux系統上編譯安裝postgresql,這次我們在此基礎上簡單講講如何在linux系統上調試和追蹤代碼。

我記得之前看過一篇關於posgresql的文章,postgresql最早只有20萬左右的代碼量,而如今已經過100萬行了,如此巨大的代碼量,在沒有綱領的前提下簡直是盲人摸象。

為方便調試工作,在進入具體的調試之前,我們先來好好瞭解下postgresql的代碼體繫結構。

  • 1.postgresql的目錄結構

 

首先進入postgresql的第一級目錄

config檔案夾主要放的是一些設定檔;contrib檔案夾裡放的是一些第三方的外掛程式、擴充程式等,常用的有pg_standby、postgres_fdw這些;doc檔案夾不用說放的是一些協助文檔和manuals;最主要的是src目錄,這裡放置的是postgresql的原始碼,也是我們調試和跟蹤的主要檔案目錄了。然後INSTALL文檔裡較為詳細的寫了如何編譯安裝postgresql;configure和Makefile這些是程式編譯時間要用的檔案了。

進去src目錄,

首先那幾個Makefile檔案什麼的就不用多介紹了,主要看看這幾個檔案夾。

bin/              放置了postgresql的unix命令,比如psql、initdb這些的原始碼;

backend/          postgresql後端程式的原始碼;

include/          標頭檔;

interfaces/      前端相關的庫的代碼(包括pgsql的C語言庫libpq);

makefiles/       平台相關的make的設定檔案;

pl/               預存程序語言的代碼;

port/             平台移植相關的代碼;

template/        平台相關的設定檔案;

test/             postgresql內建的各種測試指令碼;

timezone/        時區相關的代碼檔案;

tools/           各種開發工具和文檔;

tutorial/        各種相關教程。

可以看出比較核心的是backend、bin、interfaces這三個目錄,其中backend對應後端(伺服器端),剩下兩個對應前端(用戶端)。

對於我們的調試工作,大部分關注點集中在後端,即backend目錄,在該目錄下細分了好多目錄:

access/            各種儲存存取方法(在各個子目錄下) common(共同函數)、gin (Generalized Inverted Index通用逆向索引) 、gist (Generalized Search Tree通用索引)、

                     hash (雜湊索引)、heap (heap的存取方法)、index (通用索引函數)、 nbtree (Btree函數)、transam (交易處理)、 bootstrap/ 資料庫的初始化處理(initdb的時候)

catalog/           系統目錄

commands/          SELECT/INSERT/UPDATE/DELETE以為的SQL文的處理

executor/          執行器(訪問的執行)

foreign/           FDW(Foreign Data Wrapper)處理

lib/               共同函數

libpq/             前端/後端通訊處理

main/              postgres的主函數

nodes/             構文樹節點相關的處理函數

optimizer/        最佳化器

parser/            SQL構文解析器

port/              平台相關的代碼

postmaster/      postmaster的主函數 (常駐postgres)

replication/     streaming replication

regex/            正則處理

rewrite/          規則及視圖相關的重寫處理

snowball/         全文檢索索引相關(語幹處理)

storage/          共用記憶體、磁碟上的儲存、緩衝等全部一次/二次記錄管理(以下的目錄)buffer/(緩衝管理)、 file/(檔案)、freespace/(Fee Space Map管理) ipc/(處理序間通訊)、

                    large_object /(大對象的訪問函數)、 lmgr/(鎖管理)、page/(頁面訪問相關函數)、 smgr/(儲存管理器)

tcop/             postgres (資料庫引擎的進程)的主要部分

tsearch/           全文檢索索引

utils/            各種模組(以下目錄) adt/(嵌入的資料類型)、cache/(緩衝管理)、 error/(錯誤處理)、fmgr/(函數管理)、hash/(hash函數)、 init/(資料庫初始化、postgres的初期處理)、

                   mb/(多位元組文文書處理)、misc/(其他)、mmgr/(記憶體的管理函數)、 resowner/(查詢處理中的資料(buffer pin及表鎖)的管理)、sort/(排序處理)、time/(事務的 MVCC 管理)

 

  • 2.利用gdb調試postgresql

首先我們要有gdb這個工具,如果沒有,可以用yum命令自動的去安裝它。

調試postgresql,我們先以最簡單的SQL文作為例子示範如何調試跟蹤代碼。例如:

select 1;

首先,我們利用postgres使用者進入postgresql:

既然要用gdb跟蹤偵錯工具,我們首先要知道postgresql後端進程的pid,然後才能attach上進行調試(對gdb命令不熟悉的可以先自行百度下)。

要擷取postgresql的pid,我們有兩個辦法。

方法1.使用ps命令查看

[[email protected] ~]# ps -ef | grep postgres

我們可以看到

那個[local] idle 提示的那個就是我們要的,可知進程pid為16581;

方法2.直接在進入postgresql後運行下面的查詢語句:

select pg_backend_pid();

也可以得到進程的pid,方便快捷。

得到進程的pid後,我們就可以進入gdb調試了。

另開一個視窗,我們輸入如下命令:

[[email protected] ~]# gdb postgres 16581

進入了gdb命令列介面。

在這個狀態下,可以接受gdb命令,這裡,我們使用b命令在ExecResult處打上斷點:

這個時候我們再回到postgresql的視窗,執行SQL文:

 

我們可以看到因為postgres進程已經暫停,SQL會卡在那裡動不了,這也是我們的目的,不然怎麼一步一步(似魔鬼的步伐)的調試呢?

我們再回到gdb這邊,運行c命令,程式就會繼續執行下去,然後再斷點處(ExecResult)停止。

作為一個好奇寶寶,我們當然會很好奇執行路徑上走過了哪些檔案調用了哪些函數(廢話,不然幹嘛要調試)?

好的,我們執行gdb的bt命令:

這一大串就是我們夢寐以求的函數調用的堆棧了。這樣從程式開始到ExecResult為止的函數調用都有了。既然說是“堆棧”,我們自然是要反著看的,比如,我們可以看到最早調用的是main函數,它在(at)main.c檔案裡,在main函數的第228行,調用了PostmasterMain函數,依次類推即可知道函數的調用路徑。

知道了函數的調用路徑,我們可以一步一步地看看這條語句是怎麼走的了。以postgresql9.5.4為例(限於篇幅和時間限制,只粗略的講講):

#13 main.c 內:

line99: 函數MemoryContextInit()啟動必須的子系統error和memory管理系統;

line110:函數set_pglocale_pgservice()擷取並設定環境變數;

line146~148: 函數init_locale初始化環境變數;

line219~228:根據輸入參數確定程式走向,這裡進入了PostmasterMain(),跳轉至postmaster.c檔案。

#12#11#10#9 postmaster.c 內:

該檔案中定義了後端的常駐進程"postmaster"所使用的主要函數介面和資料結構定義。postmaster接受前端的請求,建立新的backend進程。

 

line561~623:讀取上下文資訊和設定檔,完成初始化;

line630~812:讀取psql命令列的命令參數;

line930~1000:建立socket通訊;

line1100~1159:建立shared memory和semaphores以及堆棧和pipe,初始化子系統(stats collection、autovacuum);

line1296:進入ServerLoop()函數,跳轉至line1604;

 

line1604:ServerLoop()函數入口。該函數迴圈監聽連接埠上的串連請求;

line1673~1699:判斷是否有"合法"的串連請求,fork一個子進程去處理它,進入BackendStartup()函數,跳轉至line3857;

 

line3857:BackendStartup()函數入口。該函數負責開啟一個新的backend進程;

line3858~3914:做一些初始化準備(資料結構,開啟和關閉一些必要的進程等等);

line3917:進入BackendRun()函數,跳轉至line4179;

 

line4179:BackendRun()函數入口,該函數運行backend進程,主要幹兩件事:1.建立參數列表並初始化2.調用PostgresMain()函數;

line4243:調用PostgresMain()函數,進入postgres.c檔案.

#8#7 postgres.c 檔案內:

 該檔案定義了postgres後端的主要模組,相當於後端的main,並且負責後端進程的調度。

 

line3572:PostgresMain()函數入口。根據輸入的dbname,username和輸入參數建立一個會話;

line3573~3801:初始化工作。開設初始化環境和預設參數,設定訊號處理函數和其他參數,建立記憶體上下文,設定share buffer等等等等;

line3825:進入POSTGRES的主處理迴圈,這個if語句主要用於判斷輸入處理是否有異常等;

line3933:進入處理迴圈中。該迴圈監聽新的查詢請求並判斷請求的類別;

line4045:判斷查詢請求為simple query,調用exec_simple_query()函數,跳轉至line884;

 

line884:exec_simple_query()函數入口。該函數做一些初始化工作,建立一個transaction command,做簡單的文法規則判斷,分析重寫,並為該查詢建立查詢計劃,並返回查詢結果;

line1104:進入函數PortalRun(),進入pquery.c檔案.

#6#5 pquery.c 檔案內:

該檔案定義了postgres後端查詢語句的代碼。

 

line706:PortalRun()函數入口。該函數負責運行一個或一組查詢;

line786:進入PortalRunSelect()函數,跳轉至line888;

 

line888:PortalRunSelect()函數入口。該函數只能執行簡單的SELECT查詢操作;

line942:進入ExecutorRun()函數,進入execMain.c檔案.

#4#3#2 execMain.c 檔案內:

該檔案給出了執行的四個介面函數,分別是ExecutorStart() ExecutorRun() ExecutorFinish() ExecutorEnd()。

 

line279:ExecutorRun()函數入口。該函數時執行模組的主要部分,它接受一個查詢描述符並真正的執行一個查詢語句;

line285:進入standard_ExecutorRun()函數。跳轉至line289;

 

line289:standard_ExecutorRun()函數入口。它執行"標準"的查詢;

line337:進入ExecutePlan()函數,跳轉至line1517;

 

line1517:ExecutePlan()函數入口。還記得前面exec_simple_query()說的查詢計劃嗎?這裡用上了,執行該查詢計劃。

line1541:進入查詢計劃執行的主迴圈;

line1549:進入ExecProcNode()函數,進入execProcnode.c檔案.

#1 execProcnode.c 檔案內:

該檔案內提供了執行查詢計劃的調度函數,功能分別是:

ExecInitNode():初始化查詢計劃的節點以及其子查詢計劃;

ExecProcNode():通過執行查詢計劃獲得元組;

ExecEndNode():關閉一個查詢節點和它的子查詢計劃。

 

line367:ExecProcNode()函數入口;

line385:進入ExecResult()函數,跳轉至檔案nodeResult.c.

 #0 nodeResult.c 檔案內:

該檔案主要為每個查詢計劃的節點提供支援。

 

line67:ExecResult()函數入口,該函數返回查詢計劃獲得的元組。

這一段從#13到#0的函數調用簡單分析就到這裡,完全是自己的理解,如果有什麼不對的地方,歡迎大家批評指正,共同進步。接下來還有函數調用的返回,這裡就不細說了,留給自己和大家一起好好琢磨琢磨吧~

不得不說,postgresql的源碼寫的很優雅,注釋也很到位,看起來很少有雲山霧罩的感覺,真乃吾輩楷模。說起閱讀源碼,想推薦一本書,叫《代碼閱讀方法與實踐》,書不太好找,我還是在托師弟在學校的圖書館才找到的。

另外,今天這種代碼閱讀方法仍然有些原始和低效,決定再看看使用Emacs的Tag或者Eclipse來調試一些更難一些的例子,這個例子畢竟比較簡單。這些就留給postgresql編譯安裝與調試(三)來完成吧,感覺這個系列要出好多的樣子呢,哈哈~

postgresql編譯安裝與調試(二)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.