리눅스에서 파일 디스크립터(fd)란?
epoll은 어떤 역할을 할까?
이 두 개념을 제대로 이해하면 고성능 네트워크 프로그래밍과 시스템 프로그래밍을 더 쉽게 다룰 수 있습니다.
오늘은 파일 디스크립터(fd)와 epoll이 어떻게 동작하는지 쉬운 예제와 함께 설명해보겠습니다.
🔹 파일 디스크립터(fd)란?
운영체제에서 파일, 디렉터리, 소켓, 파이프, 장치 파일 같은 모든 I/O 자원을 관리하는 번호(핸들)입니다.
**즉, fd는 "운영체제가 파일을 다루기 위해 부여하는 숫자"**라고 생각하면 됩니다.
📌 기본적인 fd 값
fd 값설명
0 | 표준 입력 (stdin, 키보드 입력) |
1 | 표준 출력 (stdout, 터미널 출력) |
2 | 표준 에러 (stderr, 오류 메시지) |
우리가 프로그램을 실행할 때, 기본적으로 이 3개의 fd가 항상 열려 있습니다.
하지만 open()을 사용하면 추가적인 파일이나 디렉터리를 fd로 다룰 수 있습니다.
✅ 파일을 열고 fd를 확인하는 예제
#include <fcntl.h> // open()
#include <unistd.h> // close()
#include <iostream>
int main() {
int fd = open("test.txt", O_RDONLY); // 파일 열기
if (fd == -1) {
perror("파일 열기 실패");
return 1;
}
std::cout << "파일 디스크립터: " << fd << std::endl;
close(fd); // 파일 닫기
return 0;
}
📌 실행하면 test.txt 파일을 열고 파일 디스크립터 번호가 출력됩니다.
예를 들어, fd = 3이 출력될 수 있습니다.
(fd 0~2는 이미 기본적으로 사용 중이기 때문에 3부터 할당됨)
✅ 디렉터리도 fd로 다룰 수 있음
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
int main() {
int fd = open("/home/user", O_RDONLY); // 디렉터리 열기
if (fd == -1) {
perror("디렉터리 열기 실패");
return 1;
}
std::cout << "디렉터리의 fd: " << fd << std::endl;
close(fd); // 닫기
return 0;
}
📌 디렉터리도 파일처럼 열어서 fd로 다룰 수 있습니다!
하지만 read()로 읽을 수는 없고, readdir() 같은 함수를 사용해야 합니다.
🔹 epoll이란?
리눅스에서 epoll은 다수의 파일 디스크립터를 효율적으로 감시하는 기술입니다.
파일뿐만 아니라 소켓, 파이프, 장치 파일 같은 다양한 fd를 감시할 수 있습니다.
✅ epoll이 필요한 이유?
fd가 많아질수록 기존 select()나 poll() 방식은 성능이 떨어집니다.
- 기존 방식 (select, poll)
- 100개의 fd가 있다면, 모든 fd를 반복적으로 확인해야 함 → 비효율적
- epoll 방식
- **"이벤트가 생기면 알려줘!"**라고 운영체제에 요청
- 이벤트 발생 시점에만 처리 → 성능 향상
✅ epoll을 활용한 FIFO 감지 예제
#include <sys/epoll.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h> // mkfifo 함수 사용을 위한 헤더 추가
#include <iostream>
#define FIFO_PATH "./test_fifo"
int main() {
// FIFO(이름 있는 파이프) 생성
if (mkfifo(FIFO_PATH, 0666) == -1) {
perror("mkfifo 실패");
return 1;
}
int epfd = epoll_create1(0); // epoll 인스턴스 생성
int fd = open(FIFO_PATH, O_RDONLY | O_NONBLOCK); // 비동기 모드로 열기
if (fd == -1) {
perror("파일 열기 실패");
return 1;
}
struct epoll_event ev;
ev.events = EPOLLIN; // 읽을 데이터가 있는지 감지
ev.data.fd = fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); // epoll에 등록
struct epoll_event events[5];
while (true) {
int n = epoll_wait(epfd, events, 5, -1); // 이벤트 대기
if (n > 0) {
std::cout << "데이터가 들어옴!" << std::endl;
char buffer[128] = {0};
read(fd, buffer, sizeof(buffer)); // 데이터 읽기
std::cout << "읽은 데이터: " << buffer << std::endl;
}
}
close(fd);
close(epfd);
return 0;
}
📌 코드 설명
- mkfifo(FIFO_PATH, 0666)
→ **이름 있는 파이프(FIFO)**를 생성
(이미 존재하면 오류 발생하지 않음) - open(FIFO_PATH, O_RDONLY | O_NONBLOCK)
→ FIFO를 읽기 전용 + 비동기 모드로 열기 - epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev)
→ epoll에 FIFO 감지 등록 (읽을 데이터가 생기면 감지) - epoll_wait()
→ 새로운 데이터가 들어올 때까지 대기
(데이터가 들어오면 read()로 읽고 출력)
✅ 실행 방법
1️⃣ 컴파일 & 실행
g++ -o epoll_fifo epoll_fifo.cpp
./epoll_fifo
2️⃣ 다른 터미널에서 데이터 입력
echo "Hello, epoll!" > test_fifo
🔹 FIFO로 데이터가 입력되면, epoll이 감지하고 출력됩니다.
🔹 fd와 epoll을 정리하면?
개념설명
파일 디스크립터(fd) | 운영체제가 파일, 디렉터리, 소켓 등을 다룰 때 사용하는 번호(핸들) |
epoll | 여러 개의 fd에서 이벤트(데이터 읽기/쓰기 가능)를 감시하는 고성능 이벤트 감지 기술 |
epoll의 장점 | select, poll보다 성능이 뛰어나며, 비동기 처리가 가능 |
✔ epoll을 사용하면 다중 연결(소켓)이나 대량의 파일 이벤트를 더 효율적으로 감시 가능!
✔ fd는 파일뿐만 아니라 디렉터리, 소켓, 파이프까지 다룰 수 있음
📌 마무리
- **파일 디스크립터(fd)**는 운영체제가 파일, 디렉터리, 소켓 등을 다루기 위해 부여하는 고유한 번호입니다.
- epoll은 fd를 효율적으로 감시하는 기술로, 다중 I/O 처리를 더 빠르게 할 수 있습니다.
- open()으로 fd를 얻고, close()로 해제하는 것이 중요합니다.
'임베디드 관련 카테고리 > Ubuntu' 카테고리의 다른 글
Ubuntu에서 Python 가상환경(venv) 설정 및 externally-managed-environment 에러 해결 (1) | 2024.12.05 |
---|---|
dd 명령어로 ISO 이미지를 특정 파티션에 설치하는 방법 (0) | 2024.11.26 |
Ubuntu 관리 꿀팁: USB 제작부터 파티션 관리, ISO 생성, 특정 파티션 OS 설치까지! (0) | 2024.11.26 |
Docker로 GitHub 리포지토리 클론 및 환경 설정 자동화하기 (3) | 2024.10.23 |
.env 파일로 GITHUB_TOKEN 환경 변수 항상 사용하기 (0) | 2024.10.22 |
댓글