1. 程式人生 > >linux核心posix檔案鎖實現

linux核心posix檔案鎖實現

       最近由於在我們的檔案系統中需要實現posix檔案鎖,因此研究了下linux核心和gluster的檔案鎖的實現的程式碼。主要關注posix檔案鎖。整理了相關的學習內容,概念方面主要摘自網路和將系統核心的兩本書(《linux核心深入理解》和《核心情景分析》)。程式碼部分參考了書的描述,然後閱讀了相關程式碼。

       主要參考網址,請搜尋《Linux 2.6 中的檔案鎖》

1基本概念

1.1鎖的型別

按讀寫特性:共享鎖(讀鎖)和排它鎖(寫鎖)

按鎖的工作方式:強制鎖、勸告鎖、共享模式鎖、租借鎖

1.1.1勸告鎖

核心只提供加鎖以及檢測檔案是否已經加鎖的方法,但不參與鎖的控制和協調。如果有程序不遵守“遊戲規則”,不檢查目標檔案是否已經由別的程序加了鎖就往其中寫入資料,那麼核心是不會阻攔的。

1.1.2強制鎖

當有系統呼叫 open()read() 以及write() 發生的時候,核心都要檢查並確保這些系統呼叫不會違反在所訪問檔案上加的強制鎖約束。Unlink不會受到強制鎖的影響。

當程序對檔案進行了讀或寫這樣的系統呼叫時,系統則會檢查該檔案已經到加強制鎖時,會判斷檔案的狀態O_NONBLOCK 標識,如果設定了 O_NONBLOCK,則該程序會出錯eagain返回;否則,該程序被阻塞

O_NONBLOCK 標識設定方式是通過fcntl傳遞引數F_SETFL

系統支援強制鎖的配置:

  1. Mount –o mand設定檔案系統是否支援強制鎖(super_block

    結構中s_flags設定為01

  2. 修改要加強制鎖的檔案的許可權:設定 SGID 位,並清除組可執行位。

1.2鎖的釋放

  1. 程序對某個檔案擁有的各種鎖會在檔案對應的檔案描述符被關閉時自動清除;

  2. 程序執行結束後,其所加的各種鎖也會自動清除。

1.3鎖的繼承

  1. fork產生的子程序不會繼承父程序的檔案鎖;

  2. 在執行exec之後,新程式可以繼承原來程式的檔案鎖。

2系統呼叫

2.1資料結構

  1. 系統中所有檔案的活動的鎖都連結在一個全域性的單向連結串列file_lock_list中,inode->i_flock指向連結串列中屬於本檔案的第一個鎖;所有阻塞的鎖被連結在“阻塞列表”

    blocked_list中。file_lock_lock保護這兩個連結串列。

  2. file_locklinux核心描述的所有型別鎖的資料結構

  • fl_next: 如果file_lock插入在file_lock_list,指向連結串列中屬於本檔案的下一個鎖;如果file_lock插入blocked_list,指向產生衝突的當前活動的鎖

  • fl_link: file_lock物件插入到file_lock_listblocked_list所代表的兩個連結串列之一

  • fl_block: 給定的活動鎖的所有阻塞的鎖構成一個迴圈連結串列,活動鎖的fl_block指向連結串列的頭部,阻塞鎖的fl_block指向連結串列中相鄰的下一個元素。

  • fl_wait: 由於與當前的鎖產生衝突被掛起的程序被插入到相應衝突鎖的fl_wait指向等待佇列

  • fl_ownerstruct files_struct * 快速找到程序

  • fl_pid

  • Fl_file/fl_type/fl_flags/fl_start/fl_end….

2.2fcntl

Poxis檔案鎖通過Fcntl系統呼叫實現:int fcntl (int fd, intcmd, struct flock *lock);

struct flock {

...

short l_type;/* Type of lock: F_RDLCK, F_WRLCK, F_UNLCK */

short l_whence;/* How tointerpret l_start:

SEEK_SET,SEEK_CUR, SEEK_END */

off_t l_start;/* Startingoffset for lock */

off_t l_len;/* Number ofbytes to lock */

pid_t l_pid;/* PID of processblocking our lock

(F_GETLK only)*/

...

};

2.2.1強制鎖

F_GETLK/ F_SETLK/ F_SETLKW

  1. F_GETLK:獲取相應型別的鎖,如果存在,返回相應鎖的資訊(包括l_pid設定為加鎖程序的PID;如果不存在,l_type被賦值為F_UNLCK

    fcntl_getlk

  • flock_to_posix_lock

  • vfs_test_lockfilp->f_op->lock或者posix_test_lock(遍歷inode上的所有Posix鎖,判斷是否衝突)

  • 如果存在衝突的鎖(file_lock.fl_type!= F_UNLCK),posix_lock_to_flock

      2. F_SETLK:設定鎖(l_typeF_RDLCK, F_WRLCKF_UNLCK),如果檔案已經到其它鎖鎖定,直接返回eagain

F_SETLKW:設定鎖,如果檔案已經到其它鎖鎖定,阻塞,進入睡眠;

         fcntl_setlk

  • 如果要加讀鎖,檔案必須被讀模式開啟,如果要開寫鎖,檔案必須寫模式開啟

  • flock_to_posix_lock

  • if (cmd == F_SETLKW) 設定file_lock->fl_flags |= FL_SLEEP;

  • vfs_lock_file 如果檔案系統是否提供lock介面,呼叫;否則呼叫posix_lock_file

  • wait_event_interruptible(fl->fl_wait, !fl->fl_next);

2.2.2租借鎖

F_SETLEASE F_GETLEASE 可以實現租約鎖,具體引數(F_RDLCKF_WRLCKF_UNLCK

某個程序可能會對檔案執行其他一些系統呼叫(比如 OPEN() 或者 TRUNCATE()),如果這些系統呼叫與該檔案上由 F_SETLEASE 所設定的租借鎖相沖突,核心就會阻塞這個系統呼叫;同時,核心會給擁有這個租借鎖的程序發訊號。擁有此租借鎖的程序會對該訊號進行處理,可能會刪除或縮短這個租借鎖。

如果擁有租借鎖的程序不能在給定時間內完成上述操作,那麼系統會強制完成。通過F_SETLEASE 命令將 arg 引數指定為 F_UNLCK 就可以刪除這個租借鎖。

即使被阻塞的系統呼叫因為某些原因被解除阻塞,但是對租借鎖的租約進行減短或刪除這個過程還是會執行的

租借鎖也只能對整個檔案生效,而無法實現記錄級的加鎖。

2.3Open

open_namei()

如果open的標誌包括trunc,會通過locks_mandatory_locked檔案是否加了poxis鎖,MANDATORY_LOCK判斷檔案是否支援強制鎖,然後遍歷所有鎖,比較其中強制鎖的owner是否是當前,如果不是,返回錯誤eagain

2.4close

filp_close()呼叫locks_remove_posix

  • locks_remove_posix (struct file *filp, fl_owner_t owner)

owner is the POSIX threadID. We use the files pointer for this.(files_struct)

  • 首先生成file_lock物件,fl_type型別為F_UNLCK呼叫,lock.fl_owner= owner; lock.fl_pid = current->tgid;

  • 然後呼叫vfs_lock_file如果檔案系統是否提供lock介面,呼叫;否則呼叫posix_lock_file

2.5Read/write/truncate

       locks_mandatory_area

  • 構建file_lock

fl.fl_flags= FL_POSIX | FL_ACCESS;

if (filp && !(filp->f_flags & O_NONBLOCK))

fl.fl_flags|= FL_SLEEP;

fl.fl_type= (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;

  • __posix_lock_file

  • wait_event_interruptible(fl.fl_wait, !fl.fl_next); 判斷fl_next(如果產生鎖衝突,fl被插入blocked_listfl_next會被賦值),如果不為空,插入fl_wait的等待佇列。

     注意:沒有呼叫vfs_lock_file.

2.6__posix_lock_file

  1. file_lock_lock加鎖

  2. 遍歷所有的lock,如果fl_type不是F_UNLCK,檢查是否產生衝突,如果是,判斷fl_flags是否設定FL_SLEEP(返回-EAGAIN),是否死鎖(-EDEADLK),locks_insert_block插入等待列表。

    locks_insert_block

  • 將當前鎖的fl_block插入到衝突的鎖的fl_block連結串列的尾部;

  • 將當前鎖的fl_next設定為衝突鎖;

  • 將當前鎖fl_link插入blocked_list中;

    3.如果request->fl_flags & FL_ACCESS,退出

    4.inode上找到相同的own的的第一個鎖,然後如果是相同型別的鎖,判斷是否進行合併;如果是不同型別的鎖,交叉部分,新鎖總是覆蓋舊鎖的型別(舊鎖的部分要wake_up上面阻塞的鎖),然後舊鎖可能產生拆分。

   5. 由於合併或覆蓋或F_UNLCK等原因需要呼叫locks_delete_lock釋放鎖。

  • fl->fl_next = NULL;

  • list_del_init(&fl->fl_link);

  • fl_fasync

  • locks_wake_up_blocks:遍歷fl_block指向的連結串列上的鎖,reset fl_linkfl_blockwake_up(&waiter->fl_wait); 通知阻塞的程序

  • locks_free_lock:釋放記憶體空間

   6. 把可能產生的拆分和新的file_lock插入(locks_insert_lock)全域性鎖鏈表和索引節點連結串列中

locks_insert_lock

  • 將當前鎖的fl_link插入到file_lock_list中;

  • 將當前鎖的fl_next設定為通過inodei_flock遍歷的插入節點位置pos,同時將*pos設定為當前鎖;

     7.  file_lock_lock解鎖



相關推薦

linux核心posix檔案實現

       最近由於在我們的檔案系統中需要實現posix檔案鎖,因此研究了下linux核心和gluster的檔案鎖的實現的程式碼。主要關注posix檔案鎖。整理了相關的學習內容,概念方面主要摘自網路和將系統核心的兩本書(《linux核心深入理解》和《核心情景分析》)。程式

Linux核心-核心層互斥實現

核心層程式碼實現 #include <linux/module.h> #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/fs.h&

Linux 核心自旋

  為什麼需要核心自旋鎖? 現在很多CPU都是幾核幾核的了,如果有一個變數A,CPU-X正在訪問,突然CPU-Y也過來訪問他,這時候就可能出現問題,因為這個A非常重要,可能導致系統崩潰,中斷異常等。 我們來看之前說的TP驅動裡面的程式碼 void gtp_irq_ena

linux中的檔案(勸告性上和強制性上)

上午在看UNP卷二這一節的時候及其想睡覺,就草草了事,夜晚沒有事情幹,就來找找部落格看看這兩個鎖到底是怎麼回事吧! 參考文章:https://www.ibm.com/developerworks/cn/linux/l-cn-filelock/index.html 背景知識:在早期的

linux使用flock檔案解決crontab衝突問題

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

linux核心自旋和中斷知識講解

一、併發與競態三個要點 1、只要併發的執行單元同時訪問共享記憶體是就會出現競態 2、解決競態的唯一途徑是保證共享資源的互斥訪問,即一個執行單元在訪問共享資源時,其他的執行單元被禁止訪問。 3、

Linux核心多執行緒實現方法

參考:http://blog.csdn.net/sharecode/article/details/40076951 1.建立執行緒方法1 kthread_create:       建立執行緒;改函式建立執行緒後,不會馬上執行

Linux核心中的

1. 為什麼要保證原子性 處理器分兩種:cisc(複雜指令集,可以直接在記憶體上進行操作,如x86,一條彙編指令可以原子的完整讀記憶體、計算、寫記憶體)和rics(精簡指令集,所有操作都必須是在CPU內部進行。所以你想給記憶體某個變數做加法,你要先用load指

Linux核心檔案操作函式整理

1.判斷檔案是否存在 struct file *filp = NULL; filp = filp_open("/etc/passwd", O_RDONLY, 0); if (IS_ERR(filp)) { printk("Cannot open ...

linux核心連結串列的實現和使用和詳解

首先,核心連結串列的標頭檔案,在linux核心的 /include/linux 下的 List.h ,把List.h 複製出來,黏貼到 工程下,就可以直接用核心連結串列的一些巨集和函式。 以下介紹核心連結串列的一些巨集和函式,已經他的實現方式和使用方法。 (1)什麼是核心

排他檔案實現(Java版本)

一 .前言 某年某月某天,同事說需要一個檔案排他鎖功能,需求如下: (1)寫操作是排他屬性 (2)適用於同一程序的多執行緒/也適用於多程序的排他操作 (3)容錯性:獲得鎖的程序若Crash,不影響到後續程序的正常獲取鎖 二 .解決方案 1. 最初的構想 在Java領域,同

Linux核心檔案系統移植之jffs2燒錄後無法啟動

近一週的時候都在玩linux 核心及檔案系統移植,使用的版本如下: Bootloader: u-boot-2010.06.tgz Kernel:   linux-3.0.y.tgz

Linux核心使用ioctl函式實現使用者態命令

驅動程式: /******************************** * GPIO驅動程式控制GPIO介面高低電平 * 基於gpio庫,四個GPIO識別為一個裝置 * 使用miscdevice結構體動態分配裝置號,自動建立/dev/檔案 * 使用ioc

Linux核心查詢檔案操作函式的過程

先根據路徑找到父目錄項,然後找到對應的i_node,i_ndoe的成員 file_operations * i_fop是指向檔案操作函式集的指標。 在建立檔案的i_node時會設定 file_operations * i_fop的值。一般預設使用init_spec

Linux核心閱讀--檔案路徑查詢(二)

Linux檔案路徑查詢的基本策略,是從查詢根(一般是根目錄或當前目錄)開始,逐級向下查詢。具體到程式碼中,每個查詢的節點被表示為path,path的定義如下。 struct path { struct vfsmount *mnt; struct

LinuxPOSIX 檔案 I/O 操作函式

(1)檔案描述符與檔案流轉換操作 /*成功返回檔案描述符,失敗返回-1*/ int fileno(FILE* stream); /*成功返回檔案流,失敗返回NULL*/ FILE* fdopen(int fd,char* mode); (2)修改檔案描

Linux kernel FAT32檔案系統實現

1.     FAT表操作 FAT檔案系統中,使用FAT表標記哪個cluster被佔用,哪個沒被佔用。在Linux核心程式碼中,與FAT表操作對應的是fat_entry,fatent_ops結構和fat_cache_id快取等。 1.1 fat_entry fat中的f

Linux核心讀取檔案流程原始碼及阻塞點超詳解

以linux核心3.13版本為例,首先核心通過系統呼叫read(),執行sys_read()函式,在檔案linux/fs/read_write.c中: //linux/fs/read_write.c SYSCALL_DEFINE3(read, unsigne

Linux 搭建NFS檔案伺服器實現檔案共享

我們接著玩Linux,O(∩_∩)O哈哈~ 1.什麼是nfs NFS(Network File System)即網路檔案系統,是FreeBSD支援的檔案系統中的一種,它允許網路中的計算機之間通過TCP/IP網路共享資源。在NFS的應用中,本地NFS的客戶端應用可以透明地讀寫位於遠端NFS伺服器上的檔案,就像

linux 中的檔案

Linux執行多個程序同時對一檔案進行讀寫,雖然每一個read和write都是原子操作,但核心並沒有在兩個讀寫操作之間加以同步。因此,當一個程序多次呼叫read讀檔案時,就有可能在某兩次讀之間被另一程序所寫,因此,讀的的值將發生錯誤,造成了檔案資料的隨機性衝突,為解決此類併