Skip to content

리눅스 시스템 프로그래밍 프로젝트

  • 선택과목으로 리눅스 시스템 프로그래밍을 수강하며 진행한 프로젝트 코드 및 배운점에 대해 정리한다.
  • 이 프로젝트에서 개발하는 Application은 아래의 기능을 포함한다.
  1. Text File(swblocks.txt)에 기록된 S/W 블록 정보로부터 여러 S/W 블록들을 초기화시킨다.
    • 기록된 파일에는 파일 이름과 파라미터가 아래와 같이 세미콜론으로 구분되어 있다.
      SwBlock1; Param1; Param2; Param3
      SwBlock2; Param1; Param2
      SwBlock3; Param1
  2. S/W 블록의 이상동작(블럭 다운) 발생 시 재초기화를 수행한다.
    • 즉, 해당 블록에 해당하는 프로세스를 재시작한다.
  3. 각 S/W 블록의 최종 초기화 시간 및 재초기화 횟수를 출력한다.
  • 프로젝트를 동작시켰을 때 출력되는 결과이다.
image

코드 설명

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);
}

전체 코드

https://github.com/rlaisqls/Linux-Study/tree/main/project

#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