服務器之家:專注于服務器技術及軟件下載分享
分類導航

Mysql|Sql Server|Oracle|Redis|

服務器之家 - 數據庫 - Mysql - Mysql源碼學習筆記 偷窺線程

Mysql源碼學習筆記 偷窺線程

2019-11-16 16:35cnblogs心中無碼 Mysql

安裝完Mysql后,使用VS打開源碼開開眼,我嘞個去,這代碼和想象中怎么差別這么大呢?

感覺代碼有些凌亂,注釋代碼都寫的比較隨意,好像沒有什么統一的規范,不同的文件中代碼風格也有差異,可能Mysql經過了很多牛人的手之后,集眾牛人之長吧。也可能是我見識比較淺薄,適應了自己的代碼風格,井底之蛙了,總之還是懷著敬畏的心情開始咱的源碼之旅吧。本人菜鳥,大神輕拍。 

  Mysql可以啟動起來了,應該怎么學習呢?總不能從main開始一步一步的看吧,Mysql作為比較底層的大型軟件,涉及到數據庫實現的方方面面,沒有厚實的數據庫理論基礎和對Mysql各個模塊相當的熟悉,從main開始勢必會把自己引入某個死胡同啊,什么都看,最后啥也不會,咱傷不起。 

  經過思考后,我想還是通過客戶端來調試服務器,從而學習服務器代碼比較現實。也就是通過客戶端的動作,看服務器的反應。比如從客戶端的登錄動作來看SERVER如何進行通信、用戶識別、鑒定以及任務分配的,通過CREATE TABLE,來看SERVER如何解析DDL語句以及針對不同的存儲引擎采取的不同的物理存儲方式,通過INSERT語句,來看SERVER如何進行Btree的操作。通過SELECT語句來看如何進行SQL語句語法樹的創建和優化的,通過ROLL BACK,來看SERVER事務是如何實現的。這里主要是通過跟蹤代碼學習Mysql數據庫實現的思想,對于具體的代碼不去做過多的追究(主要是我對C++不是很熟悉),好讀書,不求甚解,呵呵。 
  由此,暫時準備了以下幾條SQL語句,來有針對的進行SERVER的分析 

復制代碼代碼如下:


1、 LOGIN(登錄) 
mysql.exe –uroot –p 
2、 DDL(建表語句) 
create table tb_myisam(c1 int, c2 varchar(256)) engine = myisam; 
create table tb_innodb(c1 int, c2 varchar(256)) engine = innodb; 
3、 INSERT 
Insert into tb_myisam values(1 , '寂寞的肥肉'); 
Insert into tb_innodb values(1 , '寂寞的肥肉'); 
4、 SELECT 
Select c1 from tb_myisam; 
Select * from tb_innodb; 
5、 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的,先粗略看下源碼。 

復制代碼代碼如下:


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函數中,肯定會出現這樣的代碼: 
              map->func(map->param); 
  mysql沒有選擇直接_beginthread(func, stack_size, param)的形式,而是進行了一次封裝,不知道這樣的好處是什么,可能牛人的思想不是我這樣小菜鳥能頓悟的,跑題了~~ 
  至此,我們只在pthread_create函數上設置斷點,調試啟動mysqld,斷點停下來,看下系統的線程狀況: 

Mysql源碼學習筆記 偷窺線程
我們第一次進入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,如下所示: 
Mysql源碼學習筆記 偷窺線程

  調試時看堆棧可以知道這三個線程的創建者和作用,如下所示

 

創建者

處理函數

create_shutdown_thread

handle_shutdown

start_handle_manager

handle_manager

handle_connections_methods

handle_connections_sockets

 

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

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

 

  由名稱我們就可以看出,handle_connections_sockets應該是處理連接的線程了,從順序上看,也應該是這樣,只有系統中所有的其他必須的線程創建完畢后,才能創建監聽線程(連接線程),即監聽線程應該是系統最后創建的。

    找到了我們LOGIN需要的線程了,下次針對這個線程,分析下如何進行登錄的,以及登錄后為用戶分配哪些資源。時間不早啦,洗洗睡了
作者 : cnblogs 心中無碼

延伸 · 閱讀

精彩推薦
法甲赛程积分