이번에는 ls를 구현하는것을 포스팅 해보도록 하겠다.
그전에 알아야 할 것들이 좀 많은데.. 디렉토리란 무엇인지, 시스템 콜이란 무엇인지, ls를 사용하기 위한 시스템 콜들을 알아보고 ls를 구현해보겠다.
💻디렉토리란?
리눅스에서 모든 것들은 파일로 표현된다. 디렉토리도 파일의 한 종류로서 여러개의 하위 파일이나 혹은 디렉토리의 주소들을 담고 있다. 리눅스에서는 FHS라는 디렉토리 구조를 사용하고 있는데, 이는 트리구조로 되어있다. 쉽게 말하자면 디렉토리 안에 디렉토리 안에 디렉토리 안에 파일이 있는 형식이다.
위는 리눅스에서의 디렉토리 계층을 보여주고 있다.
/usr/라는 디렉토리에 bin, include 등 여러가지 다른 디렉토리들이 들어있다. 이를 트리구조라고 한다.
💻 Kernel(커널)
커널은 리눅스 운영체제의 주요 구성 요소로, 컴퓨터 하드웨어와 프로세스를 잇는 핵심 인터페이스이다.
커널은 사용자가 볼 수 없으며, 커널 공간이라는 자신만의 작은 작업 공간에서 메모리를 할당하며 저장되는 모든 항목을 추적한다.
핵심 기능은 총 4가지가 있다.
1. 메모리 관리 : 메모리가 어디에서 무엇을 저장하는 데 얼마나 사용되는지를 추적한다.
2. 프로세스 관리 : 어느 프로세스가 중앙처리장치(CPU)를 언제 얼마나 오랫동안 사용할지를 결정한다.
3. 장치 드라이버 : 하드웨어와 프로세스 사이에서 중재자/인터프리터의 역할을 수행한다.
4. 시스템 호출 및 보안 : 프로세스의 서비스 요청을 수신한다.
💻 System Calls(시스템 호출)
운영체제에는 커널모드와 사용자모드 2개로 나뉘어 구동된다.
운영체제에서 프로그램이 구동되는데 있어 파일을 읽어 오거나, 파일을 쓰거나 화면에 메세지를 출력하는데에는 커널모드가 사용된다.
시스템콜은 커널의 앞서 언급한 핵심 기능 4번째에 내가 적어 놓았는데, 커널모드에서 쓰이는 기능들을 사용자모드가 사용 가능하게, 즉 프로세스가 하드웨어에 직접 접근하여서 필요한 기능을 사용할 수 있게 해주는 것이다.
시스템 호출에는 프로세스 제어, 파일 조작, 장치관리, 정보 유지등 많은 기능 들이 있다. 우리는 파일 조작기능을 이용하여 디렉토리에 무슨 파일이 있는지 확인할 것이다.
💻 ls를 구현해보자.
ls를 사용하면 현재 사용자의 디렉토리 위치에 있는 하위디렉토리의 목록을 모두 보여준다.
그렇다면, 차근차근 코드를 짜보자.
DIR* opendir(char *);
struct dirent readdir(DIR *);
int closedir(DIR*)
먼저 세가지 시스템 콜을 이용하여 디렉토리를 열고, 읽고 닫을거다.
opendir에 들어가는 파라미터는 현재 디렉토리의 이름이다. 이를 통해 디렉토리를 연다.
readdir을 이용하여서 현 디렉토리 안에 있는 파일의 정보를 하나씩 받아온다. while문을 사용하여서 계속 readdir를 해주며 해당 디렉토리의 이름을 printf해주면 ls가 구현될듯 싶다. 파라미터인 DIR은 dirent와 똑같이 디렉토리의 정보를 담고있다.
그리고 모두 끝이 나면 (readdir한 값이 NULL값이면) closedir을 해주면 된다.
readdir에서 반환값은 struct dirent이다. 그렇다면 이 struct dirent라는 구조체도 알아 볼 필요가 있다.
struct dirent{
ino_t d_no;
off_t d_off;
unsigned short d_reclen;
char d_name[256];
};
이러한 dirent 구조체가 있다. 앞에 것들이 무엇을 의미하는지는 나도 잘 모르겠다. 여기서 쓰는것은 d_name인데 이것은 파일의 이름을 의미한다. -> 공부하다가 발견한건데 ino_t d_no는 딱봐도 inode 가리키는거 같네.
💻 CODE
먼저, 코드의 흐름을 간략히 설명하겠다.
내가 기초프로그래밍과, 자료구조를 부실하게 들은 덕택(?)에 이해하기 어려웠었는데
main함수의 입력 파라미터가 int argc, char* argv[]이다. argc는 argument count로 입력받은 단어의 개수로 생각하면 쉽고 argv[]는 입력받은 단어들의 배열이라고 생각하면 된다.
argc가 1이라는 말은 두가지 경우가 있는데 뒷 부분에 argc를 -1씩 해주는 과정에서 1이 되거나 처음부터 1일 수 있다. 그때에는 하위 디렉토리를 모두 printf 해주었거나 printf 해줄 경우가 자기자신밖에 없다는 의미이다. 그러므로 do_ls(".")을 해준다. 여기서 . 은 자기자신 디렉토리를 의미한다.
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
void do_ls(char []);
int main(int argc, char* argv[]){
if(argc == 1)
do_ls(".");
else{
while(--argc){
printf("%s:\n", *++argv);
do_ls(*argv);
}
}
return 0;
}
void do_ls(char dirname[]){
DIR *dir_ptr; //the directory
struct dirent *direntp;
if((dir_ptr = opendir(dirname)) == NULL)
fprintf(stderr, "ls1: cannot open %s\n", dirname);
else{
while(direntp = readdir(dir_ptr)) !=NULL)
printf("%s\n", direntp->d_name);
closedir(dir_ptr);
}
}
'2-2 > 시스템 프로그래밍' 카테고리의 다른 글
[시스템 프로그래밍] Linux - cp 구현 (0) | 2022.10.25 |
---|---|
[시스템 프로그래밍] Linux System - ls-l 구현 (1) | 2022.10.13 |
[시스템 프로그래밍] Linux System - 기본 명령어 (0) | 2022.09.16 |