Skip to content

Latest commit

 

History

History
365 lines (298 loc) · 10.3 KB

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

File metadata and controls

365 lines (298 loc) · 10.3 KB
  • 선택과목으로 리눅스 시스템 프로그래밍을 수강하며 진행한 프로젝트 코드 및 배운점에 대해 정리한다.
  • 이 프로젝트에서 개발하는 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