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 스케줄 러 층 으로 들 어 갑 니 다.
유 니 버 설 블록 층 총화
종합 적 으로 디스크 데이터 요청 이 유 니 버 설 블록 에서 거 친 절 차 는 다음 과 같 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
u - boot 2 단계 빗질 시작\ #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t * gd asm ("r8") 설명: register 를 통 해 레지스터 변 수 를 표시 하고, asm ("r8")...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.