Linux 장치 구동 - 블록 장치 의 유 니 버 설 블록 층

9781 단어 링크 ux 구동
공통 블록 층 안내
Linux 의 유 니 버 설 블록 층 은 커 널 구성 요소 로 시스템 에서 온 모든 블록 장치 의 접근 을 처리 하고 블록 장치 의 접근 을 요청 으로 IO 스 케 쥴 러 층 으로 변환 합 니 다.이 과정 에서 여러 가지 데이터 구조의 전환 과 관련 될 것 입 니 다. 다음은 유 니 버 설 블록 층 과 관련 된 데이터 구조 와 유 니 버 설 블록 층 이 하 는 일 을 토론 하 겠 습 니 다.
유 니 버 설 블록 층 데이터 구조
주: 본 고 에서 언급 한 데이터 구 조 는 모두 Linux 3.0 커 널 의 데이터 구조 이다.
1. bio 구조
bio 설명 자 는 유 니 버 설 블록 층 의 핵심 데이터 구조 로 블록 장치 의 IO 작업 을 묘사 하고 IO 작업 이 디자인 한 디스크 의 저장 구역 식별 자, IO 작업 과 관련 된 메모리 구역 의 세그먼트 정보 등 을 포함한다.bio 초 데이터 구 조 는 다음 과 같다.
struct bio {
    sector_t        bi_sector;    /*  IO           */
    struct bio      *bi_next;    /*            bio */
    struct block_device    *bi_bdev;    /*             */
    unsigned long           bi_flags;    /* bio      */
    unsigned long           bi_rw;        /* IO     */
    unsigned short        bi_vcnt;    /* bio bio_vec       , bio_vec     */
    unsigned short        bi_idx;        /* bio bio_vec        */

    /* Number of segments in this BIO after
     * physical address coalescing is performed.
     */
    unsigned int        bi_phys_segments;    /*     bio      */
    unsigned int        bi_size;             /*          */

    /*
     * To keep track of the max segment size, we account for the
     * sizes of the first and last mergeable segments in this bio.
     */
    unsigned int        bi_seg_front_size;    /*         */
    unsigned int        bi_seg_back_size;     /*         */

    unsigned int        bi_max_vecs;    /* bio bio_vec          */

    unsigned int        bi_comp_cpu;    /* completion CPU */

    atomic_t        bi_cnt;        /* pin count */

    struct bio_vec        *bi_io_vec;    /*   bio bio_vec            */

    bio_end_io_t        *bi_end_io;    /* bio IO           */

    void            *bi_private;       /* bio     */
#if defined(CONFIG_BLK_DEV_INTEGRITY)
    struct bio_integrity_payload *bi_integrity;  /* data integrity */
#endif

    bio_destructor_t    *bi_destructor;    /*   bio        */

    /*
     * We can inline a number of vecs at the end of the bio, to avoid
     * double allocations for a small number of bio_vecs. This member
     * MUST obviously be kept at the very end of the bio.
     */
    struct bio_vec        bi_inline_vecs[0];
};

bio 의 모든 단락 은 biovec 구 조 는 그 필드 가 다음 과 같다 는 것 을 나타 낸다.bio 중의 bioio_vec 필드 에 bio 저장vec 배열 의 첫 번 째 요소 주소, bivcnt 필드 에 bio 저장vec 배열 의 현재 요소 갯 수
struct bio_vec {
    struct page    *bv_page;    /*                */
    unsigned int    bv_len;     /*     ,       */
    unsigned int    bv_offset;  /*            */
};

2. gendisk 구조
gendisk 는 디스크 나 디스크 파 티 션 을 설명 합 니 다.디스크 는 유 니 버 설 블록 으로 정리 되 어 있 는 논리 장치 로 보통 디스크 는 하드웨어 장치 에 대응 합 니 다.gendisk 의 구체 적 인 필드 는 다음 과 같다.
struct gendisk {
    /* major, first_minor and minors are input parameters only,
     * don't use directly.  Use disk_devt() and disk_max_parts().
     */
    int major;            /*        */
    int first_minor;      /*               */
    int minors;           /*              */

    char disk_name[DISK_NAME_LEN];    /*        */
    char *(*devnode)(struct gendisk *gd, mode_t *mode);

    unsigned int events;        /* supported events */
    unsigned int async_events;    /* async events, subset of all */

    /* Array of pointers to partitions indexed by partno.
     * Protected with matching bdev lock but stat and other
     * non-critical accesses use RCU.  Always access through
     * helpers.
     */
    struct disk_part_tbl __rcu *part_tbl;    /*       */
    struct hd_struct part0;                  /*         */

    const struct block_device_operations *fops;    /*        */
    struct request_queue *queue;                   /*             */
    void *private_data;                            /*            */

    int flags;            /*         */
    struct device *driverfs_dev;  // FIXME: remove
    struct kobject *slave_dir;

    struct timer_rand_state *random;
    atomic_t sync_io;        /* RAID */
    struct disk_events *ev;
#ifdef  CONFIG_BLK_DEV_INTEGRITY
    struct blk_integrity *integrity;
#endif
    int node_id;
};

gendisk 구조 에서 fops 필드 는 디스크 조작 방법의 구조 지침 을 가리 키 는데 이런 방법 은 open, release, ioctl 등 을 포함 하고 문자 장치 드라이버 의 fops 구조 와 유사 하 다.gendisk 구조 중 parttbl 필드 는 디스크 의 파 티 션 시트 를 가리 키 며 파 티 션 시트 구조의 구체 적 인 필드 는 다음 과 같 습 니 다.
struct disk_part_tbl {
    struct rcu_head rcu_head;
    int len;
    struct hd_struct __rcu *last_lookup;
    struct hd_struct __rcu *part[];
};
struct hd_struct {
    sector_t start_sect;        /*            */
    sector_t nr_sects;          /*        */
    sector_t alignment_offset;
    unsigned int discard_alignment;
    struct device __dev;    /*        */
    struct kobject *holder_dir;
    int policy, partno;
    struct partition_meta_info *info;
#ifdef CONFIG_FAIL_MAKE_REQUEST
    int make_it_fail;
#endif
    unsigned long stamp;
    atomic_t in_flight[2];
#ifdef    CONFIG_SMP
    struct disk_stats __percpu *dkstats;
#else
    struct disk_stats dkstats;
#endif
    atomic_t ref;
    struct rcu_head rcu_head;
};

유 니 버 설 블록 작업 프로 세 스
이 절 은 doerase 함 수 는 예 를 들 어 Linux 에서 유 니 버 설 블록 층 에 IO 작업 을 제출 했 을 때 유 니 버 설 블록 층 의 처리 절 차 를 토론 합 니 다.우선 do 첨부erase 함수 코드:
static int do_erase(struct super_block *sb, u64 ofs, pgoff_t index,
        size_t nr_pages)
{
    struct logfs_super *super = logfs_super(sb);
    struct bio *bio;
    struct request_queue *q = bdev_get_queue(sb->s_bdev);
    unsigned int max_pages = queue_max_hw_sectors(q) >> (PAGE_SHIFT - 9);
    int i;

    if (max_pages > BIO_MAX_PAGES)
        max_pages = BIO_MAX_PAGES;
    bio = bio_alloc(GFP_NOFS, max_pages);    //  bio  
    BUG_ON(!bio);

    for (i = 0; i < nr_pages; i++) {
        if (i >= max_pages) {        //                   ,        bio  
            /* Block layer cannot split bios :( */
            bio->bi_vcnt = i;
            bio->bi_idx = 0;
            bio->bi_size = i * PAGE_SIZE;
            bio->bi_bdev = super->s_bdev;
            bio->bi_sector = ofs >> 9;
            bio->bi_private = sb;
            bio->bi_end_io = erase_end_io;
            atomic_inc(&super->s_pending_writes);
            submit_bio(WRITE, bio);

            ofs += i * PAGE_SIZE;
            index += i;
            nr_pages -= i;
            i = 0;

            bio = bio_alloc(GFP_NOFS, max_pages);    //    bio  
            BUG_ON(!bio);
        }
        bio->bi_io_vec[i].bv_page = super->s_erase_page;
        bio->bi_io_vec[i].bv_len = PAGE_SIZE;
        bio->bi_io_vec[i].bv_offset = 0;
    }
    bio->bi_vcnt = nr_pages;
    bio->bi_idx = 0;
    bio->bi_size = nr_pages * PAGE_SIZE;
    bio->bi_bdev = super->s_bdev;
    bio->bi_sector = ofs >> 9;
    bio->bi_private = sb;
    bio->bi_end_io = erase_end_io;
    atomic_inc(&super->s_pending_writes);
    submit_bio(WRITE, bio);
    return 0;
}

상기 코드 는 다음 과 같은 절차 도 에서 설명 할 수 있 습 니 다.
그림 1 doerase 흐름 도
도erase 함수 에 서 는 디스크 가 요청 한 데이터 크기 가 bio 작업 이 허용 하 는 최대 값 (i > max pages) 보다 크 면 디스크 데이터 요청 을 여러 bio 로 나 누 어 진행 합 니 다. 먼저 현재 bio 를 보완 하고 제출 한 다음 새로운 bio 구 조 를 신청 하고 나머지 데 이 터 를 새로운 bio 에 채 워 야 합 니 다.
다음은 bio 요청 함수 submit 제출 에 대해 토론 하 겠 습 니 다.bio, 소스 코드 는 다음 과 같 습 니 다:
void submit_bio(int rw, struct bio *bio)
{
    int count = bio_sectors(bio);

    bio->bi_rw |= rw;

    /*
     * If it's a regular read/write or a barrier with data attached,
     * go through the normal accounting stuff before submission.
     */
    if (bio_has_data(bio) && !(rw & REQ_DISCARD)) {
        if (rw & WRITE) {
            count_vm_events(PGPGOUT, count);
        } else {
            task_io_account_read(bio->bi_size);
            count_vm_events(PGPGIN, count);
        }

        if (unlikely(block_dump)) {
            char b[BDEVNAME_SIZE];
            printk(KERN_DEBUG "%s(%d): %s block %Lu on %s (%u sectors)
", current->comm, task_pid_nr(current), (rw & WRITE) ? "WRITE" : "READ", (unsigned long long)bio->bi_sector, bdevname(bio->bi_bdev, b), count); } } generic_make_request(bio); }

submit_bio 는 bio 정 보 를 보완 한 후 generic 를 호출 합 니 다.make_request 함수 bio 제출.generic_make_request 함수 소스 코드 는 다음 과 같 습 니 다.
void generic_make_request(struct bio *bio)
{
    struct bio_list bio_list_on_stack;

    if (current->bio_list) {
        /* make_request is active */
        bio_list_add(current->bio_list, bio);
        return;
    }

    BUG_ON(bio->bi_next);
    bio_list_init(&bio_list_on_stack);
    current->bio_list = &bio_list_on_stack;
    do {
        __generic_make_request(bio);
        bio = bio_list_pop(current->bio_list);
    } while (bio);
    current->bio_list = NULL; /* deactivate */
}

generic_make_request 함수 가 bio 를 current -> bio 에 연결 합 니 다.list 링크 에서 호출generic_make_request 함수 가 링크 에 있 는 모든 bio 를 제출 합 니 다.generic_make_request 함 수 는 최종 적 으로 블록 장치 의 요청 대기 열 에 있 는 Make 를 호출 합 니 다.request_fn 구성원 함 수 는 bio 요청 을 I/O 스케줄 러 에 보 냅 니 다. 이로써 디스크 의 데이터 요청 은 유 니 버 설 블록 층 을 떠 나 다음 층 인 I/O 스케줄 러 층 으로 들 어 갑 니 다.
유 니 버 설 블록 층 총화
종합 적 으로 디스크 데이터 요청 이 유 니 버 설 블록 에서 거 친 절 차 는 다음 과 같 습 니 다.
  • 상부 에서 디스크 데이터 요청
  • 유 니 버 설 블록 층 은 bio 구 조 를 신청 하고 요청 한 데 이 터 를 bio 에 세그먼트 로 기록 합 니 다
  • 요청 한 데이터 가 bio 가 허용 하 는 최대 데이터 보다 많 으 면 요청 을 여러 bio
  • 로 나 눕 니 다.
  • submit 호출bio bio 요청 제출
  • submit_bio 함수 가 층 층 이 호출 되 어 최종 호출 블록 장치 요청 대기 열 에 있 는 Makerequest_fn 구성원 함수 가 bio 를 I/O 스케줄 러 에 제출 하여 처리
  • 좋은 웹페이지 즐겨찾기