Mysql源碼學習筆記——偷窺線程

 安裝完Mysql 後,使用VS打開源碼開開眼,我嘞個去,這代碼和想象中怎麼差異這麼大呢?感受代碼有些凌亂,註釋代碼都寫的比較隨意,好像沒有什麼統一的規範,不一樣的文 件中代碼風格也有差別,可能Mysql通過了不少牛人的手以後,集衆牛人之長吧。也多是我見識比較淺薄,適應了本身的代碼風格,井底之蛙了,總之仍是懷 着敬畏的心情開始咱的源碼之旅吧。本人菜鳥,大神輕拍。

Mysql能夠啓動起來了,應該怎麼學習呢?總不能從main開始一步一步的 看吧,Mysql做爲比較底層的大型軟件,涉及到數據庫實現的方方面面,沒有厚實的數據庫理論基礎和對Mysql各個模塊至關的熟悉,從main開始勢必 會把本身引入某個死衚衕啊,什麼都看,最後啥也不會,咱傷不起。 mysql

通過思考後,我想仍是經過客戶端來調試服務器,從而學習服務器代碼比較現實。 也就是經過客戶端的動做,看服務器的反應。好比從客戶端的登陸動做來看SERVER如何進行通訊、用戶識別、鑑定以及任務分配的,經過CREATE TABLE,來看SERVER如何解析DDL語句以及針對不一樣的存儲引擎採起的不一樣的物理存儲方式,經過INSERT語句,來看SERVER如何進行 Btree的操做。經過SELECT語句來看如何進行SQL語句語法樹的建立和優化的,經過ROLL BACK,來看SERVER事務是如何實現的。這裏主要是經過跟蹤代碼學習Mysql數據庫實現的思想,對於具體的代碼不去作過多的追究(主要是我對 C++不是很熟悉),好讀書,不求甚解,呵呵。 sql

由此,暫時準備瞭如下幾條SQL語句,來有針對的進行SERVER的分析 數據庫

一、 LOGIN(登陸)
mysql.exe –uroot –p
二、 DDL(建表語句)
create table tb_myisam(c1 int, c2 varchar(256)) engine = myisam;
create table tb_innodb(c1 int, c2 varchar(256)) engine = innodb;
三、 INSERT
Insert into tb_myisam values(1 , ’寂寞的肥肉’);
Insert into tb_innodb values(1 , ’寂寞的肥肉’);
四、 SELECT
Select c1 from tb_myisam;
Select * from tb_innodb;
五、 ROLLBACK

你們都知道,mysql能夠經過多個客戶端,進行併發操做,固然也包括登陸 了。在別人登陸的時候,其餘的用戶可能正在進行一些其它的操做,所以對於登陸咱們猜想應該有專門的線程負責客戶端和服務器的鏈接的建立,以保證登陸的及時 性,對於每一個鏈接的用戶,應該用一個獨立的線程進行任務的執行。 服務器

首先介紹下mysql中建立線程的函數,建立線程的函數貌似就是 _begin_thread,CreateThread,咱們經過VS在整個解決方案中進行查找,bingo!在my_winthread.c中找到了調 用_begin_thread的函數pthread_create,在os0thread.c中找到了調用CreateThread的函數 os_thread_create,一個系統怎麼封裝兩個系統函數呢??再仔細看下,發現my_winthread.c是在項目mysys下,而 os0thread.c是在項目innobase下。innobase!!這不就是innodb的插件式存儲引擎麼,原來這是存儲引擎本身的封裝的底層函 數,哥心中豁然開朗了。我想Mysql應用範圍如此之廣,除了開源以外,插件式的存儲引擎功不可沒啊,用戶能夠根據本身的實際應用採起不一樣的存儲引擎,對 於大公司,估計會開發本身的存儲引擎。 併發

下面分析下pthread_create是如何調用_begin_thread的,先粗略看下源碼。  socket

int pthread_create(pthread_t *thread_id, pthread_attr_t *attr,
pthread_handler func, void *param)
{
HANDLE hThread;
struct pthread_map *map;
DBUG_ENTER(「pthread_create」);

if (!(map=malloc(sizeof(*map))))
DBUG_RETURN(-1);
map->func=func;
map->param=param;
pthread_mutex_lock(&THR_LOCK_thread);
#ifdef __BORLANDC__
hThread=(HANDLE)_beginthread((void(_USERENTRY *)(void *)) pthread_start,
attr->dwStackSize ? attr->dwStackSize :
65535, (void*) map);
#else
hThread=(HANDLE)_beginthread((void( __cdecl *)(void *)) pthread_start,
attr->dwStackSize ? attr->dwStackSize :
65535, (void*) map);
#endif
DBUG_PRINT(「info」, (「hThread=%lu」,(long) hThread));
*thread_id=map->pthreadself=hThread;
pthread_mutex_unlock(&THR_LOCK_thread); 函數

if (hThread == (HANDLE) -1)
{
int error=errno;
DBUG_PRINT(「error」,
(「Can’t create thread to handle request (error %d)」,error));
DBUG_RETURN(error ? error : -1);
}
VOID(SetThreadPriority(hThread, attr->priority)) ;
DBUG_RETURN(0);
} 學習

關鍵的代碼是下面三句: 優化

map->func=func;
map->param=param;
_beginthread((void( __cdecl *)(void *)) pthread_start,
attr->dwStackSize ? attr->dwStackSize :
65535, (void*) map);

 

從這能夠看出,建立的新線程的名字是個固定的函數——pthread_start,而咱們傳進來的想建立的函數func是掛載在了map上了,函數的參數一樣的掛載在map上了,這樣咱們就能夠推理出在pthread_start函數中,確定會出現這樣的代碼: spa

map->func(map->param);

mysql沒有選擇直接_beginthread(func, stack_size, param)的形式,而是進行了一次封裝,不知道這樣的好處是什麼,可能牛人的思想不是我這樣小菜鳥能頓悟的,跑題了~~

至此,咱們只在pthread_create函數上設置斷點,調試啓動mysqld,斷點停下來,看下系統的線程情況:

咱們第一次進入pthread_create,任何線程都沒開始建立呢,按理說系統線程應該就只有一個主線程,可如今多了這麼多,這些應該是innodb存儲引擎建立的線程了(具體是在plugin_init)。根據線程的名稱,結合註釋,猜想了下這些線程的做用。

Io_handler_thread:從名稱能夠知道這些是I/O線程,負責進行磁盤I/O。

Svr_error_monitor_thread:應該是服務器出錯監控線程。

Svr_lock_timeout_thread:應該是和上鎖相關的線程。

Svr_master_thread:

/*************************************************************************

The master thread controlling the server. */

服務器控制線程,應該是具體進行做業的線程。

Svr_monitor_thread:

/*************************************************************************

A thread prints the info output by various InnoDB monitors. */

監控線程,負責打印信息。

 

淡然飄過吧,不去細究了,咱們只關心pthread_create建立的線程。根據調試,發現多了幾個線程同名的線程_threadstart,以下所示:

調試時看堆棧能夠知道這三個線程的建立者和做用,以下所示

建立者 處理函數
create_shutdown_thread handle_shutdown
start_handle_manager handle_manager
handle_connections_methods handle_connections_sockets

建立者:調用pthread_create進行建立線程的函數。

處理函數:調用pthread_create所建立的線程的具體的線程函數。

 

由名稱咱們就能夠看出,handle_connections_sockets應該是處理鏈接的線程了,從順序上看,也應該是這樣,只有系統中全部的其餘必須的線程建立完畢後,才能建立監聽線程(鏈接線程),即監聽線程應該是系統最後建立的。

    找到了咱們LOGIN須要的線程了,下次針對這個線程,分析下如何進行登陸的,以及登陸後爲用戶分配哪些資源。時間不早啦,洗洗睡了

相關文章
相關標籤/搜索