. [PintOS, Project 4] inode.c 공부
본문 바로가기
Pintos Project/Project 4

[PintOS, Project 4] inode.c 공부

by 불냥이_ 2021. 3. 4.

※ 이 글은 Project 4를 본격적으로 시작하기 전, 추론의 영역입니다.

 

좆됐다

 

기존시스템은 bitmap으로 디스크를 관리하고 있다.

그리고 inode.c는 bitmap 기반으로 짜여져있다. 

 

그런데 이제 FAT를 도입했으니 bitmap을 다 지워고 FAT 기반으로 다 깔아야된다.

그 말은 filesys나 file에 관련된 것들을 전부 갈아엎어야한다는 것이다.

 

심지어 bitmap은 연속할당이었으나, FAT은 연속할당도 아니다.

 

마치 1x1 레고 블록을 드렸으니 이걸로 도시를 만들어보세요라는 느낌이다.

 

시발

 

 

일단 inode.c부터 공부해보자.

 

 

/* On-disk inode.
 * Must be exactly DISK_SECTOR_SIZE bytes long. */
struct inode_disk
{
    disk_sector_t start;  /* First data sector. */
    off_t length;         /* File size in bytes. */
    unsigned magic;       /* Magic number. */
    uint32_t unused[125]; /* Not used. */
};

disk에 저장되어있는 inode 정보이다. 참고로 inode들은 disk의 한 섹터안에 저장되어있다.

그리고 이게 sector size가 되어야한다는데 이 단일 inode_disk 크기가 512byte라는 것인지, 아니면 inode_disk들이 512byte가 되어야된다는 것인지.

 

 그런데 unused가 있는데, 내 생각에는 이 배열에 이 파일이 소유하고있는 cluster 번호가 와야되는 것 같다. 그러면 대충 512byte먹지 않을까.

 

 그러면 file을 열 때, inode는 어떻게 찾을 수 있을까. 물론, userprog에서는 file에 inode가 저장되있을 거다. 문제는 이 inode는 포인터라서 (memory 상의) inode 를 가리키고 있을 것인데, 우리가 필요한 것은 inode의 sector 번호이다. 대충 찾아보니 이는 directory.c 의 dir_entry에 나타나있다.

 

 

/* A single directory entry. */
struct dir_entry
{
    disk_sector_t inode_sector; /* Sector number of header. */
    char name[NAME_MAX + 1];    /* Null terminated file name. */
    bool in_use;                /* In use or free? */
};

 이런식으로.  이런게 있다라고 생각하고 계속 inode를 가보자.

 

 

/* Returns the number of sectors to allocate for an inode SIZE
 * bytes long. */
static inline size_t
bytes_to_sectors(off_t size)
{
    return DIV_ROUND_UP(size, DISK_SECTOR_SIZE);
}

 byte를 sector 단위로 바꿔주는 것이다. 512byte단위로 올린다고 생각하면 될 것이다.

만약 781byte는 2 sector로. 

 

 

/* In-memory inode. */
struct inode
{
    struct list_elem elem;  /* Element in inode list. */
    disk_sector_t sector;   /* Sector number of disk location. */
    int open_cnt;           /* Number of openers. */
    bool removed;           /* True if deleted, false otherwise. */
    int deny_write_cnt;     /* 0: writes ok, >0: deny writes. */
    struct inode_disk data; /* Inode content. */
};

memory 상의 inode 구조체이다. elem이 있으니 어딘가에 inode를 관리하는 list가 있을 것이다. file 구조체가 관리하고 있을 줄 알았는데 아니었다. 흠.... 어딘가에 있겠지.

sector는 inode_disk가 있는 곳을 말하는 것일거고.

open_cnt는 inode가 현재 몇번 참조되고 있는 것인지를 말하는 것 아닐까?

removed는 inode가 지워졌는지 아닐까

deny_write는 이전에 구현한 deny_write라고 생각한다.

그리고 inode_disk는 inode_disk를 disk에서 읽어와서 여기다가 올려놓는 것일 것이다.

 

난 천재? 

 

구조체는 다 봤으니 함수는 간단히 어떤 역할만 하는지 보도록하자. 갈길이 멀다.

 

/* Returns the disk sector that contains byte offset POS within
 * INODE.
 * Returns -1 if INODE does not contain data for a byte at offset
 * POS. */
static disk_sector_t
byte_to_sector(const struct inode *inode, off_t pos)
{
    ASSERT(inode != NULL);
    if (pos < inode->data.length)
        return inode->data.start + pos / DISK_SECTOR_SIZE;
    else
        return -1;
}

 byte를 pos로 받는다. 그래서 그에 해당하는 sector가 어디인지를 검색한다. 

즉, 어떤 파일이 3,4,5,6 sector를 차지한다고 하자. 그리고 이 파일의 765byte는 어느 sector에 있을까?

 

이 file의 start는 3이고 765byte는 2번째 섹터에 있을 것이므로 3+1 = 4가 되서, 4를 반환한다. 

 

 

문제는 FAT은 연속할당이 아니라서 아마 inode_disk의 배열에서 찾아야될 것 같다. 

 

 

/* List of open inodes, so that opening a single inode twice
 * returns the same `struct inode'. */
static struct list open_inodes;

 아하 여깄었구나! 

열려진 inode들을 관리하는 list이다. inode의 elem을 여기다 넣으면 될 것 같다.

 

 

/* Initializes an inode with LENGTH bytes of data and
 * writes the new inode to sector SECTOR on the file system
 * disk.
 * Returns true if successful.
 * Returns false if memory or disk allocation fails. */
bool inode_create(disk_sector_t sector, off_t length)
{
    struct inode_disk *disk_inode = NULL;
    bool success = false;

    ASSERT(length >= 0);

    /* If this assertion fails, the inode structure is not exactly
	 * one sector in size, and you should fix that. */
    ASSERT(sizeof *disk_inode == DISK_SECTOR_SIZE);

    disk_inode = calloc(1, sizeof *disk_inode);
    if (disk_inode != NULL)
    {
        size_t sectors = bytes_to_sectors(length);
        disk_inode->length = length;
        disk_inode->magic = INODE_MAGIC;
        if (free_map_allocate(sectors, &disk_inode->start))
        {
            disk_write(filesys_disk, sector, disk_inode);
            if (sectors > 0)
            {
                static char zeros[DISK_SECTOR_SIZE];
                size_t i;

                for (i = 0; i < sectors; i++)
                    disk_write(filesys_disk, disk_inode->start + i, zeros);
            }
            success = true;
        }
        free(disk_inode);
    }
    return success;
}

 file의 길이를 받으면, 그거를 sector수로 반환하고 (bytes_to_sectors() 함수가 있다!) 비어있는 sector에 그것을 쓴다.

문제는 이 역시 연속 할당이 아니기 때문에 비어있는 sector를 가져와서 넣는 식으로 해야될 것이다. 

 

그것을 구현할 생각하니깐 어지럽다. 

 

 

 

 

/* Reads an inode from SECTOR
 * and returns a `struct inode' that contains it.
 * Returns a null pointer if memory allocation fails. */
struct inode *
inode_open(disk_sector_t sector)
{
    struct list_elem *e;
    struct inode *inode;

    /* Check whether this inode is already open. */
    for (e = list_begin(&open_inodes); e != list_end(&open_inodes);
         e = list_next(e))
    {
        inode = list_entry(e, struct inode, elem);
        if (inode->sector == sector)
        {
            inode_reopen(inode);
            return inode;
        }
    }

    /* Allocate memory. */
    inode = malloc(sizeof *inode);
    if (inode == NULL)
        return NULL;

    /* Initialize. */
    list_push_front(&open_inodes, &inode->elem);
    inode->sector = sector;
    inode->open_cnt = 1;
    inode->deny_write_cnt = 0;
    inode->removed = false;
    disk_read(filesys_disk, inode->sector, &inode->data);
    return inode;
}

inode_disk가 있는 sector에서 데이터를 읽어와서 inode에 저장하는 함수이다.

 

 

 

/* Closes INODE and writes it to disk.
 * If this was the last reference to INODE, frees its memory.
 * If INODE was also a removed inode, frees its blocks. */
void inode_close(struct inode *inode)
{
    /* Ignore null pointer. */
    if (inode == NULL)
        return;

    /* Release resources if this was the last opener. */
    if (--inode->open_cnt == 0)
    {
        /* Remove from inode list and release lock. */
        list_remove(&inode->elem);

        /* Deallocate blocks if removed. */
        if (inode->removed)
        {
            free_map_release(inode->sector, 1);
            free_map_release(inode->data.start,
                             bytes_to_sectors(inode->data.length));
        }

        free(inode);
    }
}

반대로 inode를 닫고, 갱신사항을 inode_disk에 적는다. 

 

 

 

/* Marks INODE to be deleted when it is closed by the last caller who
 * has it open. */
void inode_remove(struct inode *inode)
{
    ASSERT(inode != NULL);
    inode->removed = true;
}

 inode가 삭제된다면 이를 체크하고, 이를 이용해서 close할 때, free까지 해준다.

 

 

 

 

/* Reads SIZE bytes from INODE into BUFFER, starting at position OFFSET.
 * Returns the number of bytes actually read, which may be less
 * than SIZE if an error occurs or end of file is reached. */
off_t inode_read_at(struct inode *inode, void *buffer_, off_t size, off_t offset)
{
    uint8_t *buffer = buffer_;
    off_t bytes_read = 0;
    uint8_t *bounce = NULL;

    while (size > 0)
    {
        /* Disk sector to read, starting byte offset within sector. */
        disk_sector_t sector_idx = byte_to_sector(inode, offset);
        int sector_ofs = offset % DISK_SECTOR_SIZE;

        /* Bytes left in inode, bytes left in sector, lesser of the two. */
        off_t inode_left = inode_length(inode) - offset;
        int sector_left = DISK_SECTOR_SIZE - sector_ofs;
        int min_left = inode_left < sector_left ? inode_left : sector_left;

        /* Number of bytes to actually copy out of this sector. */
        int chunk_size = size < min_left ? size : min_left;
        if (chunk_size <= 0)
            break;

        if (sector_ofs == 0 && chunk_size == DISK_SECTOR_SIZE)
        {
            /* Read full sector directly into caller's buffer. */
            disk_read(filesys_disk, sector_idx, buffer + bytes_read);
        }
        else
        {
            /* Read sector into bounce buffer, then partially copy
			 * into caller's buffer. */
            if (bounce == NULL)
            {
                bounce = malloc(DISK_SECTOR_SIZE);
                if (bounce == NULL)
                    break;
            }
            disk_read(filesys_disk, sector_idx, bounce);
            memcpy(buffer + bytes_read, bounce + sector_ofs, chunk_size);
        }

        /* Advance. */
        size -= chunk_size;
        offset += chunk_size;
        bytes_read += chunk_size;
    }
    free(bounce);

    return bytes_read;
}

byte_to_sector는, inode와 offset을 받았을 때, 해당 offset에 해당하는 sector를 반환한다.

그 sector에서부터 size만큼 disk에 쓴다. 역시나 문제는 연속할당이 아니라서 sector_idx를 고쳐야된다는 것이다.

 

inode_length()는 밑에서 설명하겠지만, inode가 가리키는 file의 총 크기를 의미한다.

 

요약하면 disk에 있는 file을 inode로 찾아서, 해당 offset에서 size만큼 읽어온다는 뜻이다. 

 

 

/* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET.
 * Returns the number of bytes actually written, which may be
 * less than SIZE if end of file is reached or an error occurs.
 * (Normally a write at end of file would extend the inode, but
 * growth is not yet implemented.) */
off_t inode_write_at(struct inode *inode, const void *buffer_, off_t size,
                     off_t offset)
{
    const uint8_t *buffer = buffer_;
    off_t bytes_written = 0;
    uint8_t *bounce = NULL;

    if (inode->deny_write_cnt){
        return 0;
        }

    while (size > 0)
    {
        /* Sector to write, starting byte offset within sector. */
        disk_sector_t sector_idx = byte_to_sector(inode, offset);
        int sector_ofs = offset % DISK_SECTOR_SIZE;

        /* Bytes left in inode, bytes left in sector, lesser of the two. */
        off_t inode_left = inode_length(inode) - offset;
        int sector_left = DISK_SECTOR_SIZE - sector_ofs;
        int min_left = inode_left < sector_left ? inode_left : sector_left;

        /* Number of bytes to actually write into this sector. */
        int chunk_size = size < min_left ? size : min_left;
        if (chunk_size <= 0)
            break;

        if (sector_ofs == 0 && chunk_size == DISK_SECTOR_SIZE)
        {
            /* Write full sector directly 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;
            }

            /* If the sector contains data before or after the chunk
			   we're writing, then we need to read in the sector
			   first.  Otherwise we start with a sector of all zeros. */
            if (sector_ofs > 0 || chunk_size < sector_left)
                disk_read(filesys_disk, sector_idx, bounce);
            else
                memset(bounce, 0, DISK_SECTOR_SIZE);
            memcpy(bounce + sector_ofs, buffer + bytes_written, chunk_size);
            disk_write(filesys_disk, sector_idx, bounce);
        }

        /* Advance. */
        size -= chunk_size;
        offset += chunk_size;
        bytes_written += chunk_size;
    }
    free(bounce);

    return bytes_written;

}

inode_write_at도 read_at과 기본은 똑같은 함수일 것이다.

하지만, file 뒷부분이나 중간에 data가 새로이 들어왔으면 어떡할 것인가?

 

이를 구현하려니 어지럽다. 

 

아마 while문 돌기 전에 필요한 sector의 갯수를 알고, 필요하다면 획득해야될 것 같다. 

 

 

 

/* Disables writes to INODE.
   May be called at most once per inode opener. */
void inode_deny_write(struct inode *inode)
{
    inode->deny_write_cnt++;
    ASSERT(inode->deny_write_cnt <= inode->open_cnt);
}

/* Re-enables writes to INODE.
 * Must be called once by each inode opener who has called
 * inode_deny_write() on the inode, before closing the inode. */
void inode_allow_write(struct inode *inode)
{
    ASSERT(inode->deny_write_cnt > 0);
    ASSERT(inode->deny_write_cnt <= inode->open_cnt);
    inode->deny_write_cnt--;
}

inode에 동기화를 설정해주는 함수다. 생략하겠다.

 

 

/* Returns the length, in bytes, of INODE's data. */
off_t inode_length(const struct inode *inode)
{
    return inode->data.length;
}

inode가 가리키는 file의 크기를 반환하는 함수이다.

 

 

 

이제 inode에 대해서 공부가 끝났으니 내일은 구현을 시작해보도록 하겠다. 

 

 

 

 

 

댓글