POSIX Timer API
Önceki bölümde değindiğimiz geleneksel timer yapılarının çok önemli bir handikapı vardı. En gelişmişi olan interval timer mekanizmasında bile aynı anda en fazla 3 farklı tipte timer işletmek mümkündü. Herhangi bir tipte yeni bir timer kurulduğunda, zaman olduğunda uygulamayı aynı yöntemle (aynı sinyalle) haberdar ettiğinden birbirinden bağımsız onlarca timer gerektiren senaryolar için verimli bir çözüm üretmek mümkün değildi.
Bu bölümde POSIX tarafından standardize edilen gelişmiş timer kullanımına değineceğiz. Bir POSIX timer'ın oluşturulması, süresinin ayarlanması ve artık ihtiyaç kalmadığı anda yok edilmesinden oluşan temelde 3 aşamalı bir yaşam döngüsü bulunur.
Not: POSIX timer kullanan uygulamalar derlenirken
-lrt
ile POSIX.1b Realtime Extensions kütüphanesine de linklenmelidir.
Oluşturma: timer_create
Timer işlemlerinde timer_t
tipindeki handle, aşağıda prototipi bulunan timer_create()
fonksiyonu ile elde edilmelidir.
#include <signal.h>
#include <time.h>
int timer_create (clockid_t clockid, struct sigevent *evp, timer_t *timerid);
İlk parametre ile kullanılacak clock tipi belirtilir.
Clock Tipi | Açıklama |
---|---|
CLOCK_REALTIME | Gerçek zamanda geçen süre cinsinden zaman hesaplanır. Bu değer sistem saatinin ileri veya geri alınmasından etkilenir. |
CLOCK_MONOTONIC | Sistem açılışında sıfırlanan, saat ayarlamalarından etkilenmeyen güvenilir MONOTONIC zaman değeri üzerinden hesaplanır. |
CLOCK_PROCESS_CPUTIME_ID | Uygulamanın tüm thread'lerinin kullanıcı kipi ve çekirdek kipinde harcadığı toplam süre üzerinden hesaplanır. |
CLOCK_THREAD_CPUTIME_ID | Uygulama içerisinde sadece timer oluşturma isteğini gönderen thread için kullanıcı kipi ve çekirdek kipinde harcanan süre toplamı üzerinden hesaplanır. |
Fonksiyonun 2. parametresi struct sigevent
türünde olup prototipi aşağıdaki gibidir:
#include <signal.h>
struct sigevent {
union sigval sigev_value;
int sigev_signo;
int sigev_notify;
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_attributes;
};
union sigval {
int sival_int;
void *sival_ptr;
};
Yapı içerisinde yer alan sigev_notify
elemanı, timer zamanı dolduğunda uygulamanın nasıl haberdar edileceğini kontrol etmemizi sağlar. Kullanılabilecek sabitler aşağıdaki gibidir:
sigev_notify | Açıklama |
---|---|
SIGEV_NONE | Timer sona erdiğinde uygulamaya herhangi bir bildirimde bulunulmaz |
SIGEV_SIGNAL | Timer sona erdiğinde uygulamaya sigev_signo alanında girilen tipte sinyal gönderilir. Sinyal callback fonksiyonunun si_value değerine, bu yapı ile belirtilen union sigev_value alanıyla tanımlanan parametre geçirilir. |
SIGEV_THREAD | Timer sona erdiğinde işletim sistemi yeni bir thread oluşturur. Thread içerisinde sigev_notify_function fonksiyonu çağrılır. Fonksiyona parametre olarak union sigev_value alanında belirtilen değer geçirilir. Ek olarak oluşturulacak threadin özellikleri belirlenmek istenirse pthread_attr_t tipindeki sigev_notify_attributes alanı kullanılabilir. |
Oluşturulan POSIX timer için sinyalle uyandırılma seçildi ise sigev_signo
alanında verilen sinyalin callback fonksiyonu standart sinyal işleme yöntemleriyle tanımlanmalıdır. Bu noktada dikkat edilecek ek husus, struct sigaction
veri yapısı ile callback tanımlanırken sa_flags
elemanının değerini SA_SIGINFO sabitine eşitlemektir.
Bu şekilde ilgili callback fonksiyonu sinyal nedeniyle çağrıldığında geleneksel sinyal işleme yöntemindeki gibi sadece sinyal numarasını parametre alan bir fonksiyon çağırmaz, bunun yerine aşağıdaki fonksiyon prototipinde bir çağrıda bulunur:
void callback(int sig, siginfo_t *si, void *context)
Bu sayede siginfo_t
ile gelen elemanın içerisinden, timer oluşturulma sırasında ayarlanan sigev_value
geri alınabilir. Asenkron çalışma sistematiği için bu şekildeki bir kullanım çoğu zaman zorunlu olacaktır.
Üçüncü argüman olan genel amaçlı
context
pointer verisi timer mekanizmasında kullanılmaz. Kullanım senaryosunu incelemek içingetcontext()
fonksiyonuna bakabilirsiniz: http://manpages.org/getcontext/3
Ayarlama: timer_settime
Timer oluşturup timer_t
türünde bir handle elde ettikten sonra, aşağıda prototipi yer alan timer_settime()
fonksiyonuyla interval timer sürecine benzer şekilde değerler ayarlanır.
#include <time.h>
int timer_settime(timer_t timerid, int flags,
const struct itimerspec * value, struct itimerspec * old_value)
int timer_gettime(timer_t timerid, struct itimerspec *curr_value)
struct itimerspec {
struct timespec it_interval; /* period of timer */
struct timespec it_value; /* first expiration value */
};
struct timespec {
time_t tv_sec; /* second */
long tv_nsec; /* nanosecond */
};
Interval timer kullanımından farkı it_interval
ve it_value
için struct timeval
yerine, nanosaniye seviyesinde değer girilebilmesine imkan veren struct timespec
tipi kullanılmaktadır. Benzer şekilde it_value
elemanında ilgili timer'ın ilk çalıştığındaki kullanacağı süre değeri, it_interval
elemanında ise sonraki seferlerde periyodik olarak kullanılacak değer yer alır.
İkinci parametre olan flags
argümanı normalde 0 olarak kullanılır. Bu durumda öntanımlı davranış, kullanılan clock tipine göre göreceli şekilde it_value
alanındaki timer'ın ilk çalışmasındaki süre değeri dikkate alınır. Örnek olarak it_value
bölümünde 5 saniyelik bir zaman dilimi tanımlanmışsa, kullanılan clock tipine göre göreceli olarak timer ayarlama zamanından 5 saniye sonra ilk uyandırma gerçekleşir.
Bununla birlikte bazen ilk uyandırma işleminin göreceli olarak değil de belirli bir anda yaptırılması istenebilir. Örnek olarak tam olarak bir sonraki saat başında ilk olarak uyandırılmak, sonra da saniyede bir periyodik uyandırılmak istenebilir. Bu durumda flags
bölümünde TIMER_ABSTIME sabiti kullanılabilir.
Verilen bir timer_t
handle üzerinden bir sonraki uyandırma zamanına ne kadar kaldığı da timer_gettime()
fonksiyonu alınabilmektedir.
Yok Etme: timer_delete
Kullanılmayan timer değerler, prototipi aşağıda belirtilen timer_delete()
fonksiyonuyla yok edilir ve harcadığı sistem kaynakları geri verilir:
#include <time.h>
int timer_delete(timer_t timerid)
Örnek Uygulama
Aşağıdaki uygulamayı posix.c
adıyla kaydedip ardından derleyip çalıştıralım.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#include "../common/debug.h"
#define TIMER_SIGNAL SIGRTMIN + 1
struct person {
int no;
char name[32];
};
void timer_callback (int signum, siginfo_t *si, void *context)
{
(void) context;
struct timeval now;
struct person *p;
gettimeofday(&now, NULL);
p = (struct person *) si->si_value.sival_ptr;
printf ("Signal %d caught on: %li.%03li\t Name: %s\n",
signum, now.tv_sec, now.tv_usec / 1000, p->name);
}
int main ()
{
unsigned int remaining = 3;
timer_t timer1;
timer_t timer2;
struct person *p1;
struct person *p2;
struct itimerspec new_value;
struct itimerspec old_value;
struct sigaction sa;
struct sigevent sev;
int ret;
p1 = calloc(1, sizeof(struct person));
p1->no = 1;
strcpy(p1->name, "Name 1");
p2 = calloc(1, sizeof(struct person));
p2->no = 2;
strcpy(p2->name, "Name 2");
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_callback;
sigemptyset(&sa.sa_mask);
if (sigaction(TIMER_SIGNAL, &sa, NULL) == -1) {
errorf("sigaction error");
exit(1);
}
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = TIMER_SIGNAL;
sev.sigev_value.sival_ptr = p1;
if ( (ret = timer_create(CLOCK_MONOTONIC, &sev, &timer1)) < 0) {
errorf("timer1 create failed: %s", strerror(errno));
}
sev.sigev_value.sival_ptr = p2;
if ( (ret = timer_create(CLOCK_MONOTONIC, &sev, &timer2)) < 0) {
errorf("timer2 create failed: %s", strerror(errno));
}
new_value.it_value.tv_sec = 1;
new_value.it_value.tv_nsec = 0;
new_value.it_interval.tv_sec = 0;
new_value.it_interval.tv_nsec = 600 * 1000 * 1000;
if (timer_settime(timer1, 0, &new_value, &old_value) < 0 ||
timer_settime(timer2, 0, &new_value, &old_value) < 0) {
errorf("timer settime error: %s", strerror(errno));
}
while ( (remaining = sleep(remaining)) != 0) {
if (errno == EINTR) {
debugf("sleep interrupted by signal");
} else {
errorf("sleep error: %s", strerror(errno));
}
}
return 0;
}
Derleyip çalıştırdığımızda beklediğimiz gibi 2 farklı timer, kendileriyle ilgili özel veri yapısınının adresini de taşıyarak callback fonksiyonunu çağırmaktadır.
$ gcc -p posix posix.c -lrt $ ./posix Signal 35 caught on: 1427066563.117 Name: Name 1 Signal 35 caught on: 1427066563.117 Name: Name 2 debug: sleep interrupted by signal (main posix.c:86) Signal 35 caught on: 1427066563.717 Name: Name 1 Signal 35 caught on: 1427066563.717 Name: Name 2 debug: sleep interrupted by signal (main posix.c:86) Signal 35 caught on: 1427066564.317 Name: Name 1 Signal 35 caught on: 1427066564.317 Name: Name 2