ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C언어를 이용한 파일시스템 엑세스!
    C,C++ 2024. 4. 13. 01:21

    개발자 뉴비는 너무 바빠서 오랫동안 포스팅을 못했다. ㅠㅠ 매일매일 새로운 내용을 배워서 포스팅하려고 했지만... 포스팅 할만한 내용이 없었다 ㅠㅠ 하지만 드디어! 기존에 안해봤던 파일시스템을 제어할일이 생겼다. 그래서 명령줄 도구와 dirent.h 라이브러리를 이용해서 파일 시스템에 접근하는 법을 공부해 봤다. 먼저 간단하게 특정 디렉토리 내에 최상위 디렉토리들을 카운트 해봤다. 먼저 Unix계열 시스템에서 사용되는 명령어를 조합해 보았다.

    find mypath -maxdepth 1 -type d | wc -l

    find: Unix 기반 시스템에서 파일이나 디렉토리를 검색하는 명령어.
    mypath: find 명령어가 검색을 시작할 기준 디렉토리.
    -maxdepth 1: find 명령어의 검색 깊이를 제한. 1은 검색을 시작 디렉토리와 그 바로 아래 디렉토리까지만 포함하도록 제한하는 것을 의미한다. 이 숫자를 바꾸면 하위 디렉토리의 숫자도 파악이 가능하다.
    -type d: 검색 대상을 디렉토리(d)로 제한한다. 모든 파일을 확인하려면 -type f를 사용하면 된다.
    |: 파이프(pipe)는 한 명령어의 출력을 다른 명령어의 입력으로 전달하는 역할을 한다. 이 경우, find 명령어의 출력이 wc 명령어로 전달된다.
    wc: 'word count'의 약자로, 텍스트 데이터의 줄 수, 단어 수, 문자 수를 세는 명령어.
    -l: wc 명령어의 옵션으로, 입력받은 데이터의 줄 수를 세라는 지시. 여기서는 find 명령어로부터 받은 출력(디렉토리 목록)의 줄 수를 세게 된다. -w는 단어의 수를 세고, -c는 바이트 수, -m은 문자의 수를 의미한다.

    이런식으로 find mypath -maxdepth 1 -type d 의 출력이 wc -l에 들어가는 방식이다 find mypath -maxdepth 1 -type d | wc -l의 출력으로는 9가 나와서 디렉토리 수가 9개인걸 알 수 있다. 실제 상위 디렉토리인 /tystory도 맨 윗줄에 카운트 되었기때문에 하위 디렉토리는 -1을 해주어야 한다. 위에 설명한 명령줄 도구를 C언어로 사용하기 위해 popen 함수를 사용해 보았다. popen 함수는 실행 시에 지정된 명령어 쉘을 수행하는 독립 프로세서를 생성하고 호출 프로세스의 파이프와 연결된다. popen 함수의 출력으로 연결된 파이프를 제어하기 위한 파일 포인터를 반환하게 되서 개발자는 표준 C 라이브러리의 파일 입출력 함수를 사용하여 데이터를 읽고 쓸 수 있다. 아래는 예제 코드이다 mypath 대신에 실제 경로를 입력하면 된다.

    #include <stdio.h>
    #include <stdlib.h>
    #define TEST_PATH "mypath"
    int get_file_number(char *path) {
    char cmd[512]; // 충분히 큰 크기로 버퍼 선언
    char buf[128];
    FILE *fp;
    // 명령어 구성
    sprintf(cmd, "find %s -maxdepth 1 -type d | wc -l", path);// 명령줄에 입력할 문자열을 만듬
    if ((fp = popen(cmd, "r")) == NULL) {// popen() 함수를 이용하여 명령어를 실행하고 결과를 읽을 파일 포인터를 반환
    perror("Error opening pipe");//실패시 에러 메시지 출력
    return -1;//실패시 -1 반환
    }
    if (fgets(buf, sizeof(buf), fp) != NULL) { // 파일 포인터로부터 한 줄을 읽어서 buf에 저장
    int num_dirs = atoi(buf) - 1; // 상위 디렉토리 자신을 제외
    printf("Number of immediate subdirectories: %d\n", num_dirs);//결과 출력
    }
    if (pclose(fp)) {// 파일 포인터를 닫고 명령어의 종료 상태를 반환
    perror("Command not found or exited with error status");//실패시 에러 메시지 출력
    return -1;//실패시 -1 반환
    }
    return 0;//성공시 0 반환
    }
    int main() {
    get_file_number(TEST_PATH); // 함수 호출
    return 0;
    }

    해당 코드를 컴파일 하고 돌리면 짜잔!!!

     

    8개가 나왔다!!  이번엔 dirent.h 라이브러리를 이용해서 같은 기능을 구현해보자 dirent.h라이브러리는 POSIX 호환 시스템과 Linux에서 동작한다 하지만 윈도우즈에서 쓰려면 다른 라이브러리를 써야할지두..... 일단 구현 함수다!

    int get_file_number_without_cmd(const char *path) {
    DIR *dir; // 디렉토리 스트림에 대한 타입으로 dirent.h 헤더파일에 정의되어 있음 opendir 함수의 반환 포인터로 디렉토리 엔트리를 읽을 수 있음.
    struct dirent *entry;//디렉토리 엔트리의 정보를 저장하는 구조체 디렉토리 엔트리란 파일 시스템의 디렉토리에 존재하는 각 파일이나 하위 디렉토리에 대한 정보, readdir()함수를 사용하여 디렉토리 스트림에서 다음 엔트리를 읽을 때마다 struct dirent 타입의 포인터가 반환
    int count = 0;// 디렉토리의 개수를 저장할 변수
    dir = opendir(path);// 디렉토리 열기
    if (dir == NULL) {// dir포인터의 값으로 디렉토리 열기 실패여부를 판단
    perror("Failed to open directory");//실패시 에러 메시지 출력
    return -1;//실패시 -1 반환
    }
    while ((entry = readdir(dir)) != NULL) { // 디렉토리 내부를 반복하여 탐색 entry가 NULL이 되면 와일루프 종료
    if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {//상위 경로를 나타내는 .과 ..을 제외
    if (entry->d_type == DT_DIR) { // 디렉토리인 경우 카운트
    count++; // 디렉토리 개수 증가
    }
    }
    }
    closedir(dir);// 디렉토리 닫기
    printf("Number of immediate subdirectories without_cmd: %d\n", count);//결과 출력
    return count;
    }

    이제 함수를 다 만들었으니 성능 체크를 해보자!!

     

    #include <stdio.h>
    #include <stdlib.h>
    #include <dirent.h>
    #include <string.h>
    #include <sys/types.h>
    #include <time.h> // 시간 측정을 위해 추가

    #define TEST_PATH "mypath"
    int get_file_number(char *path) {
    char cmd[512]; // 충분히 큰 크기로 버퍼 선언
    char buf[128];
    FILE *fp;
    // 명령어 구성
    sprintf(cmd, "find %s -maxdepth 1 -type d | wc -l", path);// 명령줄에 입력할 문자열을 만듬
    if ((fp = popen(cmd, "r")) == NULL) {// popen() 함수를 이용하여 명령어를 실행하고 결과를 읽을 파일 포인터를 반환
    perror("Error opening pipe");//실패시 에러 메시지 출력
    return -1;//실패시 -1 반환
    }
    if (fgets(buf, sizeof(buf), fp) != NULL) { // 파일 포인터로부터 한 줄을 읽어서 buf에 저장
    int num_dirs = atoi(buf) - 1; // 상위 디렉토리 자신을 제외
    printf("Number of immediate subdirectories: %d\n", num_dirs);//결과 출력
    }
    if (pclose(fp)) {// 파일 포인터를 닫고 명령어의 종료 상태를 반환
    perror("Command not found or exited with error status");//실패시 에러 메시지 출력
    return -1;//실패시 -1 반환
    }
    return 0;//성공시 0 반환
    }
    int get_file_number_without_cmd(const char *path) {
    DIR *dir; // 디렉토리 스트림에 대한 타입으로 dirent.h 헤더파일에 정의되어 있음 opendir 함수의 반환 포인터로 디렉토리 엔트리를 읽을 수 있음.
    struct dirent *entry;//디렉토리 엔트리의 정보를 저장하는 구조체 디렉토리 엔트리란 파일 시스템의 디렉토리에 존재하는 각 파일이나 하위 디렉토리에 대한 정보
    //readdir()함수를 사용하여 디렉토리 스트림에서 다음 엔트리를 읽을 때마다 struct dirent 타입의 포인터가 반환
    int count = 0;// 디렉토리의 개수를 저장할 변수
    dir = opendir(path);// 디렉토리 열기
    if (dir == NULL) {// dir포인터의 값으로 디렉토리 열기 실패여부를 판단
    perror("Failed to open directory");//실패시 에러 메시지 출력
    return -1;//실패시 -1 반환
    }
    while ((entry = readdir(dir)) != NULL) { // 디렉토리 내부를 반복하여 탐색 entry가 NULL이 되면 와일루프 종료
    if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {//상위 경로를 나타내는 .과 ..을 제외
    if (entry->d_type == DT_DIR) { // 디렉토리인 경우 카운트
    count++; // 디렉토리 개수 증가
    }
    }
    }
    closedir(dir);// 디렉토리 닫기
    printf("Number of immediate subdirectories without_cmd: %d\n", count);//결과 출력
    return count;
    }

    int main() {
    struct timespec start, end;//시간 측정을 위한 변수
    long long cmd_time_used, no_cmd_time_used;//시간 측정을 위한 변수
    // Measure time for get_file_number
    clock_gettime(CLOCK_MONOTONIC, &start);//시간 측정 시작
    get_file_number(TEST_PATH);//함수 호출
    clock_gettime(CLOCK_MONOTONIC, &end);//시간 측정 종료
    cmd_time_used = (end.tv_sec - start.tv_sec) * 1000000000LL + (end.tv_nsec - start.tv_nsec);//시간 계산
    printf("Execution time using command: %lld nanoseconds\n", cmd_time_used);//결과 출력
    // Measure time for get_file_number_without_cmd
    clock_gettime(CLOCK_MONOTONIC, &start);//시간 측정 시작
    get_file_number_without_cmd(TEST_PATH);//함수 호출
    clock_gettime(CLOCK_MONOTONIC, &end);//시간 측정 종료
    no_cmd_time_used = (end.tv_sec - start.tv_sec) * 1000000000LL + (end.tv_nsec - start.tv_nsec);//시간 계산
    printf("Execution time without using command: %lld nanoseconds\n", no_cmd_time_used);//결과 출력
    return 0;
    }

    과연 결과는!!!

    dirent.h라이브러리가 압도적으로 빠르다 앞으로 명령줄 도구는 최대한 안쓰는 방향으로 해야겠다 ㅠㅠ

    'C,C++' 카테고리의 다른 글

    리눅스 C언어 개발환경  (0) 2024.03.05
    lock free queue  (2) 2024.03.05
Designed by Tistory.