소개
이 장은 Linux 의 파일 및 디렉토리 인터페이스를 기반으로 합니다. 이 프로젝트는 파일 시스템의 본질을 중심으로 하며, lstat 함수와 디렉토리 연산을 사용하여 파일 유형을 재귀적으로 계산하는 프로그램을 구현합니다. 이를 통해 Linux 파일 시스템의 파일 유형 구성을 깊이 있게 이해할 수 있는 편리한 방법을 제공합니다. 또한, 이 프로젝트에서 개발된 파일 유형 계산 프로그램은 실제 학습 및 작업 환경에서 활용될 수 있습니다.
👀 미리보기
$ ./file_type .
regular files = 2, 66.67 %
directories = 1, 33.33 %
block special = 0, 0.00 %
char special = 0, 0.00 %
FIFOs = 0, 0.00 %
symbolic links = 0, 0.00 %
sockets = 0, 0.00 %
🎯 과제
이 프로젝트에서는 다음을 배우게 됩니다:
- Linux 파일 및 디렉토리 인터페이스를 사용하여 디렉토리 내의 파일 유형을 재귀적으로 계산하는 C 프로그램을 구현하는 방법.
🏆 성과
이 프로젝트를 완료하면 다음을 수행할 수 있습니다:
- Linux 에서
lstat함수를 사용하여 파일 정보를 얻을 수 있습니다. - 디렉토리 열기 및 디렉토리 항목 읽기와 같은 디렉토리 연산을 수행할 수 있습니다.
- 일반 파일, 디렉토리, 블록 특수 파일, 문자 특수 파일, 명명된 파이프, 심볼릭 링크 및 소켓을 포함한 다양한 파일 유형을 재귀적으로 계산하는 프로그램을 만들 수 있습니다.
- 디렉토리 내 각 파일 유형의 백분율을 계산하고 표시할 수 있습니다.
기본 지식 및 프로젝트 파일 생성
다음으로, 구상에서 구현까지의 단계를 소개하며, 주로 다음 C 언어 지식 포인트를 적용합니다:
stat구조체 및lstat함수,opendir,readdir함수,dirent구조체, 재귀, 함수 호출 등.
전체 프로그램에서 main, myftw, dopath, myfunc, 그리고 path_alloc과 같은 함수를 구성합니다.
myfunc함수는 주로 기준을 충족하는 파일 유형을 순회하고 계산합니다.dopath함수는 주로 경로를 재귀적으로 얻고 디렉토리인지 파일인지 판단합니다.myftw함수는path_alloc에서 전체 경로를 저장하기 위한 메모리 공간의 시작 주소와 크기를 얻는 것부터 시작합니다.path_alloc함수는 주로 경로 (전체 경로) 에 대한 메모리 공간을 할당합니다.
~/project 디렉토리에 file_type.c라는 새 파일을 생성하고, 선호하는 코드 편집기에서 엽니다.
cd ~/project
touch file_type.c
main 함수 설계
main 함수의 주요 기능은 먼저 명령줄 인수를 받아 유효성을 검사하는 것입니다. 그런 다음 myftw 함수를 호출하여 다양한 유형의 파일 수를 계산합니다. 마지막으로, 파일 유형의 백분율을 계산하고 출력합니다.
#include <dirent.h>
#include <limits.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define FTW_F 1 /* Flag for non-directory files */
#define FTW_D 2 /* Flag for directory files */
#define FTW_DNR 3 /* Flag for unreadable directory files */
#define FTW_NS 4 /* Flag for files that cannot be accessed by stat */
static char *fullpath;
static size_t pathlen;
/* Define the function for handling files */
typedef int Myfunc(const char *, const struct stat *, int);
static Myfunc myfunc;
static int myftw(char *, Myfunc *);
static int dopath(Myfunc *);
char *path_alloc(size_t *size);
static long nreg, ndir, nblk, nchr, nfifo, nslink, nsock, ntot;
int main(int argc, char *argv[])
{
int ret;
// Perform input validity check
if (argc != 2)
{
printf("Invalid command input! \n");
exit(1);
}
/* Calculate the number of various types of files */
ret = myftw(argv[1], myfunc);
/* Calculate the total number of files */
ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;
/* Avoid division by 0 to improve program stability */
if (ntot == 0)
ntot = 1;
/* Print the percentage of various types of files */
printf("regular files = %7ld, %5.2f %%\n", nreg,
nreg*100.0 / ntot);
printf("directories = %7ld, %5.2f %%\n", ndir,
ndir*100.0 / ntot);
printf("block special = %7ld, %5.2f %%\n", nblk,
nblk*100.0 / ntot);
printf("char special = %7ld, %5.2f %%\n", nchr,
nchr*100.0 / ntot);
printf("FIFOs = %7ld, %5.2f %%\n", nfifo,
nfifo*100.0 / ntot);
printf("symbolic links = %7ld, %5.2f %%\n", nslink,
nslink*100.0 / ntot);
printf("sockets = %7ld, %5.2f %%\n", nsock,
nsock*100.0 / ntot);
exit(ret);
}
*fullpath: 파일의 전체 경로를 저장하는 데 사용됩니다.pathlen: 파일 경로의 길이를 저장하는 데 사용됩니다.nreg: 일반 파일의 수.ndir: 디렉토리 파일의 수.nblk: 블록 특수 파일의 수.nchr: 문자 특수 파일의 수.nfifo: 명명된 파이프의 수.nslink: 심볼릭 링크 파일의 수.nsock: 소켓 파일의 수.ntot: 총 파일 수.
myftw 함수 설계
이 함수는 pathname을 처리하고 이를 전역 문자 배열에 저장한 다음 dopath를 호출하는 데 사용됩니다. path_alloc 함수는 공간을 할당하는 데 사용되며, fullpath가 전역 변수이므로 다른 함수에서 편리하게 사용할 수 있다는 점이 중요합니다. 그런 다음, dopath 함수를 호출하여 pathname 을 추가로 처리합니다 (디렉토리인지 여부).
static int myftw(char *pathname, Myfunc *func)
{
/* Allocate space for the string array to save the path */
fullpath = path_alloc(&pathlen);
/* If the allocated space is not enough to save the path, use realloc to reallocate */
if (pathlen <= strlen(pathname)) {
pathlen = strlen(pathname) * 2;
if ((fullpath = realloc(fullpath, pathlen)) == NULL);
printf("realloc failed!\n");
}
/* Save the pathname parameter in the full path. Pay attention: fullpath is a global variable
and can be called by dopath */
strcpy(fullpath, pathname);
/* Call the dopath function */
return(dopath(func));
}
/* Path array allocation */
char *path_alloc(size_t* size)
{
char *p = NULL;
if (!size)
return NULL;
p = malloc(256);
if (p)
*size = 256;
else
*size = 0;
return p;
}
'dopath' 함수 설계
int lstat(const char *path, struct stat *buf)함수: 파일이 심볼릭 링크인 경우,lstat는 심볼릭 링크 자체에 대한 정보를 반환하는 반면,stat는 링크가 가리키는 파일에 대한 정보를 반환합니다. 여기서 사용되는 데이터 구조 중 하나는stat구조체입니다. 이 구조체의 정의는 다음과 같습니다.
struct stat {
dev_t st_dev;
ino_t st_ino;
mode_t st_mode;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
dev_t st_rdev;
off_t st_size;
timestruc_t st_atim;
timestruc_t st_mtim;
timestruc_t st_ctim;
blksize_t st_blksize;
blkcnt_t st_blocks;
char st_fstype[_ST_FSTYPSZ];
};
DIR* opendir (const char * path )함수: 디렉토리를 열고, 실패 시 빈 포인터를 반환합니다.struct dirent *readdir(DIR *dir)함수:readdir()는 디렉토리 스트림dir에서 다음 디렉토리 항목을 반환합니다. 반환 값: 성공 시,readdir()는 다음 디렉토리 항목에 대한 포인터를 반환합니다. 디렉토리 스트림의 끝에 도달하면readdir()는 NULL 을 반환합니다.
/* dopath 는 디렉토리인지 여부를 결정하고, 카운팅을 위해 myfunc 함수를 직접 호출할지, 아니면 dopath 함수를 재귀적으로 호출할지를 선택하는 데 사용됩니다. */
static int dopath(Myfunc* func)
{
struct stat statbuf;
struct dirent *dirp;
DIR *dp;
int ret, n;
/* Call the lstat function to obtain the stat information of the pathname. If it fails, call the func function and pass FTW_NS */
if (lstat(fullpath, &statbuf) < 0)
return(func(fullpath, &statbuf, FTW_NS));
/* Check the st_mode of the file stat structure. If it is not a directory, call the func function and pass FTW_F, and then determine the file type by myfunc */
if (S_ISDIR(statbuf.st_mode) == 0)
return(func(fullpath, &statbuf, FTW_F));
/* The last case is that the pathname represents a directory. The normal return value of func is 0, so after executing func, it will not return and will continue to recursively call func */
if ((ret = func(fullpath, &statbuf, FTW_D)) != 0)
return(ret);
/* Path processing, expand the length of the path space */
n = strlen(fullpath);
if (n + NAME_MAX + 2 > pathlen) {
pathlen *= 2;
if ((fullpath = realloc(fullpath, pathlen)) == NULL)
printf("realloc failed!\n");
}
fullpath[n++] = '/';
fullpath[n] = 0;
/* Handle each entry in the directory */
if ((dp = opendir(fullpath)) == NULL)
return(func(fullpath, &statbuf, FTW_DNR));
while ((dirp = readdir(dp)) != NULL) {
if (strcmp(dirp->d_name, ".") == 0 ||
strcmp(dirp->d_name, "..") == 0)
continue; /* Ignore the current directory (.) and the parent directory (..) to avoid infinite loop */
strcpy(&fullpath[n], dirp->d_name); /* Append the name of the current directory entry after "/" */
if ((ret = dopath(func)) != 0) /* Then recursively call dopath with the new pathname */
break;
}
fullpath[n-1] = 0;
/* Close the directory */
if (closedir(dp) < 0)
printf("can't close directory %s", fullpath);
return(ret);
}
myfunc 함수 설계
myfunc 함수의 주요 목적은 stat을 기반으로 파일 유형을 결정하고 이를 카운트하는 것입니다. S_IFMT는 st_mode 플래그를 해석하는 데 사용되는 마스크입니다.
파일 유형을 결정하는 데 도움이 되는 몇 가지 매크로 정의가 있습니다. 이들은 함수 호출과 유사한 매개변수화된 매크로입니다.
S_ISBLK: 특수 블록 장치 파일인지 테스트합니다.S_ISCHR: 특수 문자 장치 파일인지 테스트합니다.S_ISDIR: 디렉토리인지 테스트합니다.S_ISFIFO: FIFO 장치인지 테스트합니다.S_ISREG: 일반 파일인지 테스트합니다.S_ISLNK: 심볼릭 링크인지 테스트합니다.S_ISSOCK: 소켓인지 테스트합니다.
static int myfunc(const char *pathname, const struct stat *statptr, int type)
{
switch (type) {
/* Handling for non-directory files */
case FTW_F:
switch (statptr->st_mode & S_IFMT) {
case S_IFREG: nreg++; break;
case S_IFBLK: nblk++; break;
case S_IFCHR: nchr++; break;
case S_IFIFO: nfifo++; break;
case S_IFLNK: nslink++; break;
case S_IFSOCK: nsock++; break;
case S_IFDIR:
printf("for S_IFDIR for %s", pathname);
}
break;
/* Handling for directory files */
case FTW_D:
ndir++;
break;
/* Handling for unreadable directories */
case FTW_DNR:
printf("%s directory is unreadable", pathname);
break;
case FTW_NS:
printf("%s error in stat", pathname);
break;
default:
printf("Type %d is unrecognized, pathname is %s", type, pathname);
}
return(0);
}
컴파일 및 테스트
터미널에서 다음 명령을 입력하여 컴파일하고 실행합니다.
cd ~/project
gcc -o file_type file_type.c
./file_type .
labex:project/ $ ls
file_type file_type.c
labex:project/ $ gcc -o file_type file_type.c
labex:project/ $ ./file_type .
regular files = 2, 66.67 %
directories = 1, 33.33 %
block special = 0, 0.00 %
char special = 0, 0.00 %
FIFOs = 0, 0.00 %
symbolic links = 0, 0.00 %
sockets = 0, 0.00 %
결과에 따르면 현재 디렉토리에서 일반 파일이 66.67% 를 차지하고 디렉토리가 33.33% 를 차지합니다.
시스템 디렉토리의 권한을 계산해야 하는 경우, 명령 실행 전에 sudo를 사용할 수 있습니다. 그럼에도 불구하고, 여전히 권한을 계산할 수 없는 파일이 일부 존재합니다.
요약
이 프로젝트의 훈련을 통해 Linux 파일 시스템에 대한 이해도를 높일 수 있습니다. 디렉토리 작업을 수행하는 방법을 배우고 파일 정보를 저장하는 stat 구조에 대한 더 깊은 이해를 얻게 될 것입니다. 이 프로젝트를 완료함으로써 실용적인 Linux 도구를 개발할 수 있습니다.



