一起學習MySQL源碼筆記之偷窺線程

來源:互聯網
上載者:User

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

 
  1. int pthread_create(pthread_t *thread_id, pthread_attr_t *attr,  
  2.    pthread_handler func, void *param)  
  3. {  
  4.   HANDLE hThread;  
  5.   struct pthread_map *map;  
  6.   DBUG_ENTER("pthread_create");  
  7.   if (!(map=malloc(sizeof(*map))))  
  8.     DBUG_RETURN(-1);  
  9.   map->func=func;  
  10.   map->param=param;  
  11.   pthread_mutex_lock(&THR_LOCK_thread);  
  12. #ifdef __BORLANDC__  
  13.   hThread=(HANDLE)_beginthread((void(_USERENTRY *)(void *)) pthread_start,  
  14.                    attr->dwStackSize ? attr->dwStackSize :  
  15.                    65535, (void*) map);  
  16. #else 
  17.   hThread=(HANDLE)_beginthread((void( __cdecl *)(void *)) pthread_start,  
  18.                    attr->dwStackSize ? attr->dwStackSize :  
  19.                    65535, (void*) map);  
  20. #endif  
  21.   DBUG_PRINT("info", ("hThread=%lu",(long) hThread));  
  22.   *thread_id=map->pthreadself=hThread;  
  23.   pthread_mutex_unlock(&THR_LOCK_thread);  
  24.   if (hThread == (HANDLE) -1)  
  25.   {  
  26.     int error=errno;  
  27.     DBUG_PRINT("error",  
  28.            ("Can't create thread to handle request (error %d)",error));  
  29.     DBUG_RETURN(error ? error : -1);  
  30.   }  
  31.   VOID(SetThreadPriority(hThread, attr->priority)) ;  
  32.   DBUG_RETURN(0);  
  33. }  

關鍵的代碼是下面三句:

 
  1. map->func=func;  
  2. map->param=param;  
  3. _beginthread((void( __cdecl *)(void *)) pthread_start,  
  4.                    attr->dwStackSize ? attr->dwStackSize :  
  5.                    65535, (void*) map);  

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

 
  1. 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需要的線程了,下次針對這個線程,分析下如何進行登入的,以及登入後為使用者指派哪些資源。時間不早啦,洗洗睡了

相關文章

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.