프로그래밍/etc

[Ubuntu] systemd service 등록하기

인썸니아 2024. 8. 6. 20:52

 

우분투에서 어떠한 기능을 서비스로 돌리기 위해 systemd에 등록할 수 있다.

 

## service 파일 등록

/etc/systemd/system 위치에 파일명 {service name}.service 로 아래와 같이 생성 후 저장한다.

[Unit]
Description=My Test Service

[Service]
Type=simple
WorkingDirectory=/home/yunikim/dev/study/cpp_testing/cmake-build-debug
ExecStart=/home/yunikim/dev/study/cpp_testing/cmake-build-debug/ipc_exam_server
StandardOutput=tty
TTYPath=/dev/pts/0

[Install]
WantedBy=multi-user.target

 

기본적으로 systemd에 등록한 서비스가 출력하는 로그들은 journal에 저장되고, 아래 명령으로 확인할 수 있다.

$ journalctl -u service-name

 

하지만, 위의 예제에서는 ssh를 통해 접속한 terminal에서의 출력을 위한 stdout 변경을 위해 StandardOutput TTYPath 설정을 추가하였다. (man systemd.exec에서 해당 옵션에 대해 확인할 수 있다.)

 

 

systemd.exec

Takes a boolean argument or the special values "full" or "strict". If true, mounts the /usr/ and the boot loader directories (/boot and /efi) read-only for processes invoked by this unit. If set to "full", the /etc/ directory is mounted read-only, too. If

www.freedesktop.org

 

현재의 terminal tty 정보는 아래 명령으로  알 수 있다.

 

 

## systemctl을 통한 서비스 제어

systemd 서비스 제어를 위한 주요 명령어는 다음과 같다.

 

# systemd configuration reload
systemctl daemon-reload

# show service status
systemctl status {service name}

# start/restart/stop service
systemctl start/restart/stop {service name}

# enable/disable service
# 자동으로 서비스가 실행되도록 설정
systemctl enable/disable {service name}

 

 

## 서비스 프로그램 예제

아래는 지난 글에서 소개했던 예제에서 C++의 std::thread를 사용하여 약간 변경한 예제이다.

#include <iostream>
#include <thread>
#include <mutex>
#include <sys/ipc.h>
#include <sys/msg.h>

#define MAX_TEXT 100

struct msg_buffer {
    [[maybe_unused]] long msg_type;
    char msg_text[MAX_TEXT];
};

volatile bool g_aborted = false;
volatile bool g_print = false;
int msgid;
char output_char = 'A';  // default output character

void message_receiver();

int main() {
    key_t key;

    std::cout << "start test server !!" << std::endl;

    // Generate unique key for IPC
    if ((key = ftok(".", 173)) == -1) {
        perror("ftok");
        exit(EXIT_FAILURE);
    }

    // Create message queue
    if ((msgid = msgget(key, 0666 | IPC_CREAT)) == -1) {
        perror("msgget");
        exit(EXIT_FAILURE);
    }

    std::thread receiver { message_receiver };
    //receiver.detach();

    // Main thread: Output the current char every second
    while (not g_aborted) {
        if (g_print)
            std::cout << output_char << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    if (receiver.joinable())
        receiver.join();

    std::cout << "exit test server !!" << std::endl;

    return EXIT_SUCCESS;
}

void message_receiver() {
    struct msg_buffer msg {};

    while (true) {
        // Receive message from client
        if (msgrcv(msgid, &msg, sizeof(msg.msg_text), 1, 0) == -1) {
            perror("msgrcv");
            exit(1);
        }

        // Update output character
        output_char = msg.msg_text[0];

        if (output_char == 'Z') {
            g_aborted = true;
            break;
        }
        else if (output_char == 'P') {
            g_print = not g_print;
        }
    }

    std::cout << "exit message receiver" << std::endl;
}

 

service 파일의 StandardOutput을 변경하지 않은 경우(아마, journel인 경우)에는, 서비스를 종료하고 status 확인했을때 아래와 같이 journal의 로그도 함께 확인해 볼 수 있다.

 

 

 

 

반응형