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

[PintOS, Project 4] inode.c 구현

by 불냥이_ 2021. 3. 4.

※ 이 글은 Project 4를 구현중인 불확실과 추론의 영역입니다.

    특히 이부분은 다른 곳을 구현하면서 수정할 여지가 높은 곳입니다.

    착한 어린이들은 따라하지 마세요. 

 

 

 

 

 

우선 inode_create부터 만들어보자. 이 함수는 인자로 받은 sector에 찾아가 inode_disk를 sector에 넣고 연속적으로 실제 데이터를 넣을 sector를 확보해놓는 것이다. 

 여기서 바꿀 곳은 free-map (bitmap)으로 빈 sector를 찾던 것을 FAT free block list로 바꾸고, 실제 data도 비연속할당으로 넣는 것이다. 

/* 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)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    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;
        
        // 남은 자유 블륵이 sectors보다 많은가 
        if (ctrl_free_blocks_EA(0) >= sectors)
        {
            disk_write(filesys_disk, sector, disk_inode);
            if (sectors > 0)
            {
                static char zeros[DISK_SECTOR_SIZE];
                size_t i;

                // chain을 만든다.
                disk_inode->start = fat_create_chain(0);
                // 3.5 추가 >>
                disk_write(filesys_disk, sector, disk_inode);
                // << 3.5 추가
                disk_sector_t prev = disk_inode->start;
                for (i = 1; i < sectors; i++)
                {
                    // 섹터가 2개 이상이면 chain으로 이어준다. 
                    disk_sector_t sector_idx = fat_create_chain(prev);
                    disk_write(filesys_disk, sector_idx, zeros);
                    prev = sector_idx;
                }
            }
            success = true;
        }
        free(disk_inode);
    }
    return success;
/* ---------------------------- << Project.4 FAT << ---------------------------- */

 

 데이터의 계산은 메타데이터 (inode)를 뺀 실제 데이터인 것 같다. 그리고, inode_disk는 인자sector에 넣고, 따로 데이터를 넣을 공간을 할당받는다. length도 실질 데이터의 크기일 것이다.

 sectors로 필요한 sector 갯수를 가져오고 fat_create_chain()으로 chain을 만들어준다. 

 

 사실 이건 disk_sector_t랑 cluster_t 같기 때문에 사용 가능하지, 아니라면 cluster_to_sector를 써야할 것이다.

하지만 나는 추잡한 사람이니 이렇게하겠다. 

 

 

+++ 3/5 수정사항 +++

실 data의 첫 sector를 할당받고, 그 정보 (disk_inode->start) 를 inode에 써줘야한다.

 

 

 

 

 

 

inode_open()을 구현해보자.

 

 우선 이 함수는 inode_disk를 꺼내서 inode를 하나 만든다. inode_disk가 들어있는 sector를 인자로 준다. 그리고 open_inode_list 안에 있으면 reopen을 한다.

 

 그리고 inode_disk의 정보로 inode.data에 기입하는건데...

 

여기는 고칠 것이 없어보인다. 일단 패스 

 

 

 inode_reopen()도 마찬가지

 

 

 inode_close()는 고칠게 많아 보인다. 시작해보자!

 

 

 

/* 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)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */

    /* 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)
        {
            fat_remove_chain(inode->sector, 0);
            fat_remove_chain(inode->data.start, 0);
        }

        free(inode);
    }
    /* ---------------------------- << Project.4 FAT << ---------------------------- */

 우선 이 함수는 inode를 닫고, 만약 file이 삭제된다면 (inode->removed == true 라면)

inode_disk가 들어있는 곳과 실 data가 들어있는 곳을 지운다.

 

일단 inode->sector (inode_disk가 있는 곳) 을 fat_remove_chain(inode->sector, 0) 으로 지우고

그다음은 fat_remove_chain(inode->data.start, 0)

 

그런데 불안하다.

이렇게 간단하게 함수로 구현이 되나?

 

지금으로선 생각나는게 이것밖에 없으니 일단 넘어가자.

 

 

 

다음은 inode_read_at()이다. 

딱봐도 힘들 것 같긴 하다. 

에혀... 언제 고치냐 

 

 

/* 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)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    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;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */

 

우선, 함수를 파악해보자. 우선 offset이 어느 sector안에 있는가 파악하는데, 이는 byte_to_sector()로 수행한다.

그런데 byte_to_sector()는 연속할당기준이므로 이를 바꿔야한다. 

 

 

 

/* 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)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    ASSERT(inode != NULL);
    if (pos < inode->data.length) 
    {
        cluster_t cur = inode->data.start;
        uint32_t cnt = pos / DISK_SECTOR_SIZE;
        int i = 0;
        for (i ; i < cnt ; i++)
        {
            cur = fat_get(cur);
        }
        return cur;
    }
    else
        return -1;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */

 이런 식으로 pos가 속한 sector를 반환한다. ← 중요!!!

 

 

 

 그런데 쓱 보니깐, 이것만 바꾸면 더이상 바꿀 것이 없는 것 같은데?

그래도 하나씩 자세히 보자.

 

 

 

/* 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)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    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;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */

while 문을 돌면서 size가 0 보다 많으면 반복한다. 아마 size는 while 한번 돌면 읽은만큼 감소시킬 것이다.

offset이 속한 sector를 sector_idx에 저장하고, sector 안의 offset을 sector_ofs로 저장한다. 이는 맨 처음의 시작위치이며, 첫번째 섹터 후에는 쭉 0이 될 것이다.

 

 inode_left는 offset으로부터 끝까지의 크기.

sector_left는 sector 내에서 현 위치에서 섹터 끝까지의 크기

min_lieft 는 둘중에 작은 것을 내놓는다. (작은 것이 진짜 남은 데이터의 크기이다.)

 

그리고 disk_read를 수행한다. 이는 FAT_open()과 유사하니 패스

음.. 나머지는 안바꿔도 되겠는데?

 

 

inode_write_at() 도 똑같은 것 같은데.

 

 

일단 inode.c는 여기에서 종료하고 나중에 바꿀게 있으면 이 밑에 추가하도록 하겠다. 

댓글