해당 프로젝트는 2인 팀과제로 평균적으로 30일이 소요되는 큰 프로젝트이기 때문에 사용가능한 함수가 상당히 많습니다.
처음 보는 함수들이 굉장히 많아 전부 공부했지만, 작성 결과물을 보면 다 사용한건 아닙니다.
프롬프트를 띄우고 입력값을 받을때 사용한 readline
함수
히스토리를 확인하기 위한 add_history
함수
파일의 상태와 정보를 파악하기 위한 stat
함수
부모프로세스가 자식프로세스를 기다릴때 사용한 wait
함수
시그널 신호를 처리하는 signal
함수
디렉토리를 열고 닫을때 사용하는 opendir
함수
환경변수에서 key값을 가지고 key와 value를 확인할 수 있는 getenv
함수
현재 경로를 리턴해주는 getcwd
함수
현재 디렉토리를 변경해주는 chdir
함수
등등 대표적으로 사용한 함수들이고 이 외에도 함수를 공부하는데 많은 공부가 되었습니다.
#include <errno.h>
#include <readline/readline.h>
char *readline(const char *prompt);
#include <readline/readline.h>
int rl_on_new_line(void);
#include <readline/readline.h>
void rl_replace_line(const char *text, int clear_undo);
#include <readline/readline.h>
void rl_redisplay(void);
#include <readline/readline.h>
int add_history(const char *line);
void add_history(const char *line);
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <unistd.h>
void handler(int signum)
{
printf("Ctrl + C[%d] is not working...\n", signum);
rl_redisplay();
}
int main(void)
{
char *line;
signal(SIGINT, handler);
while (1)
{
line = readline("minishell> ");
if (!line)
break ;
printf("you wrote [%s]\n", line);
add_history(line);
free(line);
}
return (0);
}
#include <sys/stat.h>
#include <unistd.h>
int fstat(int fildes, struct stat *buf);
int lstat(const char *restrict path, struct stat *restrict buf);
int stat(const char *restrict path, struct stat *restrict buf);
//struct status : 파일 정보를 저장하는 구조체
lstat은 지정한 파일이 심볼릭 링크면 링크 파일 자체의 정보를 전달.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <readline/readline.h>
#define DIRECTORY "directory"
#define REG_FILE "regular file"
#define SYM_LINK "symbolic link"
#define WR_MODE "write mode"
#define RD_MODE "read mode"
#define EX_MODE "execution mode"
#define FILE_NAME "Makefile"
void print_info_list(void)
{
printf("================\n");
puts(DIRECTORY);
puts(REG_FILE);
puts(SYM_LINK);
puts(WR_MODE);
puts(RD_MODE);
puts(EX_MODE);
printf("================\n");
}
void print_cmd_info(struct stat *info, char *cmd)
{
if (!strcmp(cmd, DIRECTORY))
printf("%s\n", S_IFDIR & info->st_mode ? "is a directory" : "is NOT a directory");
else if (!strcmp(cmd, REG_FILE))
printf("%s\n", S_IFREG & info->st_mode ? "is a reg file" : "is NOT a reg file");
else if (!strcmp(cmd, SYM_LINK))
printf("%s\n", S_IFLNK & info->st_mode ? "is a symbolic link" : "is NOT a symbolic link");
else if (!strcmp(cmd, WR_MODE))
printf("%s\n", S_IWUSR & info->st_mode ? "is writable" : "is NOT writable");
else if (!strcmp(cmd, RD_MODE))
printf("%s\n", S_IRUSR & info->st_mode ? "is readable" : "is NOT readable");
else if (!strcmp(cmd, EX_MODE))
printf("%s\n", S_IXUSR & info->st_mode ? "is executable" : "is NOT executable");
}
int main(void)
{
int fd;
struct stat buf;
char *cmd;
if ((fd = open(FILE_NAME, O_RDONLY)) < 0)
perror("open failed");
if (fstat(fd, &buf) < 0) //fstat은 file descripter를 받아와야 하지만,
//lstat, stat함수는 파일의 경로만 알면 된다.
perror("fstat failed");
else
{
while (1)
{
print_info_list();
cmd = readline("cmd : ");
print_cmd_info(&buf, cmd);
free(cmd);
}
}
return (0);
}
#include <sys/wait.h>
#include <sys/resource.h>
pid_t wait3(int *stat_loc, int options, struct rusage *rusage);
pid_t wait4(pid_t pid, int *stat_loc, int options, struct rusage *rusage);
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/resource.h>
#define PROCESS_NUM 100
void hello(void)
{
printf("hello\n");
fork();
printf("hello2\n");
sleep(1);
}
int main(void)
{
int i;
pid_t pid;
struct rusage rusage;
int pid_arr[PROCESS_NUM];
i = 0;
while (i < PROCESS_NUM)
{
pid_arr[i] = fork();
if (pid_arr[i] == 0)
{
hello();
exit(0);
}
printf("waiting....\n");
wait4(pid_arr[i], 0, 0, &rusage);
printf("voluntary context switches : %ld\n", rusage.ru_nvcsw);
printf("involuntary context switches : %ld\n", rusage.ru_nivcsw);
i++;
}
return (0);
}
#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);
//struct가 있으면 ->
typedef void (*sig_t) (int); //반환값이 void형이고, 매개변수가 int형인 함수 포인터의 별명을 sig_t
sig_t signal(int sig, sig_t func);
번호 | 시그널 | 기본처리 | 발생조건 |
---|---|---|---|
1 | SIGHUP | 종료 | HangUP, 터미널에서 접속이 끊겼을 때 보내진다. |
2 | SIGINT | 종료 | 키보드로 Ctrl + c |
9 | SIGKILL | 종료 | 강제 종료시 |
11 | SIGSEGV | 코어 덤프 | segfault가 생겼을 때 |
12 | SIGSYS | 코어 덤프 | system call을 잘못 했을 때 |
16 | SIGUSR1 | 종료 | 사용자 정의 시그널1 |
17 | SIGUSR2 | 종료 | 사용자 정의 시그널2 |
23 | SIGSTOP | 중지 | 이 시그널을 받으면 SIGCONT 시그널을 받을 때까지 프로세스를 중지 |
24 | SIGTSTP | 중지 | 키보드로 Ctrl + z |
25 | SIGCONT | 무시 | 중지된 프로세스 |
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <signal.h>
void (*old_handler)(int);
void signal_handler(int sig)
{
printf("you pressed Ctrl + c(%d)\n", sig);
printf("if you press Ctrl + c again, then this program ends\n");
printf("old_handler : %p\n", old_handler);
signal(SIGINT, 0);
}
int main(void)
{
old_handler = signal(SIGINT, signal_handler);
printf("old_handler : %p\n", old_handler);
while (1)
{
printf("waiting...\n");
sleep(1);
}
return (0);
}
old_handler : 0x0 //전에 설정된 시그널 핸들러가 없기 때문에 NULL이다.
waiting...
waiting...
waiting...
waiting...
^Cyou pressed Ctrl + c(2) //ctrl + c를 눌렀을 때 signal_handler함수로 가기 때문에 출력된다.
if you press Ctrl + c again, then this program ends
old_handler : 0x0
waiting...
waiting...
^C //시그널 핸들러를 0로 재설정했기 때문에 기본 처리였던 종료를 한다.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <signal.h>
void (*old_handler)(int);
void signal_handler(int sig)
{
printf("i detected signal SIGUSR1(%d)\n", sig);
exit(0);
}
void child_process(void)
{
old_handler = signal(SIGUSR1, signal_handler);
while (1)
{
printf("child process....\n");
sleep(1);
}
}
int main(void)
{
pid_t pid;
pid = fork();
if (pid == 0)
{
child_process();
exit(0);
}
sleep(2);
kill(pid, SIGUSR1);
return (0);
}
child process....
child process....
child process....
i detected signal SIGUSR1(30) //부모 프로세스에서 SIGUSR1 시그널을 보내서 자식이 종료됐다.
#include <signal.h>
struct sigaction
{
void (*sa_handler)(int); //시그널을 처리하기 위한 핸들러
//SIG_DFS, SIG_IGN 또는 핸들러 함수
void (*sa_sigaction)(int, siginfo_t *, void *); //밑의 sa_flags가 SA_SIGINFO일 때
//sa_handler 대신에 동작하는 핸들러
sigset_t sa_mask; //시그널을 처리하는 동안 블록화할 시그널 집합의 마스크
int sa_flags; //아래 설명 참고
void (*sa_restorer)(void); //사용해서는 안 된다.
}
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct sigaction act_new;
struct sigaction act_old;
void sigint_handler(int signum)
{
printf("you pressed Ctrl + C[%d]\n", signum);
printf("again -> exit");
sigaction(SIGINT, &act_old, NULL);
}
int main(void)
{
act_new.__sigaction_u.__sa_handler = sigint_handler; //시그널 핸들러 지정
sigemptyset(&act_new.sa_mask); //시그널 처리 중 블록될 시그널 없다.
//SIGINT를 지정하면서 act_old에 이전 정보를 구한다.
sigaction(SIGINT, &act_new, &act_old);
while (1)
{
printf("hello\n");
sleep(1);
}
return (0);
}
#include <readline/readline.h>
void rl_clear_history(void);
#include <signal.h>
int sigemptyset(sigset_t *set);
#include <signal.h>
int sigaddset(sigset_t *set, int signum);
#include <dirent.h>
DIR *opendir(const char *filename);
struct dirent *readdir(DIR *dirp);
int closedir(DIR *dirp);
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <signal.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[])
{
DIR *dir_stream;
struct dirent *dir_info;
if ((dir_stream = opendir(argv[1])) == NULL) //argv[1]로 된 디렉토리 스트림을 연다.
{
perror("opendir error");
return (1);
}
errno = 0; //readdir이 실패인지 끝까지 다 읽었는지 확인하기 위해 errno를 0으로 만든다.
while ((dir_info = readdir(dir_stream)) != NULL) //readdir이 끝까지 또는 실패일 때까지 읽는다.
printf("found file : %s\n", dir_info->d_name); //디렉토리에 있는 파일들의 이름들을 출력한다.
if (errno != 0) //errno가 바뀌었다면 -> readdir이 오류라는 뜻이므로 오류를 출력한다.
perror("readdir error");
closedir(dir_stream); //디렉토리 스트림을 닫는다.
return (0);
}
./a.out mandatory //mandatory라는 디렉토리에 있는 파일들을 출력한다.
found file : .
found file : ..
found file : a.out
found file : main.c
#include <termios.h>
int tcsetattr(int fildes, int optional_actions,
const struct termios *termios_p);
int tcgetattr(int fildes, struct termios *termios_p);
TCSNOW : 속성(termios_p)를 바로 변경한다.
TCSADRAIN : 현재 출력이 완료됐을 때 값을 변경한다.(fd로 써진 출력값이 터미널로 다 보내진 후에 변경된다.) This value of optional_actions should be used when changing parameters that affect output
TSCAFLUSH : 현재 출력이 완료됐을 때 값을 변경한다. 하지만 현재 읽을 수 있으며, read 호출에서 아직 반환되지 않은 입력값은 버린다.
termios_p : 터미널 속성을 설정할 포인터
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <signal.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <termios.h>
#define FILE_NAME "Makefile"
struct termios org_term;
struct termios new_term;
// org_term에 초기 터미널 세팅 저장
void save_input_mode(void)
{
tcgetattr(STDIN_FILENO, &org_term); //STDIN으로부터 터미널 속성을 받아온다.
}
//new_term에 원하는 터미널 속성 설정
void set_input_mode(void)
{
tcgetattr(STDIN_FILENO, &new_term); //STDIN으로부터 터미널 속성을 받아온다.
new_term.c_lflag &= ~(ICANON | ECHO); //ICANON, ECHO 속성을 off
new_term.c_cc[VMIN] = 1;
new_term.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &new_term);
}
//기존 터미널 세팅으로 다시 변경
void reset_input_mode(void)
{
tcsetattr(STDIN_FILENO, TCSANOW, &org_term);
}
int main(void)
{
int ch = 0;
save_input_mode(); //터미널 세팅 저장
set_input_mode(); //터미널 세팅 변경
while (read(STDIN_FILENO, &ch, sizeof(char)) > 0)
{
if (ch == 4/* ctrl + d */)
break ;
else
write(0, &ch, sizeof(char));
ch = 0;
}
reset_input_mode(); //터미널 세팅 초기화
return (0);
}
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <signal.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <termios.h>
#include <string.h>
#define FILE_NAME "Makefile"
int main(void)
{
struct termios newtio;
int ttyfd;
char *ttyname;
ttyname = "/dev/ttys000";
ttyfd = open(ttyname, O_RDWR | O_NOCTTY);
if (ttyfd < 0)
return (perror("open failed"), 1);
memset(&newtio, 0, sizeof(newtio));
newtio.c_cflag = B115200 | CS8 | CLOCAL | CREAD | CRTSCTS;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
//set input mode(non-cannonical, no echo.......)
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
tcflush(ttyfd, TCIFLUSH);
tcsetattr(ttyfd, TCSANOW, &newtio);
printf("## tty01 Opened [%s]\r\n", ttyname);
close(ttyfd);
return (0);
}
//실행을 하면 /dev/ttys000에 해당하는 터미널의 환경이 바뀐다.
#include <sys/ioctl.h>
int ioctl(int fildes, unsigned long request, ...);
#include <stdlib.h>
char *getenv(const char *name);
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int i;
i = 1;
while (argv[i])
{
printf("%s=%s\n", argv[i], getenv(argv[i]));
i++;
}
return (0);
}
//./a.out PATH PWD OLDPWD
PATH=/Users/taeypark/.brew/bin:/Users/taeypark/.brew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/munki
PWD=/Users/taeypark/Desktop/42/minishell
OLDPWD=/Users/taeypark/Desktop/42/minishell
#include <unistd.h>
char *getcwd(char *buf, size_t size);
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *cwd; //current working directory
char buf[50];
cwd = 0;
if ((cwd = getcwd(buf, 5)) == NULL)
perror("getcwd error");
printf("cwd : %s\n", cwd);
if ((cwd = getcwd(buf, 37)) == NULL)
perror("getcwd error");
printf("cwd : %s\n", cwd);
if ((cwd = getcwd(NULL, 37)) == NULL)
perror("getcwd error");
printf("cwd : %s\n", cwd);
free(cwd);
return (0);
}
getcwd error: Result too large //buf에 담을 값이 size보다 크기 때문에 error가 난다.
cwd : (null)
cwd : /Users/taeypark/Desktop/42/minishell //37(널문자 포함)이상이면 현재 경로를 담을 수 있다.
cwd : /Users/taeypark/Desktop/42/minishell //자동으로 할당을 해준다. free 필수
#include <unistd.h>
int chdir(const char *path);
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/dir.h>
void print_all_files(char *cwd)
{
DIR *dir_stream;
struct dirent *dir_info;
dir_stream = opendir(cwd);
while ((dir_info = readdir(dir_stream)) != NULL)
printf("found file : %s\n", dir_info->d_name);
closedir(dir_stream);
}
int main(int argc, char *argv[])
{
char *cwd;
cwd = getcwd(NULL, 0);
printf("cwd : %s\n", cwd);
print_all_files(cwd);
free(cwd);
printf("=== after changing dir ===\n");
if (chdir("/tmp") == 0)
{
cwd = getcwd(NULL, 0);
printf("cwd : %s\n", cwd);
print_all_files(cwd);
free(cwd);
}
return (0);
}
cwd : /Users/taeypark/Desktop/42/minishell
found file : .
found file : ..
found file : .git
found file : .vscode
found file : a.out
found file : bonus
found file : main.c
found file : Makefile
found file : mandatory
found file : minishell
found file : objs
=== after changing dir ===
cwd : /private/tmp
found file : .
found file : ..
found file : loose
found file : com.google.Keystone
found file : .ifstats
found file : com.apple.launchd.J2t54VqUBA
found file : powerlog
found file : pymp-m31741c6
found file : munki_swupd_cache
found file : last_fs_sync
#include <unistd.h>
int isatty(int fd);
char *ttyname(int fd);
#include <stdio.h>
#include <string.h>
int main(void)
{
char *ret, tty[40];
printf("%s\n", isatty(STDIN_FILENO) ? "stdin is a tty" : "stdin is not a tty");
if ((ret = ttyname(STDOUT_FILENO)) == NULL)
perror("ttyname() error");
else
{
strcpy(tty, ret); //ret는 바뀔 수가 있기 때문에 안전하게 복사본을 만든다.
printf("the ttyname associated with my stdin is %s\n", ret);
}
return (0);
}
#include <unistd.h>
int ttyslot(void);