System Programming/Device Driver

Linux Device Driver 기초 #4 system daemon과 라이브러리 개발

uzguns 2024. 10. 20. 10:08

IPC

프로세스간 통신은 서로 다른 프로세스가 데이터를 교환할 수 있도록 하는 통신 메커니즘이다. 

 

pipe

부모 프로세스와 자식 프로세스 사이에서 간단하게 데이터를 주고 받기 위해 사용되는 단방향 통신 방법이다. 한쪽 프로세스가 쓰고 다른 쪽 프로세스가 읽는 방식으로 작동하며 기본적으로 데이터를 한방향으로만 전달한다.

 

socket

네트워크 통신을 위한 양방향 통신 방법으로 동일한 시스템 뿐만 아니라 네트워크를 통해 다른 시스템과도 통신할 수 있다. TCP/IP 와 같은 프로토콜을 사용하며 클라이언트와 서버간의 통신을 설정하고 데이터를 주고 받을 수 있다. 

 

named pipe

naemd pipe는 이름이 붙어있어 부모-자식 관계가 아닌 프로세스 간에도 사용할 수 있는 통신 방식이다. 

named pipe는 다음과 같은 특징이 있다.

  • 단방향 통신
    • 일반 파이프와 마찬가지로 데이터를 보내는 쪽과 받는 쪽이 미리 정해져야 한다.
    • 데이터를 양방향으로 주고 받으려면 두개의 파이프가 필요하다.
  • 주로 FIFO 방식으로 통신

mkfifo 명령어로 네임드 파이프 생성할 수 있다. 

#!/bin/bash
mkfifo -m 0600 fifo  # FIFO 파일 생성
wc -l < fifo &       # FIFO에서 읽은 내용을 wc 명령으로 처리
cat /etc/passwd > fifo  # /etc/passwd 파일을 fifo로 보냄

cat < fifo # 다른쪽 터미널에서 확인

rm fifo             # 작업 완료 후 FIFO 파일 삭제

 

shared memory

가장 빠른 IPC 방식 중 하나로 여러 프로세스가 동일한 메모리 영역을 공유하여 데이터를 주고 받는 방식이다. 복사 과정 없이 직접 메모리를 참조할 수 있기 때문에 성능이 매우 빠르다. 

프로세스들이 shm_open, mmap 등의 시스템 콜을 통해 동일한 물리적 메모리 영역을 매핑하고 그 메모리에서 데이터를 읽거나 쓸 수 있다. 동시 접근 제어가 필요하며 semaphore나 mutex 같은 동기화 기법이 필수적이다.

 

  • mmap(start, length, prot, flags, fd, offset) : 메모리 매핑 함수
    • fd가 실제 파일이면 파일의 일부를 메모리에 매핑
      • 파일로의 접근을 read / write 호출 없이 일반적인 메모리 접근 이랑 동일하게 사용가능
      • shm_open 으로 얻어진 경우는 특정 물리 메모리를 매핑: 공유 메모리로 사용
    • start 는 NULL, flag는 MAP_SHARED 를 일반적으로 사용, prot 은 읽기/쓰기 가능 여부 지정
  • shm_open(name, flags, mode): name의 이름으로 공유메모리를 오픈
    • 실제 파일은 아니지만 일반적으로 name 은 "/"로 시작하는 것을 추천
    • 맨처음에는 O_CREATE 로 생성해야함(/dev/shm 밑에 생성되며 재부팅하면 사라짐)
  • ftruncate(fd, size): 파일 크기를 설정(공유 메모리는 메모리의 크기 설정)

 

Named Semaphore

동시에 여러 프로세스가 접근하면 안되는 자원을 보호하기 위한 기법으로 세마포어는 여러 프로세스 간 동시 접근을 조율하며 특정 자원의 접근을 제한하는 데 사용된다. named semaphore는 이름이 있는 세마포어로 서로 다른 프로세스에서 공통의 세마포어에 접근할 수 있도록 지원한다.

Futex(Fast user-space Mutex) 라는 기법도 많이 사용된다.

 

semaphore와 mutex의 차이

  • mutex는 해당 자원의 접근은 모두 상호 배제, 즉 한번에 하나
  • semaphore 는 해당 자원에 동시 접근 가능한 수 제한, 즉 한번에 하나 이상

semaphore는 특정 서버 자원에 대한 최대 동시 연결 수를 제한할 때 유용하다. named semaphore는 이를 여러 프로세스에서 공유할 수 있도록 해주기 때문에 공통의 리소스에 대한 접근 제어에 사용된다.

  • sem_open(name, flags, mode, value):
    • 지정한 이름의 네임드 세마포어를 생성하거나 연다.
    • value는 동시에 접근 가능한 프로세스 수를 나타낸다. 예를 들어, value가 3이면 최대 3개의 프로세스가 동시에 자원에 접근할 수 있다.
    • O_CREAT 플래그를 사용하면 세마포어가 존재하지 않을 때 새로 생성합니다. /dev/shm 아래에 sem.* 형태로 생성됩니다.
  • sem_wait(sem):
    • 해당 세마포어에 대한 제어권을 얻다. 세마포어의 카운터를 감소시키고, 만약 카운터가 0이면 다른 프로세스가 제어권을 내려놓을 때까지 대기한다.
  • sem_post(sem):
    • 세마포어의 제어권을 내려놓다. 세마포어의 카운터를 증가시켜, 대기 중인 다른 프로세스가 자원에 접근할 수 있게 한다.

Signal

프로세스에게 뭔가 

 

Systemd

서비스 데몬의 목적은 어플리케이션으로부터 요청을 받아서 그 요청을 서비스 데몬이 대신 수행하기 위함이다.

따라서 server-client 구조로 동작하면 좋은데 이 구조로 동작하는 대표적인 IPC가 Socket이다.

systemd 데몬의 가장 기본적인 동작은 소켓을 열고 listen하고 있다가 요청이 오면 요청을 accept해서 쿼리하고 listen을 계속하는 것이다. 

 

동작 순서

  • Systemd가 소켓을 열어 listen 상태로 둔다.
  • 소켓에 접속 요청이 발생하면, Systemd는 서비스를 실행한다.
  • 서비스 데몬은 실행된 후 accept 처리를 시작한다.
  • 처리가 완료되면 서비스는 종료되고 대기 상태로 돌아간다.
  • 이후 요청이 들어올 때마다 2~4의 과정이 반복된다.

 

sd_listen_fds(0): systemd로부터 현재 서비스 데몬에서 사용할 수 있는 소켓을 획득한다.

  • systemd가 관리하는 파일 디스크립터(FD)들을 반환하며, 기본적으로 SD_LISTEN_FDS_START부터 시작된다.
  • 반환값에 따라 여러 소켓을 사용할 수 있다.

소켓을 사용할 때 옵션:

  • accept 옵션을 사용하지 않은 소켓이 하나 있을 경우, 반환된 값은 1개이다.
  • accept=yes를 사용하면 systemd가 accept도 대신 처리해주지만, 성능 상의 이유로 추천되지 않는다.

빌드 시 systemd/sd-daemon.h를 포함해야 하며, libsystemd.so를 링크하여 사용할 수 있다.
Buildroot 환경에서는 --sysroot=<빌드루트 디렉토리>를 사용하여 systemd 라이브러리 경로를 지정해줘야 한다.

 

 

동작 순서:

  • systemd가 소켓을 열고 listen 상태로 유지한다.
  • 클라이언트로부터 소켓 접속 요청이 들어오면 systemd가 해당 요청을 서비스 데몬으로 전달하고 데몬을 실행한다.
  • 이후 데몬은 시스템이 종료되거나 더 이상 처리할 것이 없으면 종료된다.

 

 

 

SDK