170 likes | 314 Views
System call – read/write. read(fd, buf, count) Kernel 開啟 fd(file descriptor) 所指的 file object ,從 file object 的 f_pos 欄位開始讀取 count 個 bytes 的資料到 buf 中。 write(fd, buf, count). read/write source code. sys_read - fs/read_write.c.
E N D
System call – read/write • read(fd, buf, count) • Kernel開啟fd(file descriptor)所指的file object,從file object的f_pos欄位開始讀取count個bytes的資料到buf中。 • write(fd, buf, count)
sys_read - fs/read_write.c • asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count) • { • struct file *file; • ssize_t ret = -EBADF; • int fput_needed; • file = fget_light(fd, &fput_needed); • if (file) { • loff_t pos = file_pos_read(file); • ret = vfs_read(file, buf, count, &pos); • file_pos_write(file, pos); • fput_light(file, fput_needed); • } • return ret; • }
struct file fastcall *fget_light(unsigned int fd, int *fput_needed) • { • struct file *file; • struct files_struct *files = current->files; • *fput_needed = 0; • if (likely((atomic_read(&files->count) == 1))) { • file = fcheck_files(files, fd); • } else { • rcu_read_lock(); • file = fcheck_files(files, fd); • if (file) { • if (atomic_inc_not_zero(&file->f_count)) • *fput_needed = 1; • else • /* Didn't get the reference, someone's freed */ • file = NULL; • } • rcu_read_unlock(); • } • return file; • } • fs/file_tables.c
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) • { • ssize_t ret; • if (!(file->f_mode & FMODE_READ)) • return -EBADF; • if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read)) • return -EINVAL; • if (unlikely(!access_ok(VERIFY_WRITE, buf, count))) • return -EFAULT; • ret = rw_verify_area(READ, file, pos, count); • if (ret >= 0) { • count = ret; • ret = security_file_permission (file, MAY_READ); • if (!ret) { • if (file->f_op->read) • ret = file->f_op->read(file, buf, count, pos); • else • ret = do_sync_read(file, buf, count, pos); • if (ret > 0) { • fsnotify_access(file->f_path.dentry); • current->rchar += ret; • } • current->syscr++; • } • } • return ret; • } • fs/read_write.c
sys_write - fs/read_write.c • asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count) • { • struct file *file; • ssize_t ret = -EBADF; • int fput_needed; • file = fget_light(fd, &fput_needed); • if (file) { • loff_t pos = file_pos_read(file); • ret = vfs_write(file, buf, count, &pos); • file_pos_write(file, pos); • fput_light(file, fput_needed); • } • return ret; • }
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) • { • ssize_t ret; • if (!(file->f_mode & FMODE_WRITE)) • return -EBADF; • if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write)) • return -EINVAL; • if (unlikely(!access_ok(VERIFY_READ, buf, count))) • return -EFAULT; • ret = rw_verify_area(WRITE, file, pos, count); • if (ret >= 0) { • count = ret; • ret = security_file_permission (file, MAY_WRITE); • if (!ret) { • if (file->f_op->write) • ret = file->f_op->write(file, buf, count, pos); • else • ret = do_sync_write(file, buf, count, pos); • if (ret > 0) { • fsnotify_modify(file->f_path.dentry); • current->wchar += ret; • } • current->syscw++; • } • } • return ret; • } • fs/read_write.c
檔案系統的一致性 • Ext2 • 當檔案系統龐大時,檢查需要花費大量時間。 • 對上線環境或高可用性伺服器不適用。 • EXt3 • 日誌(Journal) • 可避免對整個檔案系統做耗時檢查。 • 只需查看特定磁碟區域進行修正。
日誌式檔案系統 • 可解決保持中介資料(metadata)與普通資料區塊的一致性問題。 • 透過兩步驟進行高階修改: • 將要寫入的區塊副本儲存於日誌中 • 當對日誌的I/O資料傳輸完成之後,再將區塊寫入至檔案系統。 • 系統失敗復原 • 在資料交付給日誌之前:e2fsck會直接忽略 • 在資料交付給日誌之後:區塊副本有效,寫入檔案系統 • 需多次寫入磁碟所導致的系統效能損失。 • 只能保持系統呼叫層次的一致性。
日誌式檔案系統 • 三種日誌模式: • 日誌(Journal) • 將所有檔案系統與中介資料的改變都記錄下來。 • 安全性最高,但速度最慢 • 依序(Order) • 只有對中介資料的修改會紀錄,但會將中介資料與相關資料區塊群集起來,資料區塊會在中介資料之前寫入磁碟。 • 預定的Ext3日誌模式。 • 寫回(Writeback) • 只有對中介資料的修改會被記錄。 • 速度最快的模式。 • 可透過 mount 系統命令設定日誌模式
日誌區塊裝置層 • 通常儲存在名為 .journal的隱藏檔案 • 使用一通用核心層: • journaling block device (JBD) • 目前有Ext3、Ext4使用JDB。 • 基本架構: • 日誌紀錄(Log record) • 不可切割運算處理單元(Atomic operation handle) • 交易(Transaction)
日誌紀錄(Log record) • 系統即將發出之低階運算的描述。 • Journal_block_tag_t ( log record型態) • 儲存邏輯區塊編號與狀態編號 • Journal_head • 使用依序(Ordered)日誌模式時,附加到緩衝區的頭端。
不可切割運算處理單元(Atomic operation handle) • Log record的集合,每個系統呼叫都以不可切割方式處理。 • 確保整個高階運算都有實施,或任何低階運算沒有實施。 • Journal_start() • Journal_stop()
交易(Transaction) • 為了效率,會群集幾個不可切割運算處理單元成一個交易。 • 交易內的日誌紀錄須儲存於連續的區塊中。 • 停止接收新的處理單元: • 一段固定時間已過,通常是五秒。 • 日誌中無可用區塊供新的處理單元使用。 • Transaction_t • 描述交易的型態,最重要的是t_state欄位用來表示當前的交易狀態
交易(Transaction) • 完整的 • 交易中所有的log record已經實際寫入於日誌內,t_state欄位儲存的是T_FINSHED • 不完整的 • T_RUNNING • 仍在接收新的處理單元 • T_LOCKED • 不接收新的處理單元,但部分仍未完成 • T_FLUSH • 所有不可切割運算處理單元都已完成,但log records仍在寫入日誌中 • T_COMMIT • 所有log records都已寫入磁碟之內,但尚未標示成完整的狀態。
交易(Transaction) • 在任何時間可以有很多個交易在日誌中,但只能有一個是在執行中。 • 當交易完成後,JBD會驗證由log record所描述的所有緩衝區是否都已成功寫入至磁碟之內,之後才將一個完整的交易從日誌中刪除。