일반적으로 polling이라함은 아주 쉬운 설명으로 cpu가 주변장치에게서 읽을것이 있나없나 스스로 확인하는것을 의미합니다. 그런데 문득 들었던 생각이 poll 함수는 polling인가라는 것이었습니다.
제 생각으로 아니라고 결론지었습니다.
우선 polling이라함은 interrupt 방식과는 대조적으로 polling rate도 설정해야 하는등 수동적인 조작이 필요한 부분이 있습니다. 예를 들어 pseudo-code로 확인할 수 있습니다.
while (true) {
if (eyboard.is_readable()) {
do_something()
} else {
sleep(wait for);
}
}
sleep 함수를 이용해 적당한 polling rate를 설정해줘야 합니다. 이렇게 되면 while문을 돌리는 task가 cpu 점유권을 가지고 지속적으로 keyboard.is_readable()을 통해 키보드 입력이 있는지 확인합니다. 이에 따른 최악의 경우로는 키보드 입력이 없는데도 불구하고 지속적으로 키보드로부터 읽을것이 있나 확인하게 되는것이죠. 즉 cpu 사이클 낭비입니다.
하지만 poll 함수는 timeout을 걸어서 해당 task를 block 상태로 만듭니다. poll 함수의 description을 보도록 하죠.
The timeout argument specifies the number of milliseconds that poll() should block waiting for a file descriptor to be‐ come ready. The call will block until either:
* a file descriptor becomes ready;
* the call is interrupted by a signal handler; or
* the timeout expires.
mint os 에서 man poll 입력한 후 timeout에 대한 description을 확인할 수 있습니다. 해당 디스크립션을 참조해보면 poll 함수가 polling과 용어가 비슷하다고 해서 polling을 의미하는거다라고 볼 수 없을것 같습니다.
저는 회사에서 mbed 또는 yocto linux를 이용하여 펌웨어를 작성하는 업무를 진행합니다. 업무를 진행하던 중 gps를 처리해야하는 일이 생겼었는데, 인터넷의 예제를 이용해 코딩을 진행하다가 꽤 큰 곤란을 겪었습니다. 그래서 어떤식으로 처리를 해야하는지 많은 고민을 했었는데, 그 해결법을 이번 글에 포스팅하겠습니다.
gps 모듈은 전원을 공급하면, 곧바로 gps 메시지를 출력합니다. 이는 Tera Term같은 에뮬레이터를 이용하여 확인할 수 있습니다. 그리고 전원 공급을 멈출때까지 계속 출력을 진행합니다. 일종의 스트리밍 데이터인것이죠. 앞으로는 이러한 스트리밍 데이터를 아래와 같은 코딩 방법으로 처리하려고 합니다.
1. blocking I/O 기법 이용
이렇게 처리해야겠다 생각이 든것은 can-utils에 포함된 candump 툴의 코드를 봤기 때문입니다
candump는 can 데이터를 처리할 때 이용합니다. 예시로 can 시뮬레이터와 리눅스 디바이스를 연결하고 시뮬레이션할 때 "candump can0"와 같은 명령어를 이용합니다. can0 채널로 올라오는 can 데이터를 터미널에 출력합니다.
그럼 candump는 연속적으로 들어오는 데이터, 즉 스트리밍 데이터를 어떻게 처리하느냐... 아래와 같습니다.
candump.c
while (running) {
num_events = epoll_wait(fd_epoll, events_pending, currmax, timeout_ms);
if (num_events == -1) {
if (errno != EINTR)
running = 0;
continue;
}
......
실제 코드는 훨씬 깁니다만, candump가 I/O를 처리하는 방식은 epoll_wait 펑션을 이용합니다. 더 자세히 알려면 epoll_wait의 디스크립션을 봐야겠죠.
The epoll_wait() system call waits for events on the epoll(7)
instance referred to by the file descriptor epfd. The buffer
pointed to by events is used to return information from the ready
list about file descriptors in the interest list that have some
events available. Up to maxevents are returned by epoll_wait().
The maxevents argument must be greater than zero.
The timeout argument specifies the number of milliseconds that
epoll_wait() will block. Time is measured against the
CLOCK_MONOTONIC clock.
A call to epoll_wait() will block until either:
• a file descriptor delivers an event;
• the call is interrupted by a signal handler; or
• the timeout expires.
Note that the timeout interval will be rounded up to the system
clock granularity, and kernel scheduling delays mean that the
blocking interval may overrun by a small amount. Specifying a
timeout of -1 causes epoll_wait() to block indefinitely, while
specifying a timeout equal to zero cause epoll_wait() to return
immediately, even if no events are available.
The struct epoll_event is defined as:
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
The data field of each returned epoll_event structure contains
the same data as was specified in the most recent call to
epoll_ctl(2) (EPOLL_CTL_ADD, EPOLL_CTL_MOD) for the corresponding
open file descriptor.
The events field is a bit mask that indicates the events that
have occurred for the corresponding open file description. See
epoll_ctl(2) for a list of the bits that may appear in this mask.
candump.c에서의 epoll_wait의 timeout은 -1로 설정되어 있습니다.
int timeout_ms = -1; /* default to no timeout */
timeout이 없다는것입니다. 즉, timeout이 없는 blocking인것을 확인할 수 있습니다.
candump 명령어는 회사에서 테스트해본 결과 ns 단위의 I/O도 정상적으로 처리하는 것으로 확인했습니다. 실제로 시뮬레이터 동작과도 일치하고요.