본문 바로가기
Computer Science/UNIX & Linux

[UNIX/Linux] ep1) 디렉터리 다루기

by 클레어몬트 2024. 9. 11.

리눅스에서는 파일을 일반 파일특수 파일, 디렉터리 세 가지로 구분한다
디렉터리는 해당 디렉터리에 속한 파일을 관리하는 특별한 파일이다

리눅스의 세 가지 파일 구분
리눅스 파일의 종류

 
1. 일반 파일(-): 우리가 흔히 아는 데이터를 저장하는 파일
e.g. 텍스트 파일, 실행 파일, 프로그램 파일, 라이브러리, 이미지 파일 등과 같은 사용자가 읽거나 수정할 수 있는 파일들

/usr/bin 디렉터리

 
 
2. 특수 파일: 하드웨어 장치나 프로세스와의 인터페이스를 제공하는 파일 (운영체제와 하드웨어 간의 인터페이스 역할)
하드웨어 장치(예: 키보드, 마우스, 디스크)와 상호작용할 수 있도록 시스템 자원에 대한 접근을 제공한다
리눅스에서는 주로 /dev 디렉터리에서 특수 파일들을 확인할 수 있다

/dev 디렉터리

  • 블록 특수 파일(b): 디스크와 같은 장치에서 데이터를 블록 단위로 처리
  • 문자 특수 파일(c): 키보드, 터미널 등에서 데이터를 문자 단위(바이트 단위)로 처리
  • 심볼릭 링크, 소프트 링크(l): 특정 파일이나 디렉터리의 위치를 참조하는 특수 파일(파일 시스템 내에서 다른 파일의 경로를 가리키는 역할)

 
3. 디렉터리(d): 리눅스에서는 우리가 흔히 아는 폴더를 하나의 파일로 간주
디렉터리와 연관된 데이터 블록은 해당 디렉터리에 속한 파일의 목록과 inode를 저장한다
최상위 디렉터리는 루트 디렉터리(/) 이다

/ 최상위(root) 디렉터리

 
 
ㅇ파일의 종류 구분 방법: ls -l

제일 앞에 있는 '파일 종류 식별 문자'를 확인하자

 
 
 
(데이터 블록: 실제로 데이터가 저장되는 HDD 공간)
ㅇ inode(index node): 파일 시스템에서 파일과 디렉터리의 메타데이터가 저장되는 곳 (파일의 소유자나 크기 등의 정보와 실제 데이터를 저장하고 있는 데이터 블록의 위치를 나타내는 주소들이 저장)

일반적으로 각 파일이나 디렉터리는 고유의 inode를 가진다

 
 
리눅스 커널의 입장에서는 파일의 정보를 관리하는 자료구조로 사용된다

inode 구조

 
파일의 inode 번호는 ls -i 명령으로 알 수 있다

 
 
 
(리눅스 디렉터리 함수)

리눅스 디렉터리 함수 정리

 

[리눅스 디렉터리 생성/삭제 함수]

ㅇ int mkdir(const char* pathname, mode_t mode): 디렉터리 생성
- pathname : 디렉터리가 포함된 경로
- mode : 접근 권한
mkdir() 함수는 성공하면 0, 실패하면 -1을 반환

#include <stdio.h> // perror() 함수
#include <stdlib.h> // exit() 함수
#include <sys/stat.h>

int main() {
    // ha 라는 이름의 디렉터리를 생성하고 권한을 0755로 설정
    if (mkdir("ha", 0755) == -1) { 
        perror("ha"); // 에러 메시지 출력
        exit(1); // 0이 아닌 값은 에러를 의미
    }
    
    return 0;
}

 
 
ㅇ int rmdir(const char* pathname): 디렉터리 삭제
- pathname : 디렉터리가 포함된 경로
똑같이 성공하면 0, 실패하면 -1을 반환

#include <stdio.h>
#include <stdlib.h> // exit() 함수
#include <unistd.h>

int main() {
    // ha 라는 이름의 디렉터리를 제거
    if (rmdir("ha") == -1) { 
        perror("ha"); // 에러 메시지 출력
        exit(1); // 0이 아닌 값은 에러를 의미
    }
    
    return 0;
}

 
 
 

[디렉터리 관리 함수]

ㅇ char* getcwd(char* buf, size_t size): 현재 위치 확인 (cwd: current working directory)
- buf : 현재 디렉터리의 절대 경로를 저장할 버퍼 주소
- size : 버퍼의 크기
getcwd() 함수는 경로를 저장할 버퍼의 주소와 버퍼의 크기를 인자로 받고 현재 디렉터리 값을 반환한다
[인자를 지정하는 방법]
① buf에 경로를 저장할 만큼 충분한 메모리를 할당하고, 그 크기를 size에 지정
② buf에 NULL을 지정하고 할당이 필요한 메모리 크기를 size에 지정
③ buf에 NULL을 지정하고 size는 0으로 지정

#include <stdio.h>
#include <stdlib.h> // exit() 함수
#include <unistd.h> // getcwd() 함수

// BUFSIZ 값은 stdio.h 파일에 8192(8KB)로 정의
int main() {
    char* cwd; // 현재 작업 디렉터리(current working directory)
    char wd1[BUFSIZ]; // 경로 저장 배열1
    char wd2[10]; // 경로 저장 배열2

    // 현재 작업 디렉터리 경로를 wd1에 저장
    getcwd(wd1, BUFSIZ); // BUFSIZ 크기만큼 버퍼에 경로를 저장
    printf("wd1 = %s\n", wd1); // 경로를 출력

    // getcwd 함수에서 동적 메모리 할당하여 현재 작업 디렉터리 경로를 얻음
    cwd = getcwd(NULL, BUFSIZ); // BUFSIZ 크기의 메모리를 자동으로 할당하여 경로를 얻음
    printf("cwd1 = %s\n", cwd); // 얻은 경로 출력
    free(cwd);

    // getcwd 함수에서 메모리 크기를 0으로 설정하면 적절한 크기의 메모리를 자동 할당
    cwd = getcwd(NULL, 0); // 0을 인자로 주면, 자동으로 메모리를 할당하여 경로를 반환
    printf("cwd2 = %s\n", cwd); // 얻은 경로 출력
    free(cwd);

    // 크기가 10인 배열 wd2에 경로를 저장하려고 하므로, 오류가 발생할 가능성이 있음
    if (getcwd(wd2, 10) == NULL) {
        perror("getcwd"); // 에러 메시지 출력
        exit(1); // 0이 아닌 값은 에러를 의미
    }

    return 0;
}
// 실행 결과: 경로를 정상적으로 출력하다가, 맨 마지막에 에러 메시지를 출력
// 에러 메시지는 결과가 메모리 범위를 벗어났다는 뜻

 
 
ㅇ char* get_current_dir_name(void): 현재 위치 확인
현재 디렉터리의 절대 경로를 반환
시스템이 메모리를 자동으로 할당해 경로를 저장하고 반환한다

#define _GNU_SOURCE // GNU 확장 함수들을 사용하기 위한 매크로 정의
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // get_current_dir_name() 함수

int main() {
    char* cwd; // 현재 작업 디렉터리(current working directory)

    // 현재 작업 디렉터리의 경로를 얻고, 그 경로에 대한 동적 메모리 할당
    cwd = get_current_dir_name(); // 경로를 검색
    printf("cwd = %s\n", cwd); // 경로를 출력
    free(cwd);

    return 0;
}

 
 
ㅇ int rename(const char* oldpath, const char* newpath): 디렉터리명 변경
- oldpath : 변경할 파일/디렉터리명
- newpath : 새 파일/디렉터리명
성공하면 0, 실패하면 -1을 반환
만약 두 번째 인자로 지정한 이름이 이미 있으면 해당 디렉터리를 삭제한다
실행 도중 오류가 발생하면 원본과 새로운 디렉터리명이 모두 남는다
+ rename() 함수는 파일명을 변경하는 데도 사용할 수가 있다
(rename() 함수의 매뉴얼을 보려면 man -s 2 rename을 이용하자)

#include <stdio.h> // rename() 함수
#include <stdlib.h> // exit() 함수

int main() {
    // "ha" -> "dong" 디렉터리명 변경
    if (rename("ha", "dong") == -1) {
        perror("rename"); // 에러 메시지 출력
        exit(1); // 비정상 종료를 의미
    }

    return 0;
}

 
 
ㅇ int chdir(const char* path): 디렉터리 이동 (change directory)
- path : 이동하려는 디렉터리 경로
성공하면 0, 실패하면 -1을 반환
(절대 경로와 상대 경로 모두 사용 가능)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // getcwd() 함수, chdir() 함수

int main() {
    char* cwd; // 현재 작업 디렉터리(current working directory)

    cwd = getcwd(NULL, BUFSIZ);
    printf("1.Current Directory: %s\n", cwd);

    chdir("dong"); // "dong" 디렉터리로 이동

    cwd = getcwd(NULL, BUFSIZ);
    printf("2.Current Directory: %s\n", cwd);
    free(cwd);
    
    return 0;
}
// 실행 결과: 디렉터리가 이동, 하지만 디렉터리 이동은 프로그램 내부에서만 진행된다
// 따라서 프로그램 종료 후, pwd 명령으로 보면 현재 디렉터리가 바뀐 것은 아님을 알 수 있음

 
 
ㅇ int fchdir(int fd): 디렉터리 이동
- fd : 이동하려는 디렉터리의 파일 디스크립터
※ 파일 디스크립터: open() 함수로 디렉터리를 열고 돌려받는 것, 따라서 fchdir() 함수를 사용하려면 open() 함수로 해당 디렉터리를 먼저 열어야 한다
성공하면 0, 실패하면 -1을 반환

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // getcwd() 함수, fchdir() 함수, close() 함수
#include <fcntl.h> // O_RDONLY: 읽기 전용으로 파일을 열기 위한 flag

int main() {
    char* cwd; // 현재 작업 디렉터리(current working directory)
    int fd;

    cwd = getcwd(NULL, BUFSIZ);
    printf("1.Current Directory: %s\n", cwd);

    fd = open("dong", O_RDONLY); // open 함수로 dong 디렉터리를 실행
    fchdir(fd); // fchdir 함수로 디렉터리를 이동

    cwd = getcwd(NULL, BUFSIZ);
    printf("2.Current Directory: %s\n", cwd);
    close(fd);
    free(cwd);
    
    return 0;
}
// 실행 결과: 디렉터리가 이동, 하지만 디렉터리 이동은 프로그램 내부에서만 진행된다
// 따라서 pwd 명령으로 확인하면 프로그램 내부에서만(프로그램 실행 동안만) 디렉터리가 바뀐 것을 알 수 있다

 
 
 

[디렉터리 읽기 함수] (dirent: directory entry)

ㅇ DIR* opendir(const char* name): 디렉터리 열기
#include <sys/types.h>
#include <dirent.h>
 
ㅇ int closedir(DIR* dirp): 디렉터리 닫기
#include <sys/types.h>
#include <dirent.h>
 
ㅇ struct dirent* readdir(DIR* dirp): 디렉터리 내용 읽기
#include <dirent.h>
 
ㅇ long telldir(DIR* dirp), void seekdir(DIR* dirp, long loc), void rewinddir(DIR* dirp): 디렉터리의 내용을 읽는 위치 변경하기 (디렉터리 오프셋)
#include <sys/types.h>
#include <dirent.h>
 
 
 
 
 
 
참고 및 출처: 시스템 프로그래밍 리눅스&유닉스(이종원)