리얼타임시계 함수사용에 관한 질의
김선영님의 Advanced Linux System Network Programming 이란 책을 참고해서
몇가지 코딩작업중인 초보입니다.
이책의 POSIX 리얼타임 확장단원의 리얼타임시계의 사용에 관한 부분이 있는데
그중 timer_create(), timer_settime()을 사용해서 타이머를 구현하고자 합니다.
제가 궁금한것은 여러개의 타이머를 하나의 타이머 핸들러를 사용해서 처리하고자 하는것 입니다.
예를들면, 동일한 sigevent 정보와 두개의 timerid를 timer_create 함수로 등록하고
각각의 timerid에 대하여 timer_settime 함수를 사용하여 다른 interval를 주도록 하였습니다.
이럴경우 두개의 timerid에 해당하는 interval대로 sigevent에 등록된 핸들러가 호출은 됩니다만
핸들러내에서 tiemrid를 식별할 방법을 찾지 못했습니다.
다시 말씀드리면 sigevent에 등록되는 핸들러 함수의 프로토타입은
void rt_timer_handler(int signum, siginfo_t *si, void *sv) 인데
siginfo_t 구조체나 ucontext_t 구조체의 포인터로 파악된 sv값에서 tiemrid에 해당하는 변수를
찾지 못하겠습니다.
어찌하면 제가 원하는대로 tiemrid를 식별하여 타이머 구현이 가능할지 궁금합니다.
고수님의 가르침을 부탁드립니다.
추가적으로 Fedora Core5기반의 데스크탑환경에서 실행했을때는
siginfo_t 구조체의 si_band 값이 달라지는것 같아서 그것으로 식별하면 되는게 아닌가 싶었는데
ARM Linux기반의 보드에서 실행했더니 si_band값이 동일하더군요. 달라지는값이 하나도 없는듯(?) 합니다.
최종적으로는 ARM Linux기반의 보드에서 동작시키고자 하고 있습니다.
manpage 를 확인해 보니...
=> evp 를 NULL 로 지정 시 timerid 를 sigev_value 로 자동 설정. timerid 값은 다음 인자로 반환
=> 시그날 핸들러에서 SI_TIMER 일때 si_sigval 멤버로 timerid 판별 가능
도움말 잠시 찾아 보니 위와 같이 언급되어 있네요.
혹은 timer_create 호출시 sigevent 를 직접 지정해서
타이머 콜백을 직접 지정하는 방식을 쓸수도 있겠네요.
직접 써본적은 없지만, 위 처럼 하시면 될거 같네요. 추가 확인사항은 api 도움말을 자세히 보시면 될거 같습니다.
감사합니다.
관심 갖어주셔서 감사드립니다.
우선 제가 siginfo_t 구조체를 잘못 파악하고 있었던것 같습니다.
그동안 제가 책과 man페이지로 알고 있던것은 아래와 같은 형식 이였는데
그런데 막상 /usr/include/bits/siginfo.h 를 찾아서 살펴보니
다음과 같았습니다.
union이 사용되었고 순간 뭔가 다르다는걸 알 수 있었습니다.
결국 제가 원했던 값은 POSIX.1b timers용으로 사용되는 si_timerid 값이었습니다.
하지만!! 여전히 ARM-Linux(2.6.13)에서는 결과가 다르다는거... ㅡㅡ;
ARM보드에서의 Realtime Timer의 구현은 결국 Board Dependancy한 구현의 문제인지...
MCU의 내부RTC를 이용해야만 하는건지... 고민되아부네요...
발상의 전환을... RTC
발상의 전환을...
RTC 라고 해서 타이머가 여러 개 돌 수 있는 것은 아닙니다.
타이머는 한개, 핸들러도 한개, 여러 개의 timer descriptor
의 구조로 설계를 하셔야만 합니다.
이 구조로 설계를 하신다면 MCU 내부 RTC 를 사용하건 rt 확장자를 사용하건 별 차이 없습니다.
중요한 것은 minimum time resolution 을 얼마로 할 것인지와 그에 따른 오차가 얼마정도 되는지 이론적으로, 실험적으로 검증을 해봐야 한다는 것.
리눅스 커널을 아시는 것 같으니... 커널 타이머의 구조를 베끼시면 되겠습니다.
2.4 커널의 경우 10ms 짜리 타이머 한개에 핸들러도 한개 뿐입니다.
2.6 커널도 별 차이 없습니다. architecture/cpu 별로 time resolution 이 차이가 있을 뿐 핸들러는 여전히 한개입니다.
솔직히 말하면... 이 구조와 RTC 를 사용해서 라이브러리를 만든 적이 있습니다.
timeout 이 된 timer descriptor 에 대해 쓰레드(pthread)를 한개 만들고 그 쓰레드 안에서 desctiptor 에 정의된 action handler 를 호출하는 구조였는데... ㅎㅎ 재진입을 막지 않았더니 웃기게 돌아가더군요.
예를 들어 20ms timer descriptor 를 등록하고, action handler 내에서 sleep(1) 처럼 해버리면 ...
rtc 와 posix timer 와는
rtc 와 posix timer 와는 별 상관이 없습니다.
rtc 는 시스템 부팅시에 시간을 읽어오기 위해 존재하는 비휘발성 시계를 의미하는 것이지,
posix timer 에 있는 리얼타임 타이머를 의미하는게 아닙니다. bushi님 께서 용어의 혼동을 했네요.
원글을 적으신 분이 리얼타임시계라고 잘못 적은 것때문에 오는 혼동인거 같네요.
리얼타임시계가 아니라 리얼타임확장 타이머라고 해야 맞겠죠.
그리고 이 리얼타임확장 타이머는 ieee 1003.1b-1993 posix realtime extension 표준에 정의되어있는 타이머 규격을 의미합니다.
표준 규격은 오픈그룹 홈페이지인 www.opengroup.org 가면 볼 수 있습니다.
원 질문자의 의도는
원 질문자의 의도는 타이머를 여러개, 다시 말해 interval 이 틀린 여러개의 핸들러를 운용하고 싶다는 것이 아닌가요 ?
posix realtime extension 을 쓰건 RTC periodic interrupt 를 쓰건 기본적인 구조는 같다는 말을 했고,
의도와는 달리 RTC PI 에 대해 질문자께서 오해하시는 것 같아 이것에 대해서만 구체적으로 설명드렸습니다.
마지막으로,
RTC 와 posix timer 는 별 상관이 없는게 아니라 아무런 관련이 없다고 말해야 정확합니다.
자자~ 정리를
자자~ 정리를 해드리겠습니다
원질문자는 여러개의 다른 인터벌 핸들러를 운용하고 싶은 것이 맞고요,
그 와중에 posix realtime extension의 기능을 언급한 것이고요,
그런데 정확하게는 표준안의 언급된 함수로만 프로그래밍을 하는게 좋습니다.
되도록이면 커널을 건드리지 않고 작업하는게 나중을 위해서도 더 좋은 것이죠
간혹 이것을 거꾸로 하는 사람들이 많은데 좋지 않은 습관이고, 우리나라에서나 통용되는 수법이죠
미국에서 이렇게 작업하면 보스에게 불려가게 됩니다
그리고 posix timer 와 RTC 는 원래 별개의 것이지요.
서로 상관없는 것인데 왜 자꾸 언급하는지 모르겠네요
이건 컴퓨터 구조 배울때 수업시간에 다루는 내용이니 텍스트북에도 나와있지요.
RTC 가 단순한 시계일
RTC 가 단순한 시계일 뿐이라면 세상이 좀 더 복잡했을 겁니다.
반면에... RTC 장치 또는 IP를 만드는 업자들은 땅짚고 헤엄치기 마냥 쉽게 장치를 만들어 팔아먹었을테지요.
굳이 timer ID를 가지고 구분할 필요가 있나요?
아주 오래전 질문에 대해서, 2년이상 지난뒤에 답변해봅니다. 저도 현재 시그널 관련해서 공부하고 있는 중이라서...
원하시는 형태가 혹시 첨부시킨 파일과 같은 기능인지는 모르겠습니다.
gcc -Wall -g -o sigtimer sigtimer.c -lrt
로 컴파일하고 실행해보시면 됩니다.
질문에 대한 답변을 드리자면,
하나의 프로세서에서 여러 개의 타이머를 만들고, 여러 타이머 핸들러를 하나의 핸들러에서 처리하고 싶다는 것이죠.
첨부시킨 파일 내용을 보시면 충분히 가능합니다.
그런데, 질문하신 내용에서 굳이 핸들러에서 타이머를 구분할 때, timer ID를 가지고 하시려고 하는데, 굳이 그럴 필요가 있을련지요?
물론 timer ID가 타이머를 구분하는 유일한 값이지만, 지금 상황(시스템 관점에서 볼 때,)에서는 timer ID로만 구분할 필요는 없습니다.
왜냐하면, timer ID를 얻기위해서 사용되는 timer_create() 함수에서 인자로 주어지는 struct sigevent 인자에
바로 시스템에서 유일하게 사용되는 시그널 번호를 주기 때문입니다.
시그널 핸들러에서는 그 timer ID와 유일하게 연결되는 시그널 번호를 가지고 구분하면 됩니다.
다시 설명드리면, 시그널 핸들러에서 첫번째 인자로 주어지는 시그널 번호를 가지고 시그널 핸들러가 타이머를 구분하면 됩니다.
시그널 번호는 timer_create() 함수에서 인자로 주어지는 struct sigevent 인자의 sigev_signo 멤버에 할당하게 됩니다.
시그널 번호와 timer ID가 일대일 매칭이기 때문에, 충분히 구분하는데 문제가 없습니다.
----------------------------
정보공개는 자신감에서 온다.
당신은 그 자신감이 있는가?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include
#include
#include
#include
#include
#include
#define SIGTIMER (SIGRTMAX)
#define ONESHOTTIMER (SIGRTMAX-1)
timer_t SetTimer(int signo, int sec, int mode);
void SignalHandler(int signo, siginfo_t * info, void *context);
timer_t timerid,oneshotTimer;
int main()
{
struct sigaction sigact;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_SIGINFO;
sigact.sa_sigaction = SignalHandler;
// Set up sigaction to catch signal
if (sigaction(SIGTIMER, &sigact, NULL) == -1) {
perror("sigaction failed");
return -1;
}
// Set up sigaction to catch signal
if (sigaction(ONESHOTTIMER, &sigact, NULL) == -1) {
perror("sigaction failed");
return -1;
}
// Establish a handler to catch CTRL+C and use it for exiting
sigaction(SIGINT, &sigact, NULL);
timerid = SetTimer(SIGTIMER, 1000, 1);
oneshotTimer = SetTimer(ONESHOTTIMER, 10000, 0);
while(1);
return 0;
}
timer_t SetTimer(int signo, int sec, int mode)
{
struct sigevent sigev;
timer_t timerid;
struct itimerspec itval;
struct itimerspec oitval;
// Create the POSIX timer to generate signo
sigev.sigev_notify = SIGEV_SIGNAL;
sigev.sigev_signo = signo;
sigev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCK_REALTIME, &sigev, &timerid) == 0) {
itval.it_value.tv_sec = sec / 1000;
itval.it_value.tv_nsec = (long)(sec % 1000) * (1000000L);
if (mode == 1) {
itval.it_interval.tv_sec = itval.it_value.tv_sec;
itval.it_interval.tv_nsec = itval.it_value.tv_nsec;
} else {
itval.it_interval.tv_sec = 0;
itval.it_interval.tv_nsec = 0;
}
if (timer_settime(timerid, 0, &itval, &oitval) != 0) {
perror("time_settime error!");
}
} else {
perror("timer_create error!");
return (timer_t)-1;
}
return timerid;
}
void SignalHandler(int signo, siginfo_t * info, void *context)
{
if (signo == SIGTIMER) {
puts("Periodic timer");
}
else if (signo == ONESHOTTIMER) {
puts("One-short timer");
}
else if (signo == SIGINT) {
timer_delete(oneshotTimer);
timer_delete(timerid);
perror("Ctrl + C cached!\n");
exit(1);
}
}
정보공개는 자신감에서 온다.
당신은 그 자신감이 있는가?
댓글 달기