国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
讀取硬盤前的準(zhǔn)備工作有哪些?
讀取硬盤數(shù)據(jù)到內(nèi)存中,是操作系統(tǒng)的一個基礎(chǔ)功能。

讀取硬盤需要有塊設(shè)備驅(qū)動程序,而以文件的方式來讀取則還有要再上面包一層文件系統(tǒng)。

把讀出來的數(shù)據(jù)放到內(nèi)存,就涉及到內(nèi)存中緩沖區(qū)的管理。

上面說的每一件事,都是一個十分龐大的體系,我們今天的文章一個都不展開講,哈哈。

我們就講講,讀取塊設(shè)備與內(nèi)存緩沖區(qū)之間的橋梁,塊設(shè)備請求項的初始化工作。

我們以 Linux 0.11 源碼為例,發(fā)現(xiàn)進入內(nèi)核的 main 函數(shù)后不久,有這樣一行代碼。
void main(void) {
    ...
    blk_dev_init();
    ...
}
看到這個方法的全部代碼后,你可能會會心一笑,也可能一臉懵逼。
void blk_dev_init(void) {
    int i;
    for (i=0; i<32; i++) {
        request[i].dev = -1;
        request[i].next = NULL;
    }
}
這也太簡單了吧?
 
就是給 request 這個數(shù)組的前 32 個元素的兩個變量 devnext 附上值,看這倆值 -1NULL 也可以大概猜出,這是沒有任何作用時的初始化值。
 
我們看下 request 結(jié)構(gòu)體。
/*
 * Ok, this is an expanded form so that we can use the same
 * request for paging requests when that is implemented. In
 * paging, 'bh' is NULL, and 'waiting' is used to wait for
 * read/write completion.
 */

struct request {
    int dev;        /* -1 if no request */
    int cmd;        /* READ or WRITE */
    int errors;
    unsigned long sector;
    unsigned long nr_sectors;
    char * buffer;
    struct task_struct * waiting;
    struct buffer_head * bh;
    struct request * next;
};
注釋也附上了。
 
哎喲,這就有點頭大了,剛剛的函數(shù)雖然很短,但看到這個結(jié)構(gòu)體我們知道了,重點在這呢。
 
這也側(cè)面說明了,學(xué)習(xí)操作系統(tǒng),其實把遇到的重要數(shù)據(jù)結(jié)構(gòu)牢記心中,就已經(jīng)成功一半了。比如主內(nèi)存管理結(jié)構(gòu) mem_map,知道它的數(shù)據(jù)結(jié)構(gòu)是什么樣子,其功能也基本就懂了。
 
收,繼續(xù)說這個 request 結(jié)構(gòu),這個結(jié)構(gòu)就代表了一次讀盤請求,其中:

dev 表示設(shè)備號,-1 就表示空閑。

cmd 表示命令,其實就是 READ 還是 WRITE,也就表示本次操作是讀還是寫。

errors 表示操作時產(chǎn)生的錯誤次數(shù)。

sector 表示起始扇區(qū)。

nr_sectors 表示扇區(qū)數(shù)。

buffer 表示數(shù)據(jù)緩沖區(qū),也就是讀盤之后的數(shù)據(jù)放在內(nèi)存中的什么位置。

waiting 是個 task_struct 結(jié)構(gòu),這可以表示一個進程,也就表示是哪個進程發(fā)起了這個請求。

bh 是緩沖區(qū)頭指針,這個后面講完緩沖區(qū)就懂了,因為這個 request 是需要與緩沖區(qū)掛鉤的。

next 指向了下一個請求項。

 
這里有的變量看不懂沒關(guān)系。
 
不過我們倒是可以基于現(xiàn)有的重點參數(shù)猜測一下,比如讀請求時,cmd 就是 READ,sector nr_sectors 這倆就定位了所要讀取的塊設(shè)備(可以簡單先理解為硬盤)的哪幾個扇區(qū),buffer 就定位了這些數(shù)據(jù)讀完之后放在內(nèi)存的什么位置。

這就夠啦,想想看,這四個參數(shù)是不是就能完整描述了一個讀取硬盤的需求了?而且完全沒有歧義,就像下面這樣。
 
 
而其他的參數(shù),肯定是為了更好地配合操作系統(tǒng)進行讀寫塊設(shè)備操作嘛,為了把多個讀寫塊設(shè)備請求很好地組織起來。這個組織不但要有這個數(shù)據(jù)結(jié)構(gòu)中 hb 和 next 等變量的配合,還要有后面的電梯調(diào)度算法的配合,僅此而已,先點到為止。
 
總之,我們這里就先明白,這個 request 結(jié)構(gòu)可以完整描述一個讀盤操作。然后那個 request 數(shù)組就是把它們都放在一起,并且它們又通過 next 指針串成鏈表。
 
 
好,本文講述的兩行代碼,其實就完成了上圖所示的工作而已。

但講到這就結(jié)束的話,很多同學(xué)可能會不太甘心,那我就簡單展望一下,后面讀盤的全流程中,是怎么用到剛剛初始化的這個 request[32] 結(jié)構(gòu)的。

讀操作的系統(tǒng)調(diào)用函數(shù)是 sys_read,源代碼很長,我給簡化一下,僅僅保留讀取普通文件的分支,就是如下的樣子。
int sys_read(unsigned int fd,char * buf,int count) {
    struct file * file = current->filp[fd];
    struct m_inode * inode = file->f_inode;
    // 校驗 buf 區(qū)域的內(nèi)存限制
    verify_area(buf,count);
    // 僅關(guān)注目錄文件或普通文件
    return file_read(inode,file,buf,count);
}
看,入?yún)?fd 是文件描述符,通過它可以找到一個文件的 inode,進而找到這個文件在硬盤中的位置。


另兩個入?yún)?buf 就是要復(fù)制到的內(nèi)存中的位置,count 就是要復(fù)制多少個字節(jié),很好理解。

鉆到 file_read 函數(shù)里繼續(xù)看。
int file_read(struct m_inode * inode, struct file * filp, char * buf, int count) {
    int left,chars,nr;
    struct buffer_head * bh;
    left = count;
    while (left) {
        if (nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE)) {
            if (!(bh=bread(inode->i_dev,nr)))
                break;
        } else
            bh = NULL;
        nr = filp->f_pos % BLOCK_SIZE;
        chars = MIN( BLOCK_SIZE-nr , left );
        filp->f_pos += chars;
        left -= chars;
        if (bh) {
            char * p = nr + bh->b_data;
            while (chars-->0)
                put_fs_byte(*(p++),buf++);
            brelse(bh);
        } else {
            while (chars-->0)
                put_fs_byte(0,buf++);
        }
    }
    inode->i_atime = CURRENT_TIME;
    return (count-left)?(count-left):-ERROR;
}
整體看,就是一個 while 循環(huán),每次讀入一個塊的數(shù)據(jù),直到入?yún)⑺蟮拇笮∪孔x完為止。

直接看 bread 那一行。
int file_read(struct m_inode * inode, struct file * filp, char * buf, int count) {
    ...
    while (left) {
        ...
        if (!(bh=bread(inode->i_dev,nr)))
    }
}
這個函數(shù)就是去讀某一個設(shè)備的某一個數(shù)據(jù)塊號的內(nèi)容,展開進去看。
struct buffer_head * bread(int dev,int block) {
    struct buffer_head * bh = getblk(dev,block);
    if (bh->b_uptodate)
        return bh;
    ll_rw_block(READ,bh);
    wait_on_buffer(bh);
    if (bh->b_uptodate)
        return bh;
    brelse(bh);
    return NULL;
}
其中 getblk 先申請了一個內(nèi)存中的緩沖塊,然后 ll_rw_block 負(fù)責(zé)把數(shù)據(jù)讀入這個緩沖塊,進去繼續(xù)看。
void ll_rw_block(int rw, struct buffer_head * bh) {
    ...
    make_request(major,rw,bh);
}

static void make_request(int major,int rw, struct buffer_head * bh) {
    ...
if (rw == READ)
        req = request+NR_REQUEST;
    else
        req = request+((NR_REQUEST*2)/3);
/* find an empty request */
    while (--req >= request)
        if (req->dev<0)
            break;
    ...
/* fill up the request-info, and add it to the queue */
    req->dev = bh->b_dev;
    req->cmd = rw;
    req->errors=0;
    req->sector = bh->b_blocknr<<1;
    req->nr_sectors = 2;
    req->buffer = bh->b_data;
    req->waiting = NULL;
    req->bh = bh;
    req->next = NULL;
    add_request(major+blk_dev,req);
}
看,這里就用到了剛剛說的結(jié)構(gòu)咯。

具體說來,就是該函數(shù)會往剛剛的設(shè)備的請求項鏈表 request[32] 中添加一個請求項,只要 request[32] 中有未處理的請求項存在,都會陸續(xù)地被處理,直到設(shè)備的請求項鏈表是空為止。

具體怎么讀盤,就是與硬盤 IO 端口進行交互的過程了,可以繼續(xù)往里跟,直到看到一個 hd_out 函數(shù)為止,本講不展開了。

具體讀盤操作,后面會有詳細(xì)的章節(jié)展開講解,本講你只需要知道,我們在 main 函數(shù)的 init 系列函數(shù)中,通過 blk_dev_init 為后面的塊設(shè)備訪問,提前建立了一個數(shù)據(jù)結(jié)構(gòu),作為訪問塊設(shè)備和內(nèi)存緩沖區(qū)之間的橋梁,就可以了。

------


本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
從源碼學(xué)習(xí)Linux linux0.12中bread函數(shù)流程
標(biāo)題:LINUX塊設(shè)備分析
Linux文件系統(tǒng)之文件的讀寫(續(xù)二)
Linux文件系統(tǒng)(五)
Linux0.11 文件系統(tǒng)namei.c詳解
linux
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服