Notice
Recent Posts
Recent Comments
Link
«   2025/10   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

JunHyeok

[PintOS - Userprog] Write 본문

PintOS

[PintOS - Userprog] Write

junhyeok-log 2024. 5. 28. 21:53

write 시스템 호출, 어떻게 작동할까?🤔

write 시스템 호출은 파일 디스크립터를 통해 파일이나 표준 출력(화면)에 데이터를 쓰는 함수입니다.

이는 크게 세 단계로 나눌 수 있습니다! ( read 시스템 콜과 유사하네요!)

  • ✅ 출력 버퍼가 유효한지 확인합니다.
  • ✅ 파일 디스크립터가 1이라면 화면에 데이터를 출력합니다.
  • 📂 파일 디스크립터가 1이 아니라면 파일에 데이터를 씁니다.
int write(int fd, const void *buffer, unsigned size) {
    is_valid_addr(buffer);
    int writes = 0;
    lock_acquire(&filesys_lock);
    if (fd == 1) {
        putbuf(buffer, size); // 표준 출력 처리
        lock_release(&filesys_lock);
        return size;
    }

    struct file *file = process_get_file(fd);
    if (file == NULL) {
        lock_release(&filesys_lock);
        return -1;
    }
    writes = file_write(file, buffer, size);
    lock_release(&filesys_lock);
    return writes;
}

PintOS에서 파일 쓰기 과정

1. write 시스템 호출 처리

write 시스템 호출은 파일 디스크립터를 통해 파일에 데이터를 써야 합니다.
이를 위해 먼저 파일 디스크립터 테이블에서 해당 파일을 찾습니다.

int write(int fd, const void *buffer, unsigned size) {
    is_valid_addr(buffer);
    int writes = 0;
    lock_acquire(&filesys_lock);
    if (fd == 1) {
        putbuf(buffer, size); // 표준 출력 처리
        lock_release(&filesys_lock);
        return size;
    }

    struct file *file = process_get_file(fd);
    if (file == NULL) {
        lock_release(&filesys_lock);
        return -1;
    }
    writes = file_write(file, buffer, size);
    lock_release(&filesys_lock);
    return writes;
}

2. 파일 디스크립터 테이블에서 파일 조회

process_get_file(fd) 함수를 통해 파일 디스크립터 테이블에서 파일 구조체를 가져옵니다.

3. 파일 쓰기 수행

파일에 데이터를 쓰기 위해 file_write 함수를 호출해요. 이 함수는 파일의 inode에 실제 데이터를 씁니다.

off_t file_write(struct file *file, const void *buffer, off_t size) {
    off_t bytes_written = inode_write_at(file->inode, buffer, size, file->pos);
    file->pos += bytes_written;
    return bytes_written;
}

4. inode에 데이터 쓰기

inode_write_at 함수는 파일의 inode에 데이터를 쓰며, 필요한 경우 임시 버퍼(bounce buffer)를 사용해 부분적인 데이터를 처리합니다.

off_t inode_write_at(struct inode *inode, const void *buffer_, off_t size, off_t offset) {
    ... 중략 ...
    if (sector_ofs == 0 && chunk_size == DISK_SECTOR_SIZE) {
            /* Write full sector dir
    ... 중략 ...ectly to disk. */
            disk_write (filesys_disk, sector_idx, buffer + bytes_written); 
        } else {
            /* We need a bounce buffer. */
            if (bounce == NULL) {
                bounce = malloc (DISK_SECTOR_SIZE);
                if (bounce == NULL)
                    break;
            }
}

5. 디스크에 데이터 쓰기

disk_write 함수는 디스크에 실제 데이터를 씁니다. 디스크에 데이터를 쓸 때는 섹터 단위로 쓰며, channel 구조체를 사용해 동시 접근을 제어합니다.

void
disk_write (struct disk *d, disk_sector_t sec_no, const void *buffer) {
    struct channel *c;

    ASSERT (d != NULL);
    ASSERT (buffer != NULL);

    c = d->channel;
    lock_acquire (&c->lock);
    select_sector (d, sec_no);
    issue_pio_command (c, CMD_WRITE_SECTOR_RETRY);
    if (!wait_while_busy (d))
        PANIC ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no);
    output_sector (c, buffer);
    sema_down (&c->completion_wait);
    d->write_cnt++;
    lock_release (&c->lock);
}

channel 구조체

/* An ATA channel (aka controller).
   Each channel can control up to two disks. */
struct channel {
    char name[8];               /* Name, e.g. "hd0". */
    uint16_t reg_base;          /* Base I/O port. */
    uint8_t irq;                /* Interrupt in use. */

    struct lock lock;           /* Must acquire to access the controller. */
    bool expecting_interrupt;   /* True if an interrupt is expected, false if
                                   any interrupt would be spurious. */
    struct semaphore completion_wait;   /* Up'd by interrupt handler. */

    struct disk devices[2];     /* The devices on this channel. */
};

직렬 ATA(Serial ATA, SATA)는 하드 디스크 혹은 광학 드라이브와 데이터 전송을 주요 목적으로 만든 컴퓨터 버스의 한 가지이다. 약자를 철자대로 읽어서 사타(영어식으로는 새터, 세이터)라고도 한다. 직렬 ATA는 예전의 ATA 표준을 계승하여, ‘병렬 ATA(PATA, Parallel ATA, 기존의 ATA)’를 대체하기 위해 고안되었다. 직렬 ATA 어댑터와 장치들은 비교적 속도가 빠른 직렬 연결을 이용하여 연결된다. - Wikipedia

struct channel 구조체는 또 뭐야..?🤔

struct channel은 ATA 채널(컨트롤러)을 나타내며, 각 채널은 최대 두 개의 디스크를 제어할 수 있습니다.

  • name: 채널 이름 (예: "hd0").
  • reg_base: 기본 I/O 포트.
  • irq: 사용 중인 인터럽트 번호.
  • lock: 컨트롤러 접근 시 사용되는 락.
  • expecting_interrupt: 인터럽트가 예상되는지 여부 (예상되면 true, 그렇지 않으면 false).
  • completion_wait: 인터럽트 핸들러에 의해 사용되는 세마포어.
  • devices: 이 채널에 연결된 두 개의 디스크.

와.. 진짜 파고 팔 수록 알아야 할게 태산이구나..! 🥲


🐣 PintOS에서 파일을 쓰는 과정. 🐥

  1. 파일 디스크립터 테이블에서 파일 조회: process_get_file 함수를 통해 FDT에서 file struct를 가져옵니다.
  2. 파일 쓰기 수행: file_write 함수를 통해 파일의 inode에 데이터를 씁니다.
  3. inode에 데이터 쓰기: inode_write_at 함수는 디스크에 데이터를 써 버퍼에 저장합니다.
  4. 디스크에 데이터 쓰기: disk_write 함수를 통해 디스크에 섹터 단위로 데이터를 씁니다.
  5. 쓴 데이터의 양 반환: bytes_written을 반환.

PintOS와 Linux 비교 🐣 🆚 🐧

  1. 파일 디스크립터 조회:
    PintOS: process_get_file 함수를 통해 FDT에서 파일 구조체를 가져옵니다.
    Linux: fget 함수를 통해 FDT에서 파일 구조체를 가져옵니다.

  2. 파일 쓰기 수행:
    PintOS: file_write 함수를 통해 inode에 데이터를 씁니다.
    Linux: vfs_write 함수를 통해 VFS 계층에서 파일 시스템의 write 함수를 호출합니다.

  3. inode에 데이터 쓰기:
    PintOS: inode_write_at 함수를 사용합니다.
    Linux: generic_file_write_iter 함수를 사용해 페이지 캐시를 통해 데이터를 관리합니다.

  4. 디스크에 데이터 쓰기:
    PintOS: disk_write 함수를 사용합니다.
    Linux: submit_bio 함수를 통해 블록 장치 드라이버에 I/O 요청을 전달합니다.

  5. 락 사용:
    PintOS: 단일 락(filesys_lock)을 사용합니다.
    Linux: 다양한 락(i-node 락, 페이지 락 등)을 사용해 동시성 문제를 해결합니다.

PintoOS 과제를 구현했다고 끝이 아니다... 진짜 이건 시작에 불과한 것 같다.. 😢

'PintOS' 카테고리의 다른 글

[PintOS - Userprog] Wait  (0) 2024.05.29
[PintOS - Userprog] Fork, Exec, Wait 에 들어가기 앞서...  (0) 2024.05.29
[PintOS - Userprog] Seek & Tell  (0) 2024.05.28
[PintOS - Userprog] Create & Remove  (0) 2024.05.28
[PintOS - Userprog] Read  (0) 2024.05.28