Named pipe
명명된 파이프(Named pipe) 또는 지명 파이프는 유닉스 및 유닉스 계열의 일반 파이프를 확장 한 것으로, 프로세스 간 통신 기법 중 하나이다. Pipes 또는 FIFO 라고도 한다.
그 개념은 마이크로소프트 윈도도 있지만, 의미는 크게 다르다. 일반 파이프는 ‘파이프’이며, 사용하는 프로세스가 실행 중에만 존재한다. 명명된 파이프는 영구 프로세스가 소멸해도 계속 존재하기 때문에 사용하지 않으면 제거할 필요가 있다. 명명된 파이프는 파일과 같이 취급할 수 있고 프로세스 간 통신(IPC)을 위해 프로세스가 오픈되어 사용한다. 또한 동작에서 명명된 파이프를 FIFO로 부르기도 한다.
표준 입출력 Pipe 속도 테스트
dd명령을 사용한 방법은 Standard streams#표준 입출력 Pipe 속도 테스트 항목 참조.
Lock 을 사용해야 하는가?
mkfifo로 생성된 FIFO 파일을 사용하여 IPC(Inter-Process Communication)를 할 때, 일반적으로는 read 및 write에 별도의 lock을 걸 필요가 없습니다. FIFO는 커널에서 관리되는 특수 파일로, 여러 프로세스가 동시에 접근할 때도 안전하게 동작하도록 설계되어 있습니다. FIFO는 기본적으로 다음과 같은 동작을 보장합니다:
- Atomicity
- 작은 크기의 write (대개 #PIPE_BUF 크기 이하)는 원자적으로 처리되므로, 여러 프로세스가 동시에 쓰기를 시도해도 데이터가 뒤섞이지 않습니다.
- Blocking
- FIFO는 기본적으로 blocking 모드로 동작합니다. 즉, read는 데이터가 올 때까지 기다리고, write는 읽을 프로세스가 없으면 블록됩니다. 이로 인해 프로세스 간의 동기화가 어느 정도 이루어집니다.
하지만 특정 상황에서는 추가적인 동기화 메커니즘이 필요할 수 있습니다:
- 대용량 데이터 전송
- #PIPE_BUF(일반적으로 4KB) 이상의 데이터를 전송할 때는 여러 번의 write 호출이 필요하며, 이 경우 원자성을 보장받지 못합니다. 따라서 대용량 데이터 전송이 필요하다면, 추가적인 동기화가 필요할 수 있습니다.
- 순서 보장
- FIFO 자체는 첫 번째 들어온 데이터가 먼저 나가는 방식으로 동작하지만, 여러 프로세스가 동시에 접근할 때는 전송 순서를 보장하지 못할 수 있습니다. 특정 순서대로 데이터를 처리해야 한다면, 이를 위한 별도의 동기화가 필요할 수 있습니다.
이를 위해 사용할 수 있는 동기화 방법은 다음과 같습니다:
- Mutexes or Semaphores
- 공유 메모리나 파일 기반의 mutex 또는 세마포어를 사용하여 read 또는 write 접근을 동기화할 수 있습니다.
- Message Queues
- 메시지 큐를 사용하면 FIFO와 유사한 기능을 제공하면서도 추가적인 동기화 메커니즘을 제공합니다.
- Shared Memory
- 공유 메모리를 사용하여 데이터를 전송하고, 이를 제어하기 위한 별도의 동기화 메커니즘을 사용할 수 있습니다.
일반적인 사용 사례에서는 FIFO의 기본 동작만으로 충분하지만, 특정한 동기화 요구사항이 있는 경우에는 위와 같은 추가적인 방법을 고려할 수 있습니다.
ENXIO
A process can open a FIFO in non-blocking mode.
In this case, opening for read only will succeed even if no-one has opened on the write side yet,
opening for write only will fail with ENXIO (no such device or address) unless the other end has already been opened.
프로세스는 비차단 모드에서 FIFO를 열 수 있습니다. 이 경우 읽기 전용 열기는 쓰기 측에서 아직 아무도 열지 않은 경우에도 성공하고, 다른 쪽 끝이 이미 열려 있지 않으면 ENXIO(해당 장치 또는 주소 없음)를 사용하여 쓰기 전용 열기에 실패합니다.
즉, 쓰기 전용 파일에서는 읽기 전용 파일이 열려있지 않다면 ENXIO(해당 장치 또는 주소 없음) 에러가 발생된다.
따라서 O_NONBLOCK 모드로 열 때 읽기 전용 파일부터 열어야 한다.
PIPE_BUF
limits.h 파일에 정의된 PIPE_BUF 시스템 변수는 파이프에 기록될 때 원자가 되도록 보장되는 최대 바이트 수를 지정합니다.
매뉴얼 페이지에서 확인할 수 있다. man 7 pipe
로 확인할 수 있는 PIPE_BUF 에 대한 설명은 다음과 같다:
PIPE_BUF
POSIX.1 says that write(2)s of less than PIPE_BUF bytes must be atomic: the output data is written to the pipe as a contiguous sequence.
Writes of more than PIPE_BUF bytes may be nonatomic: the kernel may interleave the data with data written by other processes. POSIX.1 requires
PIPE_BUF to be at least 512 bytes. (On Linux, PIPE_BUF is 4096 bytes.) The precise semantics depend on whether the file descriptor is non‐
blocking (O_NONBLOCK), whether there are multiple writers to the pipe, and on n, the number of bytes to be written:
O_NONBLOCK disabled, n <= PIPE_BUF
All n bytes are written atomically; write(2) may block if there is not room for n bytes to be written immediately
O_NONBLOCK enabled, n <= PIPE_BUF
If there is room to write n bytes to the pipe, then write(2) succeeds immediately, writing all n bytes; otherwise write(2) fails, with
errno set to EAGAIN.
O_NONBLOCK disabled, n > PIPE_BUF
The write is nonatomic: the data given to write(2) may be interleaved with write(2)s by other process; the write(2) blocks until n bytes
have been written.
O_NONBLOCK enabled, n > PIPE_BUF
If the pipe is full, then write(2) fails, with errno set to EAGAIN. Otherwise, from 1 to n bytes may be written (i.e., a "partial
write" may occur; the caller should check the return value from write(2) to see how many bytes were actually written), and these bytes
may be interleaved with writes by other processes.
PIPE_BUF는 시스템마다 다를 수 있으며, 일반적으로 limits.h 헤더 파일에 정의되어 있습니다. 이 값을 확인하는 방법은 여러 가지가 있습니다:
getconf 명령을 사용하여 터미널에서 바로 확인 가능하다.
리눅스의 경우 명령이 안될 수 있다. 다음과 같이 테스트해보자:
이미 FIFO 파일이 있고 (없다면 mkfifo /tmp/my_fifo
으로 생성), python을 사용한다면, 다음과 같이 확인할 수 있다:
import os
# 파이프의 경로 또는 파일 디스크립터를 지정합니다.
pipe_path = '/tmp/my_fifo'
# PIPE_BUF 값을 확인합니다.
pipe_buf_size = os.pathconf(pipe_path, 'PC_PIPE_BUF')
print(f'PIPE_BUF: {pipe_buf_size}')
Python example
서로 다른 파이선 프로그램이 서로 데이터를 주고 받아야하는 경우, 네임드파이프 파일을 이용하여 이용하여 프로세스간 통신을 할 수 있습니다. 예를 들어 GUI 와 데이터 처리 프로그램을 프로세스로 분리할 경우, 데이터 처리 프로그램에서 결과를 네임드 파이프 (FIFO 라고도 함) 를 통해 GUI 프로그램으로 전달합니다.
우선, 첫번째 프로그램에서 파이썬 내장 모듈의 os.mkfifo
라는 함수를 이용하여 네임드파이프(FIFO) 파일을 생성합니다.
생성된 파일을 쓰기 권한으로 열고 write()
함수를 통해 원하는 내용을 써줍니다.
그리고 두번째 프로그램에서는, 동일한 네임드파이프 파일을 읽기권한으로 열고, read()
함수를 통해 읽습니다.
Full source code
데이터송신 프로그램 : send.py
import os.path
FIFO_FILENAME = './fifo-test'
if not os.path.exists(FIFO_FILENAME):
os.mkfifo(FIFO_FILENAME)
if os.path.exists(FIFO_FILENAME):
fp_fifo = open(FIFO_FILENAME, "w")
for i in range(128):
fp_fifo.write("Hello,MakeCode\n")
fp_fifo.write("")
데이터수신 프로그램: recv.py
import os.path
FIFO_FILENAME = './fifo-test'
if os.path.exists(FIFO_FILENAME):
fp_fifo = open(FIFO_FILENAME, "r")
i = 0
while True:
with open(FIFO_FILENAME, 'r') as fifo:
data = fifo.read()
line = data.split('\n')
for str in line:
i = i+1
print(str + "%4d" % i)
Non-Block 모드에서 Read 방법
- python - How to read named FIFO non-blockingly? - Stack Overflow
- nonblocking read function for python, e.g. for named pipes - Gist
According to the manpage of read(2):
EAGAIN or EWOULDBLOCK
The file descriptor fd refers to a socket and has been marked
nonblocking (O_NONBLOCK), and the read would block.
POSIX.1-2001 allows either error to be returned for this case,
and does not require these constants to have the same value, so
a portable application should check for both possibilities.
So what you're getting is that there is no data available for reading. It is safe to handle the error like this:
try:
buffer = os.read(io, BUFFER_SIZE)
except OSError as err:
if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK:
buffer = None
else:
raise # something else has happened -- better reraise
if buffer is None:
# nothing was received -- do something else
else:
# buffer contains some received data -- do something with it
Make sure you have the errno module imported: import errno
.
Full-duplex flow
FIFO는 기본적으로 Half-duplex flow 이다. 여러 프로세스가 하나의 파일을 공유하여 그곳에 쓰고 읽기 때문에 둘 다 동시에 쓰는 작업을 할 수 없을 것이다.
Full-duplex flow 를 달성하기 위해선 FIFO를 동시에 두 개 생성해야 한다.
TCP/IP vs Unix Domain Sockets vs Named PIPE
Unix Domain Socket#TCP/IP vs Unix Domain Sockets vs Named PIPE 항목 참조.
Troubleshooting
OSError: [Errno 6] No such device or address
fifo
라는 이름의 mkfifo 파일이 있을 때 a = os.open('./fifo', os.O_WRONLY | os.O_NONBLOCK)
으로 열면 다음과 같은 에러가 발생된다:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 6] No such device or address: './fifo'
결과부터 말하면 읽기 파일부터 열어야 한다. #ENXIO 항목 참조.