알아볼 함수.
fork, wait, waitpid, wait3,
wait4, kill, exit, getcwd, chdir, stat,
stat, fstat, execve, dup, dup2, pipe,
opendir, readdir, closed, strerror, errno
* pid란 process id의 줄임말이다.
fork
형태
#include <unistd.h>
pid_t fork(void);
설명
fork는 자식프로세스를 만들기 위해서 사용되는 프로세스 생성기이다. fork 에 의해 생성된 자식 프로세스는 자신만의 PID 를가지게 되며, PPID는 부모프로세스의 PID를 가지게 된다.
반환 값
성공할경우 자식 프로세스의 PID가 부모에게 리턴되며, 자식에게는 0이 리턴된다. 실패할경우에는 -1 이 리턴되며, 절적한 errno 값이 설정된다.
에러
EAGAIN: 자식프로세스를 위한 태스크 구조체를 할당할 수 없을 경우, 주로 메모리 문제와 관련된다.
예제
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int pid;
pid = fork();
if (pid > 0)
{
printf("부모 프로세스 %d : %d\n", getpid(), pid);
pause();
}
else if (pid == 0)
{
printf("자식 프로세스 %d\n", getpid());
pause();
}
else if (pid == -1)
{
perror("fork error : ");
exit(0);
}
}
코드 실행 결과
[root@localhost test]# ./fork
부모 프로세스 1322 : 1323
자식 프로세스 1323
ps 명령을 이용해서 프로세스 상태를 확인할 수 있음
[root@localhost /root]# ps -efj | grep fork
UID PID PPID PGID SID C STIME TTY TIME CMD
root 1322 748 1322 748 0 10:33 ttyp0 00:00:00 ./fork
root 1323 1322 1322 748 0 10:33 ttyp0 00:00:00 ./fork
(여기까지 무슨 말인지 이해가 잘 안 간다면 아래 블로그를 추가로 참고하자.
wait
형태
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
설명
주로 fork() 를 이용해서 자식 프로세스(:12)를 생성했을때 사용한다. wait() 를 쓰면 자식프로세스가 종료할때까지 해당영역에서 부모프로세스가 sleep() 모드로 기다리게 된다. 이는 자식프로세스와 부모프로세스의 동기화를 위한목적으로 부모프로세스가 자식프로세스보다 먼저 종료되어서 자식프로세스가 고아 프로세스(PPID 가 1)인 프로세스가 되는걸 방지하기 위한 목적이다.
만약 자식 프로세스가 종료되었다면 함수는 즉시 리턴되며, 자식이 사용한 모든 시스템자원을 해제한다.
그런데 어떤이유로 부모가 wait()를 호출하기 전에 자식 프로세스가 종료버리는 경우도 있다(잘못된 메모리 연산등으로 인한 죽음, 혹은 정상적으로), 이럴경우 자식프로세스는 좀비(:12)프로세스가 되는데, wait()함수는 즉시 리턴하도록 되어있다.
wait()의 인자 status 를 통하여 자식 프로세스의 상태를 받아올수 있는데, 자식프로세스의 상태값은 자식프로세스의 종료값 * 256(FF) 이다.
반환값
종료된 자식의 프로세스 ID는 에러일경우 -1 그렇지 않을경우 0을 반환한다.
예제
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
int main()
{
int pid;
int status;
pid = fork();
// 자식 프로세스
if (pid < 0)
{
perror("FORK ERROR :");
exit(0);
}
if (pid == 0)
{
int i;
for (i = 0; i < 5; i++)
{
printf("Child : %d\n", i);
sleep(2);
}
exit(3);
}
else
{
// 부모프로세스는 자식프로세스가 종료할때까지 기다린다.
printf("I wait Child(%d)\n", pid);
wait(&status);
printf("Child is exit (%d)\n", status);
}
}
위의 예제를 컴파일후 실행시키면 다음과 같은 결과를 보여준다.
[root@localhost c_source]# ./wait
I wait Child(12128)
Child : 0
Child : 1
Child : 2
Child : 3
Child : 4
Child is exit (768)
waitpid
형태
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
설명
waitpid 함수는 인수로 주어진 pid 번호의 자식프로세스가 종료되거나, 시그널 함수를 호출하는 신호가 전달될때까지 waitpid 호출한 영역에서 일시 중지 된다.
만일 pid 로 지정된 자식이 waitpid 함수 호출전에 이미 종료되었다면, 함수는 즉시 리턴하고 자식프로세스는 "좀비프로세스"로 남는다.
pid 값은 다음중 하나가 된다.
-
< -1
프로세서 그룹 ID가 pid 의 절대값과 같은 자식 프로세스를 기다린다.
-
-1
임의의 자식프로세스를 기다린다. 이것은 wait(2) 와 동일하다.
-
0
프로세스 그룹 ID가 호출 프로세스의 ID와 같은 자식프로세스를 기다린다.
-
> 0
프로세스 ID가 pid 의 값과 같은 자식 프로세스를 기다린다.
options 의 값은 0이거나 다음값들과의 OR 이다.
반환값
종료된 자식 프로세스의 ID는 에러일때 -1반환 하고, 만일 어떤 자식도 이용할수 없다면 0을 반환한다.
예제
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
int main()
{
int pid;
int status;
pid = fork();
if (pid < 0)
{
perror("fork error : ");
exit(0);
}
if (pid == 0)
{
printf("Im Child\n");
sleep(10);
return 2;
}
else
{
printf("Parent: wait (%d)\n", pid);
waitpid(pid, &status, 0);
if (WIFEXITED(status))
{
printf("정상종료\n");
printf("리턴값 %d\n", WEXITSTATUS(status));
/*
WEXITSTATUS(status):
exit()를 호출하기 위한 인자나 return 값이 설정되고 종료된 자식의 반환 코드의 최하위 8비트를 평가한다.
이 매크로는 정상종료 WIFEXITED(status) 일때만 평가된다.
WIFEXITED(status):
자식이 정상적으로 종료되었다면 non-zero 이다.
*/
}
else if (WIFSIGNALED(status))
{
printf("신호받았음\n");
printf("신호번호 %d\n", WTERMSIG(status));
/*
WIFSIGNALED(status):
자식프로세스가 어떤 신호때문에 종료되었다면 참을 반환한다.
WTERMSIG(status):
자식프로세스를 종료하도록한 신호의 번호를 반환한다. 당연히 WIFSIGNALED 가 non_zero 일 경우에만 사용할수 있다.
*/
}
}
exit(0);
}
W_EXITSTATUS
exit status 127 indicates that there is some problem with path or there is a typo.
Few exit status codes are listed below for extra information :
- 1 : Miscellaneous errors, such as “divide by zero” and other impermissible operations.
- 2 : Missing keyword or command, or permission problem.
- 126 : Permission problem or command is not an executable
- 128 : invalid argument to exit.
signal
형태
sig_t signal(int sig, sig_t func)
인자
첫 번째 인자인 int sig는 시그널 번호이고, 두 번째 인자인 func는 해당 시그널을 처리할 핸들러임.
예제
#include <signal.h>
#include <unistd.h>
void sig_handler(int signo); // 비프음 발생 함수
int main()
{
int i = 0;
signal(SIGINT, (void *)sig_handler);
while(1)
{
printf("%d\n", i);
i++;
sleep(1);
}
return 1;
}
void sig_handler(int signo)
{
printf("SIGINT 발생\n");
}
SIGINT(:12) 를 발생시키는 가장간단한 방법은 키보드의 Ctrl+C 를 입력하는 방법이다. 위 프로그램을 실행시킨후 Ctrl+C 를 실행시키면 SIGINT 시그널이 전달되고 어플리케이션은 해당 시그널 핸들러인 sig_handler() 함수를 실행시키는걸 볼수 있을것이다.
signal 종류
아래 블로그 참고
http://blog.naver.com/PostView.nhn?blogId=bitnang&logNo=70172674474
키보드 입력으로 발생시킬 수 있는 시그널은 Ctrl+C 외에도 아래의 몇가지가 있다.
키 | signal | 내용 |
---|---|---|
Ctrl+C | SIGINT | 프로세스를 종료시킨다. |
Ctrl+Z | SIGSTP | 프로세스를 중단시킨다. |
Ctrl+\ | SIGQUIT | core dump를 남기고 프로세스를 종료시킨다. |
kill
프로세스(:12)에게 시그널(:12)을 보낸다.
형태
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
설명
kill(2) 시스템콜(:12)은 특정 프로세스나 프로세스 그룹에 시그널을 보내기 위해서 사용한다.
- pid 가 양수이면, sig 시그널을 pid 로 보낸다.
- pid 가 0이면 현재 프로세스가 속한 프로세스 그룹의 모든 프로세스에게 sig 시그널을 보낸다.
- pid 가 -1 이면, 1번 프로세스를 제외한 모든 프로세스에서 sig 시그널을 보낸다.
- pid 가 -1 보다 작으면, -pid 프로세스(:12)가 포함된 모든 그룹(:12)의 프로세스(:12)에게 sig 시그널을 보낸다.
- sig가 0이면 어떤 시그널(:12)도 보내지 않지만, 에러 검사는 할수 있다.
시그널의 종류는 Unix(:12) 시스템마다 조금씩 다를수 있다. 자신의 Unix 시스템에서 사용할수 있는 시그널 목록은 kill(1) 쉘 명령어를 이용해서 확인할수 있다.
[root@localhost test]# kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD
18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN
22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
...
반환값
성공할경우 0을 실패했을경우에는 -1을 반환하며, 적당한 errno(:12) 값을 설정한다.
예제
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int pid;
int sig_num;
// 아규먼트로 pid 번호와
// 전송할 signal 번호를 받아들여서
// 이를 해당 pid 로 보낸다.
pid = atoi(argv[1]);
sig_num = atoi(argv[2]);
kill(pid, sig_num);
}
exit
exit함수는 프로그램 종료를 수행. 프로그램이 종료되면 프로그램의 제어권은 운영체제로 반환됨. 그때 exit(1)은 에러로 인한 종료, exit(0)은 정상종료를 뜻함. 또한 각각 EXIT_SUCCESS, EXIT_FAILURE 로 미리 정의되어있기 때문에 사용가능. 이것은 exit뿐 아니라 return 에서도 사용 가능.
형태
#include <stdlib.h>
void exit(int status)
예제
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
// 사용자가 옵션 지정하지 않았을 때 에러 메시지 출력하고 종료
if (argc == 1) {
exit(1); // 에러시 강제 종료
}
printf("%s\n", argv[1]);
return 0; // 에러 없을 때, 자연 종료
}
자식 프로세스가 비정상적으로 종료되었을 경우에는 커널에서 비정상 종료 상태를 별도로 설정
부모 프로세스는 자식의 종료 상태(정상/비정상)를 wait() 함수 또는 waitpid() 함수로 얻을 수 있음.
getcwd
현재 작업디렉토리의 이름을 size 만큼 길이로 buf에 복사한다.
형태
#include <unistd.h>
char *getcwd(char *buf, size_t size);
반환값
실패했을경우 NULL 을 되돌려준다. 주로 현재 디렉토리에 대한 읽기 권한이 없을 경우 발생한다.
예제
#include <unistd.h>
#include <stdio.h>
int main()
{
char buf[255];
getcwd(buf, 255);
printf("%s\n", buf);
}
결과
chdir
형식 및 설명
int chdir( const char *dirname );
//dirname : 변경할 디렉토리의 경로
//반환값 : 정상 일 때 0, 에러 시 -1
예제
#include <stdio.h>
#include <direct.h> //chdir
#ifndef _MAX_PATH
#define _MAX_PATH 260
#endif
int main( )
{
char strBuffer[_MAX_PATH] = { 0, };
char strChangeDir[_MAX_PATH] = { "C:\\Windows" };
int nResult = chdir( strChangeDir );
if( nResult == 0 )
{
printf( "이동 성공" );
}
else if( nResult == -1 )
{
perror( "이동 실패 - " );
}
return 0;
}
ref - https://shaeod.tistory.com/325
stat, lstat, fstat
형태
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *file_name, struct stat *buf);
int fstat(int filedes, struct stat *buf);
int lstat(const char *file_name, struct stat *buf);
설명
stat() 함수를 이용하면 파일의 상태를 알아올수 있다. 첫번째 인자로 주어진 file_name 의 상태를 얻어와서 두번째 인자인 buf 에 채워 넣는다.
lstat() 함수는 심볼릭:::링크(:12)파일의 원본파일의 상태를 얻어온다는 것을 제외하고는 stat() 함수와 동일하다.
fstat() 는 open(2) 등을 통해서 만들어진 파일지시자를 인자로 받아들인다는 점 외에는 stat() 와 동일한 일을 수행한다.
이들 함수는 성공적으로 수행될경우 파일의 정보를 stat구조체에 복사한다. stat구조체는 다음과 같이 정의되어 있다.
struct stat {
dev_t st_dev; /* device */
ino_t st_ino; /* inode */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device type (if inode device) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for filesystem I/O */
blkcnt_t st_blocks; /* number of blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last change */
};
반환값
성공 시 - 0, 실패 시 -1
예제
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
int main(int argc, char **argv)
{
int return_stat;
char *file_name;
struct stat file_info;
struct passwd *my_passwd;
struct group *my_group;
mode_t file_mode;
if (argc != 2 )
{
printf("Usage : ./file_info [file name]\n");
exit(0);
}
file_name = argv[1];
if ((return_stat = stat(file_name, &file_info)) == -1)
{
perror("Error : ");
exit(0);
}
file_mode = file_info.st_mode;
printf("파일이름 : %s\n", file_name);
printf("=======================================\n");
printf("파일 타입 : ");
if (S_ISREG(file_mode))
{
printf("정규파일\n");
}
else if (S_ISLNK(file_mode))
{
printf("심볼릭 링크\n");
}
else if (S_ISDIR(file_mode))
{
printf("디렉토리\n");
}
else if (S_ISCHR(file_mode))
{
printf("문자 디바이스\n");
}
else if (S_ISBLK(file_mode))
{
printf("블럭 디바이스\n");
}
else if (S_ISFIFO(file_mode))
{
printf("FIFO\n");
}
else if (S_ISSOCK(file_mode))
{
printf("소켓\n");
}
my_passwd = getpwuid(file_info.st_uid);
my_group = getgrgid(file_info.st_gid);
printf("OWNER : %s\n", my_passwd->pw_name);
printf("GROUP : %s\n", my_group->gr_name);
printf("FILE SIZE IS : %d\n", file_info.st_size);
printf("마지막 읽은 시간 : %d\n", file_info.st_atime);
printf("마지막 수정 시간 : %d\n", file_info.st_mtime);
printf("하드링크된 파일수 : %d\n", file_info.st_nlink);
}
execve
실행가능한 파일인 filename의 실행코드를 현재 프로세스에 적재하여 기존의 실행코드와 교체하여 새로운 기능으로 실행한다. 즉, 현재 실행되는 프로그램의 기능은 없어지고 filename 프로그램을 메모리에 loading하여 처음부터 실행한다.
UNIX / LINUX에서는 프로세스 생성(fork(2))과 실행할 binary 교체(exec계열함수)는 분리되어 있다.
형태
#include <unistd.h>
int execve(const char *filename, char *const argv[], char *const envp[]);
(사용시 signal 설정이 default로 변경)
execve의 의미
execve는 exec계열이다.
exec 뒤에 붙는 글자의 의미는
l : argv가 list로 나열된다는 의미. 그 것의 끝은 NULL.
v : argv가 vector(배열)로 parameter를 하나를 받는다는 의미. 배열의 마지막값은 NULL
p : 첫번째 파라미터인 명령어/실행파일이 PATH로 지정된 디렉토리에 있다면
full path 또는 상대 path로 하지 않아도 된다는 뜻.
e : 설정할 환경변수를 parameter로 받는다는 의미.
즉, execve의 의미는 ※ execve : execute 프로그램 by argument를 vector(배열)로, environment(환경변수)도 변경
이다.
파라미터
filename
- 교체할 실행 파일 / 명령어.
- filename은 실행가능한 binary이거나 shell이어야 합한다.
- filename은 path가 설정되어 있는 디렉토리라고 하더라도
절대path나 상대path로 정확한 위치를 지정해야 합한다.
argv
- c언어의 main(int argc, char *argv[])에서 argv와 비슷하며, main함수에는 argc가 있지만
execve에는 argc가 없으므로 main의 argv에 마지막 array 다음은 NULL이어야 있는 것과 같다.
envp
- key=value형식의 환경변수 문자열 배열리스트로 마지막은 NULL이어야 합한다.
- 만약 기 설정된 환경변수를 사용하려면 environ 전역변수를 그냥 사용한다.
include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
extern char **environ;
int main(int argc, char *argv[]) {
char **new_argv; char command[] = "ls";
int idx;
new_argv = (char **)malloc(sizeof(char *) * (argc + 1));
/* 명령어를 ls로 변경 */
new_argv[0] = command;
/* command line으로 넘어온 parameter를 그대로 사용 */
for(idx = 1; idx < argc; idx++) {
new_argv[idx] = argv[idx];
}
/* argc를 execve 파라미터에 전달할 수 없기 때문에 NULL이 파라미터의 끝을 의미함 */
new_argv[argc] = NULL;
if(execve("/usr/bin/ls", new_argv, environ) == -1) {
fprintf(stderr, "프로그램 실행 error: %s\n", strerror(errno));
return 1;
}
/* ls 명령어 binary로 실행로직이 교체되었으므로 이후의 로직은 절대 실행되지 않는다. */
printf("이곳이 이제 ls 명령어라 이 라인은 출력이 되지 않습한다.\n");
return 0;
}
// 결과는 ls -al 한 것과 같음
dup, dup2
형식
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
설명
dup()와 dup2()는 파일 지정자 oldfd에 대한 복사본을 생성한다. 성공적으로 수행될경우 oldfd 지정자에 대한 복사본은 서로 공유되어서 사용된다. 즉 lock, 파일위치 포인터, 플래그등을 공유한다. 만약 원본 파일지정자 에서 위치변경이 일어 났다면, 다른 복사된 파일지정자에도 영향을 미친다.
그러나 이 두개의 파일지정자간 close-on-exe 플래그는 공유되지 않는다.
dup()를 이용해서 복사되어지는 새로운 파일 지정자는 사용되지 않는 가장 작은 파일 지정자를 이용한다. dup2()는 디스크립터는 파일 지정자를 지정할 수 있는데, 이전에 열린 newfd가 있다면 닫고 나서, oldfd를 newfd에 복사하면 된다.
리턴값
복사된 새로운 파일지정자를 리턴한다. 에러가 발생하면 -1 을 리턴한다.
예제
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd, fd2;
char buf[80];
fd = open("dup.c", O_RDONLY);
fd2 = dup(fd);
printf("%d = %d\n", fd, fd2);
memset(buf, 0x00, 80);
read(fd, buf, 20);
printf("fd : %d\n%s\n", fd, buf);
memset(buf, 0x00, 80);
read(fd2, buf, 20);
printf("fd : %d\n%s\n", fd2, buf);
close(fd);
}
pipe
형태
#include <unistd.h>
int pipe(int filedes[2]);
설명
pipe 를 이용하면 2개의 파일 지시자를 생성할수 있다. 2개가 생성되는 이유는 읽기전용과 쓰기전용의 파이프를 생성하기 위함이다. filedes[0] 은 읽기 전용, filedes[1] 은 쓰기전용의 파이프로 사용된다.
이들 파이프는 주로 부모프로세스와 자식프로세스간의 통신을 위한 목적으로 사용된다.
반환값
pipe 를 이용하면 2개의 파일 지시자를 생성할수 있다. 2개가 생성되는 이유는 읽기전용과 쓰기전용의 파이프를 생성하기 위함이다. filedes[0] 은 읽기 전용, filedes[1] 은 쓰기전용의 파이프로 사용된다.
이들 파이프는 주로 부모프로세스와 자식프로세스간의 통신을 위한 목적으로 사용된다.
예제
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
int n, fd[2];
char buf[255];
int pid;
if (pipe(fd) < 0)
{
perror("pipe error : ");
exit(0);
}
// 파이프를 생성한다.
if ((pid = fork()) < 0)
{
perror("fork error : ");
exit(0);
}
// 만약 자식프로세스라면 파이프에 자신의 PID(:12) 정보를 쓴다.
else if (pid == 0) {
close(fd[0]);
while(1) {
memset(buf, 0x00, 255);
sprintf(buf, "Hello : %d\n", getpid());
write(fd[1], buf, strlen(buf));
sleep(1);
}
}
// 만약 부모프로세스(:12)라면 파이프(:12)에서 데이타를 읽어들인다.
else {
close(fd[1]);
while(1) {
memset(buf, 0x00, 255);
n = read(fd[0], buf, 255);
fprintf(stderr, "%s", buf);
}
}
}
opendir, readdir, closedir
프로그램을 개발하다보면 특정 디렉토리에 포함된 디렉토리명 또는 파일명을 읽어야 하는 경우가 종종 생긴다. 예를 들면 특정 디렉토리의 하위의 모든 파일 중에서 .bak 파일을 지우는 것 같이. 이를 위해서 UNIX/LINUX에서 제공되는 Library call 함수는 아래와 같이 set로 제공한다. 이들 함수들은 dirent.h에 포함되어 있다.
형태
#include <sys/types.h>
#include <dirent.h>
DIR * opendir(const char *name);
struct dirent *readdir(DIR *dir);
int closedir(DIR *dir);
사용예제
예제 1). 로그인한 user의 home 디렉토리의 파일 및 디렉토리 목록을 출력하기
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc, char **argv)
{
DIR *dir_ptr = NULL;
struct dirent *file = NULL;
char home[1024];
strncpy(home, getenv("HOME"), sizeof(home));
/* 목록을 읽을 디렉토리명으로 DIR *를 return 받습니다. */
if((dir_ptr = opendir(home)) == NULL)
{
fprintf(stderr, "%s directory 정보를 읽을 수 없습니다.\n", home);
return -1;
}
/* 디렉토리의 처음부터 파일 또는 디렉토리명을 순서대로 한개씩 읽습니다. */
while((file = readdir(dir_ptr)) != NULL)
{
/*
* struct dirent *의 구조체에서 d_name 이외에는
* 시스템마다 항목이 없을 수 있으므로 무시하고 이름만 사용합니다.
*/
printf("%s\n", file->d_name);
}
/* open된 directory 정보를 close 합니다. */
closedir(dir_ptr);
return 0;
}
Example 2).
다음 예문은 재귀함수로서 특정 디렉토리 아래의 모든 파일을 뒤져서 파일의 확장자가 .bak이면 삭제
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int remove_all_backup_file(const char *path)
{
DIR * dir_ptr = NULL;
struct dirent *file = NULL;
struct stat buf;
char *ext;
char filename[1024];
/* 목록을 읽을 디렉토리명으로 DIR *를 return 받습니다. */
if((dir_ptr = opendir(path)) == NULL)
{
return -1;
}
/* 디렉토리의 처음부터 파일 또는 디렉토리명을 순서대로 한개씩 읽습니다. */
while((file = readdir(dir_ptr)) != NULL)
{
// readdir 읽혀진 파일명 중에 현재 디렉토리를 나타네는 . 도 포함되어 있으므로
// 무한 반복에 빠지지 않으려면 파일명이 . 이거나 .. 이면 skip 해야 함
if(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)
{
continue;
}
sprintf(filename, "%s/%s", path, file->d_name);
/* 파일의 속성(파일의 유형, 크기, 생성/변경 시간 등을 얻기 위하여 */
if(stat(filename, &buf) == -1)
{
continue;
}
if(S_ISDIR(buf.st_mode)) // 검색된 이름의 속성이 디렉토리이면
{
/*
* 검색된 파일이 directory이면 재귀호출로 하위 디렉토리를 다시 검색
*/
remove_all_backup_file(filename);
}
else if(S_ISREG(buf.st_mode)) // 검색된 이름의 속성이 일반파일이면
{
if((ext = strrchr(filename, '.')) == NULL)
{
continue;
}
if(strcmp(ext, ".bak") == 0) // 파일의 확장자가 .bak 이면 파일을 삭제함
{
unlink(filename);
}
}
}
/* open된 directory 정보를 close 합니다. */
closedir(dir_ptr);
return 0;
}
int main(int argc, char **argv)
{
int idx;
for(idx = 1; idx < argc; idx++) {
printf("%s 디렉토리 하위에 있는 모든 .bak 파일을 지웁니다\n", argv[idx]);
remove_all_backup_file(argv[idx]);
}
}
strerror
#include <string.h> // C++ 에서는 <cstring>
char* strerror(int errnum);
오류 메세지 문자열을 가리키는 포인터를 얻어온다. errnum
의 값을 통해 발생하였던 오류에 알맞은 오류 메세지를 가리키는 포인터를 리턴한다. 이 때 리턴되는 포인터는 문자열 리터럴을 가리키고 있기 때문에 그 내용이 바뀔 수 없다. 참고로 strerror 에 의해 출력되는 오류 메세지는 현재 사용중인 컴파일러나 플랫폼에 따라 다를 수 있다.
인자
errnum
말 그대로 오류번호.
리턴값
오류 번호에 해당하는 오류 문자열을 가리키는 포인터
실행 예제
/*
존재하지 않는 파일 unexist.ent 를 열라고 해 오류가 발생한다.
이 예제는
http://www.cplusplus.com/reference/clibrary/cstring/strerror/
에서 가져왔습니다.
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
int main() {
FILE* pFile;
pFile = fopen("unexist.ent", "r");
if (pFile == NULL)
printf("Error opening file unexist.ent: %s\n", strerror(errno));
return 0;
}
errno
형태
함수 아님.
extern int errno;
아래를 참고.
refs
https://www.joinc.co.kr/w/man/2/fork
https://www.joinc.co.kr/w/man/2/wait
https://www.joinc.co.kr/w/man/2/waitpid
https://www.joinc.co.kr/w/man/2/signal
http://blog.naver.com/PostView.nhn?blogId=bitnang&logNo=70172674474
https://www.joinc.co.kr/w/Site/system_programing/File/dup
https://www.joinc.co.kr/w/man/2/pipe
https://mystyle1057.tistory.com/entry/C언어-강좌-오류처리-errno-strerror-strerrorr
www.geeksforgeeks.org/exit-status-child-process-linux/
'Code 42 > Minishell' 카테고리의 다른 글
Minishell 해석 (0) | 2020.12.21 |
---|
최근댓글