티스토리 뷰

첫 글에서 운영체제의 핵심적인 부분을 모두 끝내서 뭔가 운영체제 카테고리로 이 글을 이어가는 게 민망하긴 하지만 끝내긴 해야 하니까... 코드의 기본 틀은 아래와 같다.

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

int main(int argc, char *argv[])
{
    int cmd = 0;
    int p[2]; // p[0]: read, p[1]: write

    pipe(p);            // 파이프를 생성한다.
    pid_t pid = fork(); // child를 생성한다.

    if (pid < 0)
    { // 에러
        printf("[CLIENT] Fork failed.\n");
        return 1;
    }
    else if (pid == 0)
    { // Child (Server)
        while (1)
        {
            read(p[0], &cmd, sizeof(cmd)); // 파이프로 전송된 값을 받는다.
            switch (cmd)
            {
            case 1:
                break;
            case 2:
                break;
            default:
                printf("[SERVER] %d is undefined option.\n", cmd);
            }
        }
        return 0;
    }
    else
    { // Parent (Client)
        while (1)
        {
            printf("[1] search [2] create\n");
            printf("> ");
            scanf("%d", &cmd);

            write(p[1], &cmd, sizeof(cmd)); // 파이프를 통해 값을 전송한다.

            switch (cmd)
            {
            case 1:
                break;
            case 2:
                break;
            default:
            }
        }
        wait(NULL); // child를 기다린다.
    }
    return 0;
}

사용자가 입력한 cmd 값에 따라 분기를 설정했다. 입력과 출력을 반복하며 client와 server가 계속 정보를 주고 받는다.

Search

먼저 검색부터 구현해보자.

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>

int main(int argc, char *argv[])
{
    int cmd = 0;
    char id[256];
    char name[256];
    char result[256];
    int p[2]; // p[0]: read, p[1]: write

    pipe(p);            // 파이프를 생성한다.
    pid_t pid = fork(); // child를 생성한다.

    if (pid < 0)
    { // 에러
        printf("[CLIENT] Fork failed.\n");
        return 1;
    }
    else if (pid == 0)
    { // Child (Server)
        FILE *file;

        while (1)
        {
            read(p[0], &cmd, sizeof(cmd)); // 파이프로 전송된 값을 받는다.
            switch (cmd)
            {
            case 1: // Search
                read(p[0], id, sizeof(id));
                file = fopen("student.data", "r");

                strncpy(result, "Failed", 256);

                while (!feof(file))
                { // 데이터를 검색한다.
                    fscanf(file, "%s\t%[^\n]s\n", resId, resName);
                    if (strcmp(id, resId) == 0)
                    { // 발견!
                        printf("[SERVER] Search succeeded.\n");
                        strncpy(result, resName, sizeof(resName));
                        break;
                    }
                }

                write(p[WRITE_END], result, sizeof(result));

                fclose(file);
                break;
            case 2:
                break;
            default:
                printf("[SERVER] %d is undefined option.\n", cmd);
                write(p[0], "Failed", 256);
            }
            usleep(100);
        }
        return 0;
    }
    else
    { // Parent (Client)
        while (1)
        {
            usleep(100);
            printf("--------------------\n");
            printf("[1] search [2] create\n");
            printf("> ");
            scanf("%d", &cmd);
            printf("--------------------\n");

            switch (cmd)
            {
            case 1: // Search
                printf("id > ");
                scanf("%s", id);
                printf("--------------------\n");

                write(p[1], &cmd, sizeof(cmd));
                write(p[1], id, sizeof(id));

                break;
            case 2:
                break;
            default:
                write(p[1], &cmd, sizeof(cmd));
            }

            usleep(100);
            read(p[0], result, sizeof(result));
            printf("[CLIENT] Operation completed: %s\n", result);
        }
        wait(NULL); // child를 기다린다.
    }
    return 0;
}
$ ./debug
[1] search [2] create
> 1
--------------------
id > 4885
--------------------
[SERVER] Search succeeded.
[CLIENT] Operation completed: Park
--------------------
[1] search [2] create
>

내용은 정말 기본적인 파일 입출력 내용이다. usleep(100)은 parent와 child의 값 동기화를 위해 0.1초를 기다리는 코드다. 로직을 잘 짜서 parent와 child의 송수신이 맞아 떨어지게 된다면 usleep()을 쓰지 않고 구현할 수도 있다.

Add

다음은 데이터 추가를 구현해보자.

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>

int main(int argc, char *argv[])
{
    int cmd = 0;
    char id[256];
    char name[256];
    char result[256];
    int p[2]; // p[0]: read, p[1]: write

    pipe(p);            // 파이프를 생성한다.
    pid_t pid = fork(); // child를 생성한다.

    if (pid < 0)
    { // 에러
        printf("[CLIENT] Fork failed.\n");
        return 1;
    }
    else if (pid == 0)
    { // Child (Server)
        FILE *file;

        while (1)
        {
            read(p[0], &cmd, sizeof(cmd)); // 파이프로 전송된 값을 받는다.
            switch (cmd)
            {
            case 1: // Search
                read(p[0], id, sizeof(id));
                file = fopen("student.data", "r");

                strncpy(result, "Failed", 256);

                while (!feof(file))
                { // 데이터를 검색한다.
                    fscanf(file, "%s\t%[^\n]s\n", resId, resName);
                    if (strcmp(id, resId) == 0)
                    { // 발견!
                        printf("[SERVER] Search succeeded.\n");
                        strncpy(result, resName, sizeof(resName));
                        break;
                    }
                }

                write(p[0], result, sizeof(result));

                fclose(file);
                break;
            case 2: // Add
                read(p[0], id, sizeof(id));
                read(p[0], name, sizeof(name));
                file = fopen("student.data", "a");

                strncpy(result, name, sizeof(name));

                fprintf(file, "%s\t%s\n", id, name);
                write(p[1], id, sizeof(id));

                fclose(file);
                break;
            default:
                printf("[SERVER] %d is undefined option.\n", cmd);
                write(p[0], "Failed", 256);
            }
            usleep(100);
        }
        return 0;
    }
    else
    { // Parent (Client)
        while (1)
        {
            usleep(100);
            printf("--------------------\n");
            printf("[1] search [2] create\n");
            printf("> ");
            scanf("%d", &cmd);
            printf("--------------------\n");

            switch (cmd)
            {
            case 1: // Search
                printf("id > ");
                scanf("%s", id);
                printf("--------------------\n");

                write(p[1], &cmd, sizeof(cmd));
                write(p[1], id, sizeof(id));

                break;
            case 2: // Add
                printf("id > ");
                scanf("%s", id);
                printf("name > ");
                scanf("%[^\n]s", name);
                printf("--------------------\n");

                write(p[1], &cmd, sizeof(cmd));
                write(p[1], id, sizeof(id));
                write(p[1], name, sizeof(name));

                break;
            default:
                write(p[1], &cmd, sizeof(cmd));
            }

            usleep(100);
            read(p[0], result, sizeof(result));
            printf("[CLIENT] Operation completed: %s\n", result);
        }
        wait(NULL); // child를 기다린다.
    }
    return 0;
}
$ ./debug
--------------------
[1] search [2] create
> 2
--------------------
id > 2580
name > Kim
--------------------
[CLIENT] Operation completed: Kim
--------------------
[1] search [2] create
>

이 정도로 완성됐다. 정말... 별 거 없다. 운영체제 관련된 부분으로 신경써야 할 점은 데이터의 동기화 문제뿐이고, 대부분의 시간은 파일 입출력에 썼다. 구현 난이도 자체는 높지 않지만, 프로세스 사이의 시차를 어떻게 맞출 것인가 더 고민해서 usleep()을 안 쓰는 방향으로 로직을 만든다면 좀 더 어려워질 것 같다.

최종 코드

아래 코드는 수정 기능까지 구현하고 리팩토링한 코드다.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

#define READ_END 0
#define WRITE_END 1

#define MAXLEN 255
#define MAXSTU 255
#define FAIL_MSG "Failed"
#define WAIT_TIME 100

void printClientMenu(int *cmd);
void printClientSearch(char *id);
void printClientAdd(char *id, char *name);
void openServerFile(FILE **file, char *mode);
const char *findData(FILE *file, char *id);
void updateData(FILE *file, char *id);

int main(int argc, char *argv[])
{
    int cmd;
    char id[MAXLEN];   // Student ID number
    char name[MAXLEN]; // Student name
    char result[MAXLEN];
    int p[2]; // Pipes

    // Create the pipes.
    printf("[CLIENT] Create pipes.\n");
    pipe(p);

    // Fork a child process.
    printf("[CLIENT] Execute fork.\n");
    pid_t pid = fork();

    if (pid < 0)
    { // Fork error occured.
        printf("[CLIENT] Fork failed.\n");
        return 1;
    }
    else if (pid == 0)
    { // Child (Server)
        FILE *file;

        printf("[SERVER] Child created.\n");

        while (1)
        {
            read(p[READ_END], &cmd, sizeof(cmd));

            switch (cmd)
            {
            case 1: // Search
                // Recieve id from client.
                read(p[READ_END], id, sizeof(id));
                printf("[SERVER] Request recieved: (%d, %s)\n", cmd, id);

                openServerFile(&file, "r");
                strncpy(result, findData(file, id), MAXLEN); // Search the data.

                // Print logs
                if (strcmp(result, FAIL_MSG) != 0)
                { // Search succeeded.
                    printf("[SERVER] Response: (%d, %s, %s)\n", cmd, id, result);
                }
                else
                {
                    printf("[SERVER] Response: The student %s does not exist.\n", id);
                }

                write(p[WRITE_END], result, sizeof(result)); // Send name or failure message to client.

                fclose(file);
                break;
            case 2: // Add
                // Recieve id and name from the client.
                read(p[READ_END], id, sizeof(id));
                read(p[READ_END], name, sizeof(name));
                printf("[SERVER] Request recieved: (%d, %s, %s)\n", cmd, id, name);

                openServerFile(&file, "r");
                strncpy(result, findData(file, id), MAXLEN); // Search the data.

                if (strcmp(result, FAIL_MSG) != 0)
                {                         // There is same id.
                    updateData(file, id); // Update current student data.
                }

                // Write id and name on the file.
                openServerFile(&file, "a");
                fprintf(file, "%s\t%s\n", id, name);
                printf("[SERVER] Write on file: Succeeded.\n");

                write(p[WRITE_END], id, sizeof(id)); // Send id as response data to client.

                fclose(file);
                break;
            default:
                printf("[SERVER] %d is undefined option.\n", cmd);
                write(p[WRITE_END], FAIL_MSG, sizeof(FAIL_MSG));
            }
            usleep(WAIT_TIME);
        }
        exit(0);
    }
    else
    { // Parent (Client)
        while (1)
        {
            usleep(WAIT_TIME);
            printClientMenu(&cmd);

            switch (cmd)
            {
            case 1: // Search
                printClientSearch(id);

                // Send data to server.
                printf("[CLIENT] Send request: (%d, %s)\n", cmd, id);
                write(p[WRITE_END], &cmd, sizeof(cmd));
                write(p[WRITE_END], id, sizeof(id));

                break;
            case 2: // Add
                printClientAdd(id, name);

                // Send data to server.
                printf("[CLIENT] Send request: (%d, %s, %s)\n", cmd, id, name);
                write(p[WRITE_END], &cmd, sizeof(cmd));
                write(p[WRITE_END], id, sizeof(id));
                write(p[WRITE_END], name, sizeof(name));

                break;
            default:
                write(p[WRITE_END], &cmd, sizeof(cmd));
            }

            usleep(WAIT_TIME);
            read(p[READ_END], result, sizeof(result));
            printf("[CLIENT] Operation completed: %s\n", result);
        }
        wait(NULL);
    }
    return 0;
}

// Print a main menu that showed on client.
void printClientMenu(int *cmd)
{
    printf("[CLIENT] Print menu.\n");
    printf("--------------------\n");

    printf("[1] search\n");
    printf("[2] add\n");
    printf("> ");
    scanf("%d", cmd);

    printf("--------------------\n");
}

// Print a search menu that showed on client.
void printClientSearch(char *id)
{
    printf("id > ");
    scanf("%s", id);

    printf("--------------------\n");
}

// Print a add menu that showed on client.
void printClientAdd(char *id, char *name)
{
    printf("id > ");
    scanf("%s", id);

    getchar();

    printf("name > ");
    scanf("%[^\n]s", name);

    printf("--------------------\n");
}

// Open the student data file and print logs.
void openServerFile(FILE **file, char *mode)
{
    char *path = "student.data";

    *file = fopen(path, mode);

    if (*file == NULL)
    { // Error occured.
        printf("[SERVER] Open file: Failed.\n");
        exit(0);
    }
    else
    {
        printf("[SERVER] Open file in '%s' mode: Succeeded.\n", mode);
    }
}

// Find the student name that matches the id.
const char *findData(FILE *file, char *id)
{
    char resId[MAXLEN];
    static char resName[MAXLEN];

    rewind(file);
    while (!feof(file))
    {
        fscanf(file, "%s\t%[^\n]s\n", resId, resName);
        if (strcmp(id, resId) == 0)
        { // Gotcha!
            printf("[SERVER] Search succeeded.\n");
            return resName;
        }
    }

    return FAIL_MSG;
}

// Update old data.
void updateData(FILE *file, char *id)
{
    char resId[MAXSTU][MAXLEN];
    char resName[MAXSTU][MAXLEN];
    char newId[MAXSTU][MAXLEN];
    char newName[MAXSTU][MAXLEN];
    FILE *newFile;
    int i = 0;
    int j = 0;

    // Copy all of data except the id matched data.
    rewind(file);
    for (i = 0, j = 0; !feof(file); i++)
    {
        fscanf(file, "%s\t%[^\n]s\n", resId[i], resName[i]);

        if (strcmp(resId[i], id) != 0)
        {
            strncpy(newId[j], resId[i], sizeof(resId[i]));
            strncpy(newName[j], resName[i], sizeof(resName[i]));
            j++;
        }
    }

    // Write a new file includes the copied data.
    openServerFile(&newFile, "w");
    for (i = 0; i < j; i++)
    {
        fprintf(newFile, "%s\t%s\n", newId[i], newName[i]);
    }

    fclose(newFile);
}
댓글
댓글쓰기 폼