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çin getcontext() 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

results matching ""

    No results matching ""