glusterfs變量

ec_readv、ec_writev

ec_writev

爲什麼count==1?
它是上層應用決定的,看一下writev系統是怎麼定義的

offset、vector、size
offset偏移

發數據,回來的時候會報錯寫入失敗,爲什麼呢?
因爲在EC比較特殊,寫入的數據量是要經過切割分發到下層的brick的,但是cbk返回的時候呢(返回值應該等於上層應用except寫入的量纔可以)
答:業務寫多少,就應該返回多少(應用層想寫4K,你給它返回8K或者1K,那肯定不對啊!)

[modify] vector、count已經保存在local裏面了,自己在cbk的unwind中計算(不應該提前保存在local中)

wind發送後,cbk返回的次序不一定,要按照cookie(child_idx)去取出來

[note]一定要加校驗
往下寫的時候,返回的一定必須是寫的個數
往下讀的時候,返回的一定必須是讀的個數
對比:讀寫的大小是否一樣

local->cbk_data變量:保存了每個cbk返回後的信息,用於最後整合所有cbk返回的數據

對於read:在cbk_data中加struct iovec* read_vector

linux命令

dd:指定偏移skip (跳過與bs有關)


鎖在gfs中是怎麼用的?有的需要加鎖,有的時候不需要加鎖

1、local相當於一個全局變量,wind了6次請求,在cbk時,它們可能是同時返回的,必須加鎖
2、cbk的時候是多線程的,一個wind是單線程的(2個wind是多線程的)
local對這次IO來說是全局的,但是wind的private是全局的,因爲private是所有的wind都能訪問到的
inode是所有的wind都能訪問到的,所以操作inode時,就要加鎖
local只是每一次fops操作能看得到
private是所有的fops操作能看得到


lookup

在每一個操作之前都要通過lookup來找到它的inode(可以理解爲每一個文件都是一個inode,讀寫等fops操作實際上就是操作這個inode),其它的fops操作在操作前要先得到這個inode,比如我們發一個cat操作,會先調用read,之後呢,內核會先調用lookup找到這個inode,它拿到這個inode之後纔會調用內核的read操作
再拿create文件來講,在調用create後,會先通過lookup查找這個inode,當找不到這個inode時,就會創建一個inode。如果找得到呢,就會報FILE已經存在的錯誤

afr_loopup幹了啥

afr維護了一個副本,之前畫的那個圖(在afr上層看到的就是一個文件,在afr下面看到的實際上是3個文件),也就是說afr會維護3個文件的一致性,可以理解afr就是把一個文件拆成3份(這3個數據都是一樣的),這就和EC比較像了,實際上,EC也是管理多個文件,只不過它做的動作不一樣,就是說在afr這裏,在它的讀、寫進來的時候,它是把3個一樣的參數發到不同的副本,而EC那裏就不一樣,EC是通過條帶將數據切割後發送到不同的brick。
而lookup操作大致上是一樣的,它都會發給孩子節點,afr、EC的孩子是vclnt

afr和ec的上層只能看到一個節點,而afr、ec下面實際上維護了多個節點(即vclnt),afr、ec對下層要對數據進行切割、聚合,對上層返回的時候,要返回一個上層能看到的一個節點

frame 幀:

gdb的bt打出來就是一個堆棧stack,stack裏面的每一個函數,實際上就是一個個frame,跳到某一層就是f ,在afr層的frame這一層,看到的就是afr這一層的frame,上層業務發了一個讀請求,到達nfs後,會創建一個stack(stack是一個全局的東西,但它到每一層的時候會創建一個自己的frame,比如到afr層會創建afr的frame),這個frame呢,在afr的dispatch、cbk時能看的到,會看到一個同樣的frame

在frame裏面有一個local

local是void的,爲什麼這樣設計呢?因爲每層都需要定義不同的變量,如nfs會有自己需要的參數、afr會有自己需要的參數,因此它定義的是一個void的變量,而不是一個比較通用的變量類型。(簡單的說:實際上是void*萬能指針的用法),這樣,每一個xlator自己去生成自己的local就可以了,local裏面的成員變量自己去定義就可以了。

爲什麼會用這個local呢? 因爲每個請求會向下發,發完之後因爲是異步的,在網絡那裏會直接返回,(異步就是在函數調用的時候,它就會returen 0了,但是直到服務端處理完成後,纔會調用註冊的cbk回調函數。(在cbk怎麼和之前wind聯繫起來呢,實際上是通過frame)
假設一個請求要經過3個模塊nfs、afr、vclnt,那麼,這3個模塊的stack是同一個,可以理解成一個全局的stack,每一個模塊都會生成自己的frame,當調到vclnt後,vclnt就會註冊一個cbk函數,之後wind後函數就會返回了。(函數雖然返回了,但是並沒有告訴業務這個時候返回了。當底層服務端處理完成後,就會調用回調函數,回調函數就會把底下的參數保存起來,就會調stack那一層的frame。也就是說vclnt這層的cbk拿到了之前wind的frame,[在cbk看到的frame和之前wind的frame實際上是同一個]),之後,vclnt調用完成後會繼續往上調。我們在wind時保存一個frame->local,就可以在cbk中拿到這個frame->local。(比方說,業務在讀的時候會告訴我們它想讀多大,那麼我們就可以在wind之前將該信息保存到local中,當cbk時就可以在local中將其取出來,看一下已讀到的大小是否等於想讀到的大小)

void* local的好處:萬能指針,壞處:每次用之前都要強轉

loc

對文件的操作實際上操作的是inode,那爲什麼會有local這個東西呢?因爲文件它又兩種方式,case1:打開文件去做操作;case2:沒打開文件去做操作。
對於打開的文件,我們推薦使用fd去做操作(因爲fd是一一映射,通過它會快很多)
對於未打開的文件,推薦使用loc去操作(loc要通過路徑去解析,要一層一層去解析,它會較慢)
其實loc和fd仔細看,裏面都有一個inode,封裝的loc和fd實際上就是封裝的inode(其實inode纔是最關鍵的東西,操作都會通過inode去操作那個文件)

填充路由

路由是什麼?ec對應6個孩子,但是6個孩子怎麼對應到下面的6個brick,因爲brick是很多個的,brick可能有100個,但是每個文件可能只會存在某6個brick上,那是什麼去保存這個信息,就是通過路由。路由是route層來維護的,它就是去找這個文件到底存在那幾個brick上的(只要調用這個函數能找到brick就行了)
通過inode就可以得到路由,因爲路由信息是存在結構體inode中的。路由比較特殊,很多模塊都會用到,所以就提到剛剛講到的那一點,inode就是一個全局的。frame只在這一層可見,這個frame在上一層、下一層它是看不到的!所以這個frame在某個fop時是一個局部變量,只能在自己的wind和自己的cbk中看的到。frame是沒法在模塊之間傳遞的,也就是說,更高一層想要給更第一層傳下去的時候,通過frame是帶不下來的。因爲它們都是自己的(但是通過stack是可以傳下來的,但是我們一般不會通過stack去傳,frame的第一個參數就是stack)

inode 所有模塊都能訪問

所有模塊都能看到這個inode,它是全局的,一個文件只有一個inode,它就是一個gluster客戶端的全局變量
在route層將路由信息放在inode中,這樣,不管是afr還是ec都可以在inode中獲取到路由信息

變量作用域!

inode 所有模塊都能訪問
在縮小一點,就是private,就是這個某個模塊的全局變量(比如該模塊的讀、寫都能訪問的到)
再小一點,就是frame,它是這一次操作能訪問到的(local是frame中的成員,它作用域和frame是平級的)
在小一點,就是函數中的局部變量

作用域時十分關鍵的,要自己去體會!
比如你的信息可能在模塊間要被用到的時候,那麼就要把它放在inode中
比如你的信息它在下一次fop要用的到的時候,那麼就要把它放在priate或者inode中,這樣下一次就可以訪問的到
比如你的信息只在wind和unwind時用的到,那麼就放在frame中

還有一種作用域場景:我只希望這個模塊能訪問到,但是又不是private,那麼應該存在哪裏呢?爲什麼?
因爲private是不和文件綁定的,也就是說,任何文件都能訪問到!如果我現在有一個信息,只希望這個文件在這個模塊能訪問到,那其實就是inode_ctx!

變量存在哪的決定因素? 作用域最小化(不應該被不想讓看到的模塊看到,讓想看到的都看到)

字典dict_t:很常用

dict_get(xattr_req, VS_ROUTE_FRESH_LOOKUP)
	第一個參數是哪裏來的?這個參數實際上是上層傳下來的。大家可以理解它和stack作用域是一樣的,它會從最頂層一直向下帶,
	實際上是完成2個模塊之間交互的。
	5123行的含義
		在xattr_req字典裏查找是否有key=VS_ROUTE_FRESH_LOOKUP,
		如果有,就將local->cont.lookup.fresh_lookup設置爲_gf_true
		
		這個值是在哪裏設置的呢?實際上,大家去找就可以發現,該key是在route層設置的,也就是說,這個是在它的上一層設置的,是爲了模塊之間的通信。即:route層想要這一次的lookup根據cont.lookup.fresh_lookup做不同的動作,就將該key的值設置進去,在afr層就去在字典裏找這個key,就會根據找到或者找不到,做出不同的動作,完成2個模塊之間的通信。
		記住:取出來之後,要把該key從字典中刪掉(是什麼意思呢?就是說這個字典的用法只在該層和更上層之間通信,不在在下層通信,用完要刪掉)如果不刪除,會殘留到下面,下層可能就會對它有誤解,之前調代碼的時候,沒有刪除它,導致報錯出core!
		用完之後,要刪掉!
		
		字典的傳輸路線是隨着wind和unwind單向的,是沿着主線一直走的(這點和xxxxx不同)

應用場景:
1、EC不過緩存讀的時候,EC就可以在往下wind的時候設置一個key,在緩存這層就去取出key,如果有該key,就跳過緩存
2、比如IO不想過分層,就可以在該層通過key做一個標記,當IO到達分層時,就獲取該key,如果獲取到就跳過分層

gfs層次是比較分明的,在層次之間就做了這樣一個機制。它是最常用的一個通信的東西!

child_up、call_cnt

在發之前,要先獲取孩子在線的情況(即填充child_up這個數組變量)。不在線的是沒必要發的。

STACK_WIND_COOKIE

  1. new一個新的frame給下一層:_new
  2. 保存cbk,在STACK_UNWIND中被調用
  3. _new->root = frame->root 把stack賦值給下一層的frame->root
  4. THIS是全局變量,二維指針,之前瞭解過,使用TLS來維護的,每一層的THIS都是該層的this
  5. 調用下層的fop
    爲什麼不把cookie放在local裏,下去想一想。

STACK_UNWIND_STRICT

  1. 先取出它的parent: _parent = frame->parent
  2. 之前傳來的cookie
  3. 調用之前註冊的cbk

lookup 一個線程同步跑

lookup_cbk 多個線程在跑(因此要對local中的變量加鎖)

afr是向下層發wind,多次cbk返回,因此要將多次cbk返回的參數都提前保起來,當收集完所有的cbk信息後,再根據業務場景對之前保存的參數進行構造,構造完成後,一次性向上發送&返回

同理,ec的read/write要將cbk返回的信息保存起來,當最後一個孩子也返回時(call_cnt==0),開始拼接起來,向上返回,比較噁心。

afr、ec的上層只要理解下層有一個就行了,afr、ec自己維護了多個,不讓上層感知&看見

fops函數參數:都是上層傳過來的,我們在這層取出來參數信息,在本層進行相關操作

cbk函數參數:都是下層返回來的,下層進行填充
op_ret:下層調用的結果,即返回值。返回是否成功、讀寫的數據量等信息,每個fops操作的返回含義都不一樣。
op_errno: 下層返回來的錯誤碼

變量的含義

struct iatt是幹啥的啊?

ec_writev參數講解

  1. 參數有fd的,說明它之前open過
  2. vector、count它倆是一套,決定數據塊的內容和長度
    有幾個count,就有幾個struct iovec
    struct iovec有iov_base\iov_len
  3. struct iobref 很重要,什麼用呢?維護數據的引用計數ref
    看字面意思:iobref,是對io做ref
    struct iovec內核並沒有做引用計數,用iobref來做
    說明:這層用到數據塊,在wind前,就ref;unwind時,要unref
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章