- 선택과목으로 리눅스 시스템 프로그래밍을 수강하며 진행한 프로젝트 코드 및 배운점에 대해 정리한다.
- 이 프로젝트에서 개발하는 Application은 아래의 기능을 포함한다.
- Text File(
swblocks.txt
)에 기록된 S/W 블록 정보로부터 여러 S/W 블록들을 초기화시킨다.- 기록된 파일에는 파일 이름과 파라미터가 아래와 같이 세미콜론으로 구분되어 있다.
SwBlock1; Param1; Param2; Param3SwBlock2; Param1; Param2SwBlock3; Param1
- 기록된 파일에는 파일 이름과 파라미터가 아래와 같이 세미콜론으로 구분되어 있다.
- S/W 블록의 이상동작(블럭 다운) 발생 시 재초기화를 수행한다.
- 즉, 해당 블록에 해당하는 프로세스를 재시작한다.
- 각 S/W 블록의 최종 초기화 시간 및 재초기화 횟수를 출력한다.
- 프로젝트를 동작시켰을 때 출력되는 결과이다.
코드 설명
S/W 블록 정보 저장
- S/W 블록 정보를 저장하고 있는 파일로부터 S/W 블록 저장용 구조체에 저장한다. SwBlock구조체에 대한 배열을 정의한다.
typedef struct { pid_t pid; time_t lastRestartTime; int restartCount; char name[20]; char parameters[MAX_PARAMETERS][20]; char reason[50];} SwBlock;
SwBlock blocks[MAX_SW_BLOCKS];
S/W 블록 정보 획득
- 파일로부터 S/W 블록 정보를 획득한다.
- 파일에 기록된 한 줄 마다 S/W 블록 정보가 기술되어 있고, S/W 블록 정보에 대한 S/W Name, Argument는 “;”로 구분되어 있으므로, “;”를 기준으로 문자열을 분리한 후 공백을 삭제하여 배열에 저장한다.
void trimStr(char *str) { char *start = str; char *end = str + (strlen(str) - 1); while (isspace(*start)) start++; while (isspace(*end)) end--; memmove(str, start, end - start + 1); str[end - start + 1] = '\0';}
int readSwBlocks(FILE *file) { char buf[256]; int index = 0;
while (index < MAX_SW_BLOCKS && fgets(buf, sizeof(buf), file)) { char* token = strtok(buf, ";"); trimStr(token); strcpy(blocks[index].name, token); strcpy(blocks[index].reason, "Init"); blocks[index].lastRestartTime = time(NULL);
for (int paramIndex = 0; paramIndex < MAX_PARAMETERS; paramIndex++) { token = strtok(NULL, ";"); if (token) { trimStr(token); strcpy(blocks[index].parameters[paramIndex], token); } } index++; }
return index;}
S/W 블록 초기화
-
배열에 저장한 block을 순회하며 각 block에 해당하는 자식 프로세스를 생성한다.
int main() { ... for (int i = 0; i < swBlockCount; i++) { runSwBlock(&blocks[i]); sleep(1); } ...}
void runSwBlock(SwBlock *block) { pid_t pid = fork();
if (pid == 0) { srand(time(NULL)); sleep(rand() % 5);
if ((rand() % 2) == 0) { kill(getpid(), SIGTERM); } else { exit(0); } } else { printLog(block); block->pid = pid; }}
S/W 블록 재초기화
SIGCHLD
시스템콜에 대한 sigaction으로 핸들러를 등록하여, 종료된 자식 프로세스가 있는 경우에 해당 프로세스에 대한 블록 정보를 찾는다.WIFEXITED
,WIFSIGNALED
매크로를 통해 사유를 알아낸다.- 해당 블록의 재시작 횟수, 시점, 사유 등을 로그파일에 기록하고 블록을 재시작한다.
void initSigaction() { struct sigaction sa; sa.sa_handler = signalHandler; sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sa, 0) == -1) { printf("sigaction"); exit(1); }}
void signalHandler(int signum) { int status; pid_t pid;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { SwBlock *block = NULL;
for (int i = 0; i < swBlockCount; i++) { if (blocks[i].pid == pid) { block = &blocks[i]; break; } }
if (block) { block->restartCount++; block->lastRestartTime = time(NULL);
if (WIFEXITED(status)) { snprintf(block->reason, sizeof(block->reason), "Exit(%d)", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { int chstatus = WTERMSIG(status); snprintf(block->reason, sizeof(block->reason), "Signal(%s)", strsignal(chstatus)); } else { snprintf(block->reason, sizeof(block->reason), "Unknown"); }
runSwBlock(block); } else { printf("자식 프로세스 %d 종료됨.\n", pid); } }}
S/W 블록 기동 정보 조회
- 로그에 정보를 기록하고 출력하는 역할을 하는 함수를 만들어 기동시 호출한다.
void initLog() { FILE *log = fopen("log.txt", "a"); fprintf(log, "SW Block Name | Restart cnt | Start Time | Reason\n"); fprintf(log, "========================================================================================\n"); printf("SW Block Name | Restart cnt | Start Time | Reason\n"); printf("========================================================================================\n"); fclose(log);}
void printLog(SwBlock* block) { FILE *log = fopen("log.txt", "a"); char timeString[80]; struct tm* timeInfo = localtime(&block->lastRestartTime); strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", timeInfo); fprintf(log, "%s %d %s %s\n", block->name, block->restartCount, timeString, block->reason); printf("%s %d %s %s\n", block->name, block->restartCount, timeString, block->reason); fclose(log);}
전체 코드
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <unistd.h>#include <signal.h>#include <sys/wait.h>#include <time.h>#include <string.h>#include <ctype.h>#include <stdlib.h>
#define MAX_SW_BLOCKS 10#define MAX_PARAMETERS 3
typedef struct { pid_t pid; time_t lastRestartTime; int restartCount; char name[20]; char parameters[MAX_PARAMETERS][20]; char reason[50];} SwBlock;
SwBlock blocks[MAX_SW_BLOCKS];int swBlockCount;
void initLog() { FILE *log = fopen("log.txt", "a"); fprintf(log, "SW Block Name | Restart cnt | Start Time | Reason\n"); fprintf(log, "========================================================================================\n"); printf("SW Block Name | Restart cnt | Start Time | Reason\n"); printf("========================================================================================\n"); fclose(log);}
void printLog(SwBlock* block) { FILE *log = fopen("log.txt", "a"); char timeString[80]; struct tm* timeInfo = localtime(&block->lastRestartTime); strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", timeInfo); fprintf(log, "%s %d %s %s\n", block->name, block->restartCount, timeString, block->reason); printf("%s %d %s %s\n", block->name, block->restartCount, timeString, block->reason); fclose(log);}
void runSwBlock(SwBlock *block) { pid_t pid = fork();
if (pid == 0) { srand(time(NULL)); sleep(rand() % 5);
if ((rand() % 2) == 0) { kill(getpid(), SIGTERM); } else { exit(0); } } else { printLog(block); block->pid = pid; }}
void signalHandler(int signum) { int status; pid_t pid;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { SwBlock *block = NULL;
for (int i = 0; i < swBlockCount; i++) { if (blocks[i].pid == pid) { block = &blocks[i]; break; } }
if (block) { block->restartCount++; block->lastRestartTime = time(NULL);
if (WIFEXITED(status)) { snprintf(block->reason, sizeof(block->reason), "Exit(%d)", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { int chstatus = WTERMSIG(status); snprintf(block->reason, sizeof(block->reason), "Signal(%s)", strsignal(chstatus)); } else { snprintf(block->reason, sizeof(block->reason), "Unknown"); }
runSwBlock(block); } else { printf("자식 프로세스 %d 종료됨.\n", pid); } }}
void initSigaction() { struct sigaction sa; sa.sa_handler = signalHandler; sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sa, 0) == -1) { printf("sigaction"); exit(1); }}
void trimStr(char *str) { char *start = str; char *end = str + (strlen(str) - 1); while (isspace(*start)) start++; while (isspace(*end)) end--; memmove(str, start, end - start + 1); str[end - start + 1] = '\0';}
int readSwBlocks(FILE *file) { char buf[256]; int index = 0;
while (index < MAX_SW_BLOCKS && fgets(buf, sizeof(buf), file)) { char* token = strtok(buf, ";"); trimStr(token); strcpy(blocks[index].name, token); strcpy(blocks[index].reason, "Init"); blocks[index].lastRestartTime = time(NULL);
for (int paramIndex = 0; paramIndex < MAX_PARAMETERS; paramIndex++) { token = strtok(NULL, ";"); if (token) { trimStr(token); strcpy(blocks[index].parameters[paramIndex], token); } } index++; }
return index;}
int main() { srand(time(NULL)); initLog();
FILE *fileList = fopen("swblocks.txt", "r"); swBlockCount = readSwBlocks(fileList); fclose(fileList);
initSigaction(); for (int i = 0; i < swBlockCount; i++) { runSwBlock(&blocks[i]); sleep(1); }
while (1) sleep(1);}
관련 TIL
관련글systemcallrlimitsystemcallthread 관련 systemcallsystemcallwait과 waitpidlinuxkprobe와 kretprobelinuxLinux PackagelinuxLinux 배포판