[크래프톤 정글 3기] 12/8(금) TIL 🤒

ClassBinu·2023년 12월 8일
0

크래프톤 정글 3기 TIL

목록 보기
55/120

컨디션 그나마 좀 괜찬아짐..(아직 기침 심함)
10:30 착석
오늘은 핀토스 밀린 거 부지런히 해결해 보기


알고리즘 복습

선택 정렬

투포인터로 가장 작은 값을 기억했다가, 탐색이 끝나면 첫번째 포인터와 스왑

function selectionSort(arr) {
	for (let i = 0; i < arr.length; i++) {
      let lowest = i;
      for (let j = i + 1; j < arr.length; j++) {
        if(arr[j] < arr[lowest]) {
          lowest = j;
        }
      }
      if (i !== lowest) {
        let temp = arr[i];
        arr[i] = arr[lowest];
        arr[lowest] = temp;
      }
    }
}

가장 작은 값을 '선택'해서 스왑한다고 기억하기


Pintos(systemcall)

/* The main system call interface */
void
syscall_handler (struct intr_frame *f UNUSED) {
	// TODO: Your implementation goes here.
	printf ("system call!\n");
	thread_exit ();
}

과제 분석

현재는 프로세스(스레드)를 그냥 종료함.
이걸 시스템 호출 번호와 시스템 호출 인수를 검색하고, 적절한 작업을 수행해야 함.

타이머 및 I/O 장치(하드웨어)에서 발생하는 인터럽트가 외부 인터럽트라면,
시스템콜은 소프트웨어적으로 발생하는 내부 인터럽트
(페이지 오류, 0으로 나누기 등과 같은 오류일 수도 있음.)

x86-64에서는 syscall 명령어로 시스템 콜을 호출할 수 있음.

  • %rax는 시스템 호출 번호
  • 네 번째 인수는 %r10이다. (%rcx가 아님)

syscall_handler()가 제어권을 얻으려면
1. 시스템 호출 번호가 rax에 있고
2. 인수가 rid, rsi, rdx, r10, r8, r9 순서로 전달된다.

호출자의 인터럽트 프레임을 전달한다.

반환 값은 rax에 배치한다.


시스템콜 번호

enum으로 구현되어 있음

enum {
	/* Projects 2 and later. */
	SYS_HALT,                   /* Halt the operating system. */
	SYS_EXIT,                   /* Terminate this process. */
	SYS_FORK,                   /* Clone current process. */
	SYS_EXEC,                   /* Switch current process. */
	SYS_WAIT,                   /* Wait for a child process to die. */
	SYS_CREATE,                 /* Create a file. */
	SYS_REMOVE,                 /* Delete a file. */
	SYS_OPEN,                   /* Open a file. */
	SYS_FILESIZE,               /* Obtain a file's size. */
	SYS_READ,                   /* Read from a file. */
	SYS_WRITE,                  /* Write to a file. */
	SYS_SEEK,                   /* Change position in a file. */
	SYS_TELL,                   /* Report current position in a file. */
	SYS_CLOSE,                  /* Close a file. */

	/* Project 3 and optionally project 4. */
	SYS_MMAP,                   /* Map a file into memory. */
	SYS_MUNMAP,                 /* Remove a memory mapping. */

	/* Project 4 only. */
	SYS_CHDIR,                  /* Change the current directory. */
	SYS_MKDIR,                  /* Create a directory. */
	SYS_READDIR,                /* Reads a directory entry. */
	SYS_ISDIR,                  /* Tests if a fd represents a directory. */
	SYS_INUMBER,                /* Returns the inode number for a fd. */
	SYS_SYMLINK,                /* Returns the inode number for a fd. */

	/* Extra for Project 2 */
	SYS_DUP2,                   /* Duplicate the file descriptor */

	SYS_MOUNT,
	SYS_UMOUNT,
};

구현 과제

void halt (void);

그냥 막무가내 첫번째 시도.
핸들러 함수에서 시스템콜 번호 가져오고, 스위치 문으로 함수 호출하기

void
syscall_handler (struct intr_frame *f) {
	int syscall_number;
	syscall_number = f->R.rax;

	switch (syscall_number) {
		case SYS_HALT:
			halt();
			break;
		default:
			exit(-1);
			break;
	}
}

void halt(void) {
	power_off();
}

void exit(int status) {
	struct thread *cur = thread_current();
  cur->exit_status = status;

	thread_exit();
}

각 테스트별로 의존성이 있어서 하나만 구현하다고 되는 건 아니라고 함.
팀원이 어느 정도 구현해서 그거 이어서 시스템콜 구현한다!!

void exit (int status);

현재 사용자 프로그램 종료하고 커널로 돌아감

pid_t fork (const char *thread_name);

프로세스 포크

int exec (const char *cmd_line);

현재 프로세스를 실행 파일로 변경
? 실행한다는 건가?

int wait (pid_t pid);

자식 프로세스를 기다림

bool create (const char *file, unsigned initial_size);

새 파일 만듦

bool remove (const char *file);

파일 삭제

int open (const char *file);

파일 열기

int filesize (int fd);

열린 파일 사이즈 반환

int read (int fd, void *buffer, unsigned size);

열린 파일에서 바이트 읽기

int write (int fd, const void *buffer, unsigned size);

열린 파일에 바이트 쓰기

void seek (int fd, unsigned position);

특정 위치로 점프

unsigned tell (int fd);

현재 파일 포인터 반환

void close (int fd);

파일 설명자 닫기


UNUSED

syscall_handler (struct intr_frame *f UNUSED)

UNUSED인데 함수 내에서 접근이 가능했음.
UNUSED는 컴파일러한테 알려주는거지 실제로 사용을 막는 건 아님.


switch()

switch (f->R.rax)

switch문의 조건은 일반적으로 정수타입으로 평가됨


int read (int fd, void *buffer, unsigned size);

int read (int fd, void *buffer, unsigned length) {
	return 0;
}

양이 많아서 팀원들과 나눔
나는 read부분을 해보기로 함.
gitbook을 보자.

Reads size bytes from the file open as fd into buffer. Returns the number of bytes actually read (0 at end of file), or -1 if the file could not be read (due to a condition other than end of file). fd 0 reads from the keyboard using input_getc().

파일 디스크립터(fd)로 열린 파일에서 size 바이트를 buffer로 읽어들입니다. 실제로 읽혀진 바이트 수를 반환합니다(파일 끝에서는 0). 파일을 읽을 수 없는 경우(파일 끝이 아닌 다른 조건으로 인해) -1을 반환합니다. fd가 0인 경우, input_getc()를 사용하여 키보드로부터 읽어들입니다.

구현 계획

  1. fd는 rdi: 파일 디스크립터. int 타입으로 받기?
  2. buffer는 rsi: 버퍼의 메모리 주소
  3. length는 rdx: 읽고자 하는 바이트 크기
  4. 결과는 rax에 반환
  5. 파일을 못 읽으면 -1 (이게 어떤 경우지?)
  6. if (fd == 0) 처리

코드 분석

fd에는 뭐가 들어오는거야?

먼저 fd를 반환하는 fileopen을 보자.

struct file *
filesys_open (const char *name) {
	struct dir *dir = dir_open_root ();
	struct inode *inode = NULL;

	if (dir != NULL)
		dir_lookup (dir, name, &inode);
	dir_close (dir);

	return file_open (inode);
}

그럼 file_open은 뭘 반환하는 거?

struct file *
file_open (struct inode *inode) {
	struct file *file = calloc (1, sizeof *file);
	if (inode != NULL && file != NULL) {
		file->inode = inode;
		file->pos = 0;
		file->deny_write = false;
		return file;
	} else {
		inode_close (inode);
		free (file);
		return NULL;
	}
}

파일 구조체 포인터를 반환한다..?

struct file {
	struct inode *inode;        /* File's inode. */
	off_t pos;                  /* Current position. */
	bool deny_write;            /* Has file_deny_write() been called? */
};

그럼 file은 어떻게 읽어오는 거야

off_t
file_read (struct file *file, void *buffer, off_t size) {
	off_t bytes_read = inode_read_at (file->inode, buffer, size, file->pos);
	file->pos += bytes_read;
	return bytes_read;
}

inode_read_at 너구나?

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;
}

디스크까지 가서 읽네

void
disk_read (struct disk *d, disk_sector_t sec_no, 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_READ_SECTOR_RETRY);
	sema_down (&c->completion_wait);
	if (!wait_while_busy (d))
		PANIC ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no);
	input_sector (c, buffer);
	d->read_cnt++;
	lock_release (&c->lock);
}

오호, 여기서 락이랑 세마포어가 쓰인다.


핵심 의문

fd는 그냥 int임. 포인터가 아님? 근데 이걸로 어떻게 파일을 읽어 온다는 거지?
이거 해결해서 발표해야겠음.


i-node

i-node의 실체를 보았음.
커널 코드를 보니까 이런게 보이네. 좋음..

/* 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. */
};

read() 1차 구현

int read (int fd, void *buffer, unsigned length) {
	check_address(buffer);

	unsigned int bytesRead = 0;

	if (fd == 0) { 
		for (unsigned int i = 0; i < length; i++) {
			char c = input_getc();
			((char *)buffer)[i] = c;
			bytesRead++;

			if (c == '\n') break;
		}
	} else {
		struct file *f = fd_to_file(fd); // 이거 구현해야 됨
		if (f == NULL) {
			return -1; 
		}

		bytesRead = file_read(f, buffer, length);
	}

	return bytesRead;
}

fd가지고 어떻게 파일 읽어오냐?
fd는 그냥 식별자임. 결국에는 파일 구조체 포인터를 알아야 됨.
그 포인터를 넘기면 저~~~ 밑에 있는 inode_read_at() -> 'disk_read()' 얘네들이 파일 읽어줌.

시스템 콜 구현하는 거긴한데 파일 시스템에 대해서 대충이라고 알아야 될 것 같음.
자기전에 파일 시스템 유튜브 보기

오늘의 수확
FD는 파일 구조체를 연결하기 위한 식별자였다!!

pintos 프로젝트 하면서 커널 코드를 파보면서 느끼는 점
pintos는 사실 운영체제 맛보기임. 커널 코드를 구현하는 게 아니고
커다란 퍼즐 조각에 한 두 조각 빠진 걸 맞추는거였음.
근데 엄청 어려움. 후..

0개의 댓글