Operating System note 2

짱J·2022년 12월 3일
0

오퍼레이팅시스템

목록 보기
2/2

♻️ 코드 변경사항 반영 및 재부팅

make bzImage
cp arch/x86/root/bzImage /boot/bzImage3
reboot

💊 출력 로그 레벨 변경

echo 8 > /proc/sys/kernel/printk

🐰 시스템 콜 번호 할당

arch/x86/kernel/syscall_table_32.S


🧸 여러가지 옵션

xxd -g1 myfd > x # 각 바이트가 분리되어 보이도록 함
ls -a # 숨겨진 파일(., ..)도 모두 출력됨
ls -ai # inode number 함께 출력

🔢 16진수 관련 사이트


🍀 Week 9, 10 - fs

🍁 디스크 만들기

dd bs=1024 count=1440 if=/dev/zero of=myfd
  • bs - 1블록 단위의 바이트 크기
  • count - 블록의 개수
  • if - 데이터를 읽을 입력 파일
  • of - 데이터를 작성할 출력 파일

0으로 채워진 1024*1440 바이트 크기의 데이터를 myfd에 write하겠다

🍁 디스크 포맷 변경

mkfs -t ext2 myfd

🍁 mount & unmount

mkdir temp
mount -o loop myfd temp # myfd와 temp를 연결
umount temp # 연결을 끊음

🍁 file system 구성

  • super block
  • group descriptor
  • DBM
  • IBM

리틀 인디언 - 뒤에서부터 읽어야 함 !!!!!

block location = 0x2800 + 0x80 * (c-1)

inode content = (inode table 3번째 줄) * 0x400

super block - 400 시작

  • m_inodes_count - 디스크 내 inode의 개수
    • 00 00 00 b8(=184), 184개
  • m_blocks_count - 디스크 내 block의 개수
    • 00 00 05 a0(=1440)
  • m_magic - 파일 시스템 형식 (ext2, ext3, ext4)
    • ef 53, ext2를 의미
  • m_log_block_size - 블록 사이즈
    • 00 00 00 00(=0)
    • 1024*2^(m_log_block_size) = 1024바이트
  • m_first_data_block - 첫번째 데이터 블록 number, super block
    • 00 00 00 01

group descriptor - 800 시작

  • m_block_bitmap - DBM 블록의 위치
    • 00 00 00 08
    • 8 * 400 = 2000에 위치
  • m_inode_bitmap - IBM 블록의 위치
    • 00 00 00 09
    • 09 * 400 = 2400에 위치
  • m_inode_table - inode table의 위치
    • 00 00 00 0a = 2800에 위치

DBM - 2000 시작

  • 7f ff ff ff ff ff
    = 01111111 / 11111111 / 11111111 / 11111111 / 11111111 / 11111111
  • 블록의 사용 여부를 저장 (1 = 사용되고 있음)
  • block 1~47: 사용됨
  • block 48~1440: 사용 안됨

IBM - 2400 시작

  • 0f ff
    = 00001111 / 11111111
  • block 1~12: 사용됨
  • block 13~184: 사용 안됨

Inode Table - 2800 시작

  • i_atime - access time
    • 34 62 55 63
  • i_ctime - creation time
    • 34 62 55 63
  • i_dtime - modification time
    • 34 62 55 63

🍁 f2 생성 후 myfd의 변화

DBM

  • 7f > ff
  • f2가 48번 block에 할당됨

IBM

  • f2를 위한 inode number가 블록 13번에 할당됨

🍁 inode table 분석 - 2800 시작

  • 10번째 block에서 시작
  • root directory file: 2880 ~ 28ff
    • block location - 00 00 00 21
  • byte size - 00 00 04 00 = 1024
  • block size - 00 00 00 02
    • 512 * 2 = 1024
  • owner - 00 00 (= root 사용자)

🍁 Root directory file 분석 - 8400 시작

  • 4byte - inode number / 2byte - record length / 1byte - name length / 1byte - file type / 나머지 - file name

  • 여기서도 리틀 인디언으로 읽기 !!

  • block location 구하는 방법: 0x2800 + 0x80 * (c-1)

    • c는 inode 번호
    • c-1을 16진수로 변환한 뒤 16진수 곱셈을 해주어야 함
    • ...dms inode가 2인 root directory file로 위에서 한 것과 동일
  • . - 02 00 00 00 / 0c 00 / 01 / 02 / 2e 00 00 00

    • inode number = 2
    • record length = 12
    • name length = 1
    • file type = 2
    • file name = .
  • .. - 02 00 00 00 / 0c 00 / 02 / 02 / 2e 2e 00 00

    • inode number = 2
    • record length = 12
    • name length = 2
    • file type = 2
    • file name = ..
  • lost+found - 0b 00 00 00 / 14 00 / 0a / 02 / 6c 6f 73 74 2b 66 6f 75 6e 64 00 00

    • inode number = 11
    • record length = 20
    • name length = 10
    • file type = 2
    • file name = lost+found
    • 0x2800 + 0x80 * a = 0x2d00
  • f1 - 0c 00 00 00 00 / 0c 00 / 02 / 01 / 66 31 00 00

    • inode number = 12
    • record length = 12
    • name length = 2
    • file type = 1
    • file name = f1
    • block location = 0x2800 + 0x80 * b = 0x2d80
  • f2 - 0d 00 00 00 / c8 03 / 02 / 01 / 66 32 00 00

    • inode number = 13
    • record length = 968(=0x3c8)
    • name length = 2
    • file type = 1
    • file name = f2
    • block location = 0x2800 + 0x80 * c = 0x2e00

🍁 f3 생성 후 myfd의 변화

기존에는 f1, f2만 있던 상황

DBM

  • 49번째 block이 추가됨

IBM

  • 14번째 inode가 추가로 사용됨

block location

  • 0x2800 + 13 * 0x80 = 0x2e80

inode table

  • 0x00000031 * 0x400 = 0xc400
  • 0xc400으로 가면 f3 파일의 내용은 'hello'가 기록된 것을 확인할 수 있음

root directory file

  • 0x8440 줄의 0e ~~~ 부분이 추가된 것을 알 수 있음

🍁 f3 삭제 후 myfd의 변화

DBM & IBM

inode table

  • m_ctime이 5c 4b 60 63으로 변경됨 (최종 변경 시간)
  • m_dtime이 5c 4b 60 63으로 변경됨 (삭제한 시간)
  • m_links_count - 00으로 변경됨 (해당 inode를 참조하는 링크 수)
  • block location - 31 00 00 00을 그대로 유지

root directory file

  • inode number - 14에서 0으로 바뀜
  • 모든 것이 이전으로 돌아가지 않고, 몇몇은 데이터를 그대로 남겨둠

🍁 d7(디렉토리) 생성 후 myfd의 변화

root directory file

  • inode number - 14

inode content

  • 0x2800 + 13 * 0x80 = 0x2e80
  • 0x00000031 * 0x400 = 0xc400로 가면 내용을 볼 수 있음

🍁 d7 안에 파일이 생성됐을 때 myfd의 변화

root directory file

  • 위와 동일

inode table

  • m_ctime(변경 시간)과 m_mtime(수정 시간) 변경됨
  • block location은 그대로

🍁 myhd 분석

  • week10 15번 참고

🍁 myfd의 정보를 출력하는 함수

  • super block, group descriptor, IBM, DBM, inode table, file names in the root directory, inode numbers, block locations 다 출력
  • 코드가 너무 길어서 블로그에 못 적음
  • 시험 나오면 week10 16번 보기

🍀 Week 11, 12 - onmem

🍁 mount_root()

: super block과 root inode 등의 파일 시스템을 캐싱

start_kernel > rest_init > kernel_init > prepare_namespace > mount_root 순으로 호출

  • start_kernel, rest_init, kernel_init - init/main.c
  • prepare_namespace, mount_root - init/do_mounts.c

🍁 super_block, inode, buffer_head, dentry의 data type 확인

  • super_block, inode - include/linux/fs.h
  • buffer_head - include/linux/buffer_head.h
  • dentry - include/linux/dcache.h

🍁 display_super_blocks() - 모든 superblock 출력

  • init/do_mounts.c에 함수 만들기
void display_superblocks() {
	struct super_block *sb;
    list_for_each_entry(sb, &super_blocks, s_list) {
    	printk("dev name: %s, dev maj num: %d, dev minor num: %d, root ino: %d\n",
        sb->s_id, MAJOR(sb->s_dev), MINOR(sb->s_dev), sb->s_root->d_inode->i_ino);
    }
}

// mount_root() 전 후에 함수 호출
void __init prepare_namespace() {
	...
	
    printk("before mount_root():\n");
    display_superblocks();
    mount_root();
    printk("after mount_root():\n");
    display_superblocks();
	...
}
  • device number - 각 디바이스의 고유한 넘보
    • major - device 번호
    • minor - device의 종류 안에서 구별 번호

🍁 display_all_inodes() - 캐시된 모든 inode 출력

  • init/do_mounts.c에 함수 만들기
void display_all_inodes() {
	struct inode *in;
    list_for_each_entry(in, &inode_in_use, i_list) {
    	printk("dev maj num: %d, dev minor num: %d, inode num: %d, sb dev: %s\n",
        MAJOR(in->i_rdev), MINOR(in->i_rdev), in->i_ino, in->i_sb->s_id);
    }
}

// mount_root() 전 후에 함수 호출
void __init prepare_namespace() {
	...
	
    printk("before mount_root():\n");
    display_all_inodes();
    mount_root();
    printk("after mount_root():\n");
    display_all_inodes();
	...
}

🍁 display_all_inodes() - file name & byte size 출력하도록

void display_all_inodes() {
	struct inode *in;
    list_for_each_entry(in, &inode_in_use, i_list) {
    	printk("dev maj num: %d, dev minor num: %d, inode num: %d, sb dev: %s\n",
        MAJOR(in->i_rdev), MINOR(in->i_rdev), in->i_ino, in->i_sb->s_id);
        
     // 코드 추가한 부분 !!! 
     struct dentry *den;
     list_for_each_entry(den, &in->i_dentry, d_alias) {
     	printk("file name: %s, ", den->d_name.name);
     }
     printk("file byte size: %x\n", in->i_size);
    }
}

🍁 my_sys_display_inodes() - 사용중인 모든 inode의 file name & byte size, mounting point 출력

  • 시스템 콜 번호 44번 할당
  • 나는 fs/read_write.c에 구현했는데 모범 답안은 init/do_mounts.c에 구현함

  • 예제 파일
#include <stdio.h>

int main() {
	syscall(44);
    
    return 0;
}
  • 함수 구현
extern struct list_head inode_in_use;
asmlinakge void my_sys_display_inodes() {
	struct inode *in;
    int count = 0;
    list_for_each_entry(in, &inode_in_use, i_list) {
    	if(count==100) break;
        struct dentry *den;
        
        list_for_each_entry(den, &in->i_dentry, d_alias) {
        	printk("file name: %s, ", den->d_name.name);
            printk("mounting point: %d, ", den->d_mounted);
            count++;
        }
        printk("file byte size: %x\n", in->i_size);
    }
}
  • 대부분의 파일의 mounting point가 0이지만 temp에는 myfd를 mount했기 때문에 mounting point가 1로 증가

🍁 /sbin/init

  • 11주차 7번 참고

🍁 f_pos가 변경되면서 10byte씩 읽는 문제

🍁 fork() → f_pos를 공유해서 자식이 먼저 10 바이트 읽고 부모가 그 다음 10 바이트 읽음

🍁 chroot()랑 chdir()로 다양한 경로의 f1 읽기

🍁 my_sys_show_fpos() → fd = 3 또는 4일 때 f_post 출력

🍁 my_sys_show_fpos()가 f_op->read, f_op->write도 출력하도록

🍁 f2가 f1 뒤집은 data로 덮어쓰기됨


🍀 Week 13, 14 - mem

🍁 process image 확인

g++ -o ex1 ex1.cpp
./ex1 & # ex1의 pid가 나옴
cat /proc/(ex1의 pid)/maps > x1
vi x1

메모리 맵 그리기

#include <stdio.h>
int x; // 전역 변수 - Data 영역
int y[100000]; // 전역변수지만 커서 Heap 영역

int main() { // Code 영역
	int k; // 지역 변수 - Stack 영역
    int *pk; // 동적할당 메모리 - Heap 영역
    pk = new int;
    printf("ex1. &main: %p, &x: %p, &y: %p, &y[9999]: %p, 
    &k: %p, &pk: %p, pk: %p\n", main, &x, &y, &y[9999], &pk, pk);
    
    for(;;); // memory map을 보기 위해
    
    return 0;
}

SegmentAddressIdentifier
Code08048000 - 08049000&main
Read-only Data08049000 - 0804a000-
Data0804a000 - 0804b000&x, &y
Heap0804b000 - 08075000&y[9999], pk
Stackbfa77000 - bfa8c000&k, &pk
  • pk는 Heap 영역
  • &pk는 Stack 영역

ex1과 ex2의 메모리 주소가 같은 이유

  • 프로세스 상의 주소는 logical 주소이기 때문

  • 각 프로세스 구조의 같은 부분에 메모리를 할당

  • physical 메모리 주소는 서로 다를 것

🍁 메모리 맵 그리기 - 이차원 배열

#include <stdio.h>

int A[5][1024];

int main() { // Code 영역
	int i, j; // 지역 변수 - Stack 영역
    for(i=0; i<5; i++) {
    	for(j=0; j<5; j++) {
        	A[i][j] = 3;
        }
        printf("&main: %p, &A: %p, &A[%d]: %p, &i: %p, &j: %p"
        , main, &A, i, &A[i], &i, &j);
    }
    
    for(;;);
    
    return 0;
}

SegmentPageIdentifier
Code8048 - 8049&main
Data804a - 804b&A[0]
Heap804b - 8050&A[1], &A[2], &A[3], &A[4]
Stackbfa7f - bfa94&i, &j
  • Address에서 앞에 0 지우고, 뒤에 000 지우면 Page 번호
  • 프로그램 접근 순서: Code > Data > Heap > Stack

1) Code 영역에서 페이지를 불러옴 - 1 page
2) Data, Heap 영역에서 804a ~ 8050 불러옴 - 6 page
3) Stack 영역에서 bfa92 불러옴 - 1 page

  • 총 8번의 Page Fault가 발생
  • C library Code, C Library Data, Library Loader도 고려하면 더 많은 page fault가 발생할 것

🍁 my_sys_show_pfcnt - page fault가 몇 번 일어나는지 count

  • 시스템콜 56번 할당 - my_sys_show_pfcnt

  • mm/mmap.c에 함수 구현

extern int pfcnt;
asmlinkage void my_sys_show_pfcnt() {
	printk("page fault count so far: %d\n", pfcnt);
}
  • arch/x85/mm/fault.cdo_page_fault() - page fault가 발생하면 pfcnt가 1 증가하도록 코드 추가
void __kprobes do_page_faults(struct pt_regs *regs, unsinged long error_code) {
	pfcnt += 1; // 맨 위에 코드 한 줄 추가
    
    ...
}
  • 예제 파일
#include <stdio.h>
#include <unistd.h>

int A[5][1024];

int main() { // Code 영역
	int i, j; // 지역 변수 - Stack 영역
    syscall(56);
    for(i=0; i<5; i++) {
    	for(j=0; j<5; j++) {
        	A[i][j] = 3;
        }
        // printf("&main: %p, &A: %p, &A[%d]: %p, &i: %p, &j: %p"
        // , main, &A, i, &A[i], &i, &j);
        // printf문이 page fault에 영향을 주는 것 같아 주석 처리 후 실행
    }
    syscall(56);
    for(;;);
    
    return 0;
}

  • 4번의 page fault 발생
  • A[0]인 804a - 이미 paging 됨
  • A[1], A[2], A[3], A[4]를 page하기 위해 804b, 804c, 804d, 804e 총 4번의 page fault가 발생

🍁 page fault 주소 출력

  • arch/x85/mm/fault.cdo_page_fault() - 프로그램 이름이 ex3면 page fault 주소를 출력하도록 커널 코드를 변경
void __kprobes do_page_faults(struct pt_regs *regs, unsinged long error_code) {
    ...
    address = read_cr2();
    if(strcmp(tsk->comm, "ex3")==0) {
    	printk("pg fault for ex3 at: %p\n", address);
    }
    ...
}

예제 파일 1

#include <stdio.h>
int x;
 
 int main() {
 	x = 3;
    return 0;
}
  • Data Page(804a)를 먼저 읽고
  • 라이브러리가 저장된 페이지를 읽고
  • Code 영역(8048)을 메모리에 올린다.
  • main 함수가 초기화되어 호출되기까지의 메모리 접근이 포함되어 page fault가 많이 생성되었다.

  • x - 804a014
  • main - 8048034
  • read only data - 8049f20, 8049f6c
  • library code, data - b7~~

예제 파일 2

#include <stdio.h>
#include <unistd.h>

int A[5][1024];

int main() { // Code 영역
	int i, j; // 지역 변수 - Stack 영역
    syscall(56);
    for(i=0; i<5; i++) {
    	for(j=0; j<5; j++) {
        	A[i][j] = 3;
        }
        printf("&main: %p, &A: %p, &A[%d]: %p, &i: %p, &j: %p"
        , main, &A, i, &A[i], &i, &j);
    }
    syscall(56);
    for(;;);
    
    return 0;
}
  • page fault가 22로 증가
    • printf로 추가적인 page fault가 일어났기 때문

🍁 my_sys_get_vma_list()

  • 13주차 5번 참고

🍁 Running time 비교

  • 13주차 6번 참고

🍁 my_sys_get_phyloc() - main()의 물리 주소 출력

  • 시스템 콜 98번 할당 - my_sys_get_phyloc
  • mm/mmap.c에 시스템 콜 함수 구현

  • 예제 파일 ex1.c
#include <stdio.h>
#include <unistd.h>

int main() {
	printf(&main: %p\n", main);
    syscall(98);
    
    return 0;
}
  • mm/mmap.c
asmlinkage void sys_get_phyloc(unsigned int addr) {

	// step 0. PGDIR_SHIFT, PTRS_PER_PGD, PAGE_SHIFT, PTRS_PER_PTE 확인
	printk("PGDIR_SHIFT: %d, PTRS_PER_PGD: %d, PAGE_SHIFT: %d, PTRS_PER_PTE: %d\n",
    PGDIR_SHIFT, PTRS_PER_PGD, PAGE_SHIFT, PTRS_PER_PTE);
    
    // step 1. directory number, page number, offset 추출
    unsigned int dir = addr >> PGDIR_SHIFT;
    unsigned int pg = (addr >> PAGE_SHIFT) & 0x3ff;
    unsigned int off = addr & 0xfff;
    printk("directory number: %d, page number: %d, offset from address: %d\n",
    dir, pg, off);
    
    // step 2. 현재 프로세스의 디렉토리 테이블
    unsigned int *x = current->mm->pgd;
    printk("location of directory table of the current process: %p\n", x);
    
    // step 3. 디렉토리 테이블의 위치
    unsigned int *y = &x[dir];
    printk("location of directory table entry: %p\n", y);
    
    // step 4. 디렉토리의 물리적 주소
    unsigned int pdir = *y & 0xfffff000;
    printk("physical location of the directory: %x\n", pdir);
    
    // step 5. 가상 주소에 접근 (앞에 c 붙이기)
    unsigned int *vdir = pdir + 0xc0000000;
    printk("virtual location of the directory: %p\n", vdir);
    
    // step 6. k를 이용하여 물리 주소에 접근하자
    // 물리 주소에 직접 접근 불가 > 가상 주소를 이용해야 함
    unsigned int *k = &vdir[pg];
    printk("location of the frame entry: %p\n", k);
    
    // step 7. frame의 물리 주소
    unsigned int pfr = *k & 0xfffff000;
    printk("physical location of frame: %x\n", pfr);
    
    // step 8. main 함수의 물리 주소
    unsigned int pmain = pfr + off;
    printk("physical address of main: %x\n", pmain);
    
    // step 9. 가상 주소에 접근 (앞에 c 붙이기)
    unsigned int *vmain = pmain + 0xc0000000
    printk("virtual address of main: %x\n", vmain);
    
    printk("first 4 bytes: %x\n", vmain[0]);
}

  • 첫 4바이트를 리틀 인디언으로 읽으면 8d 4c 24 04

  • objdump -d ./ex~~의 결과
  • 첫 4바이트가 8d 4c 24 04

올바르게 main 함수의 물리 주소를 찾았다 !!!

profile
[~2023.04] 블로그 이전했습니다 ㅎㅎ https://leeeeeyeon-dev.tistory.com/

0개의 댓글