3 Şubat 2016 Çarşamba

Sleep

Jiffy Nedir ?
time(7) sayfasında yazdığına göre iki çeşit saat var. Bunlar Software clock ve Hardware clock.

Software clock jiffies cinsinden ölçüm yapıyor. Jiffy Linux'ta sistem açıldığından beri geçen software tickleri gösterir. Linux 2.6 çekirdeğinden itibaren bir jiffy ticki 0.004 saniyeye eşit. (4 mili saniye).
Bir çok sistem çağrısı (sleep, select gibi) software clock ile çalıştığı için her zaman tam istenilen çözünürlüğü sağlamayabilirler.

Hardware clock ise direkt donanım kesmesine bağlı olduğu için çok daha hassatır. Burada donanıma bağlı olan Time Stamp Counter değerini rdtsc komutu ile okuma örneği bulunuyor.

Sleep ve Gerçek Zamanlı Olmayan İşletim Sistemleri
Gerçek zamanlı olmayan işletim sistemlerinde herhangi bir sleep türevi uygulamayı tam istenilen zamanda uyandırmayı garanti etmez!. Aşağıdaki şekilde durum görülebilir.


High-resolution timers
Diğer timerlara göre çok daha yüksek çözünürlük imkanı sağlarlar. High resolution timer'lar jiffy ile kısıtlanmazlar.

High-Resolution Timers
   Before Linux 2.6.21, the accuracy of timer and sleep system calls  (see
   below) was also limited by the size of the jiffy.

   Since  Linux  2.6.21,  Linux  supports  high-resolution  timers (HRTs),
   optionally configurable via CONFIG_HIGH_RES_TIMERS.  On a  system  that
   supports  HRTs,  the  accuracy  of  sleep  and timer system calls is no
   longer constrained by the jiffy, but instead can be as accurate as  the
   hardware  allows  (microsecond accuracy is typical of modern hardware).
   You can determine  whether  high-resolution  timers  are  supported  by
   checking  the resolution returned by a call to clock_getres(2) or look‐
   ing at the "resolution" entries in /proc/timer_list.

   HRTs are not supported on all hardware architectures.  (Support is pro‐
   vided on x86, arm, and powerpc, among others.)

Windows
1. Sleep
#include <window.h> yapıldıktan sonra Sleep() metodu ile milisaniye kadar uyumak mümkün.
#include <Windows.h>

int main()
{
  Sleep(100);
  return 0;
}
Sleep ile getTickCount() beraber kullanılarak ölçüm yapınca verilen süreden biraz daha fazla uyunduğu görülebilir. 500 ms. yerine 515 ms. uyunması gibi.

2. WaitableTimer
Sleep()'ten daha hassas bir zaman kullanabilmemizi sağlar. Zaman 100 nanosaniye çözünürlüğündedir.
#include <windows.h>

void usleep(__int64 usec) //microsecond
{ 
  HANDLE timer; 
  LARGE_INTEGER ft; 

  ft.QuadPart = -(10*usec); //100 nanosecond interval, - value is relative time

  timer = CreateWaitableTimer(NULL, TRUE, NULL); 
  SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); 
  WaitForSingleObject(timer, INFINITE); 
  CloseHandle(timer); 
}

POSIX metodları

1. nanosleep() - Bir sinyal tarafından yarıda kesilebilir
Metodun imzası şöyle
#include <time.h>
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
nanosleep() nanosaniye cinsinden bir süre verilerek kullanılabiliyor. Posix ile gelen yüksek çözünürlüklü bir timer. nanosleep() ile beklemeye başlayan bir thread istenilen süre geçmeden de uyandırılabilir. İstenilen süre kadar uyumak için kullanılması gereken kod parçasını burada bulabilirsiniz. Sinyal tarafından yarıda kesilme ihtimaline karşı while döngüsü içinde kullanılıyor.

#include <time.h>
...
struct timespec sleepTime;
struct timespec time_left_to_sleep;
sleepTime.tv_sec = 0;
sleepTime.tv_nsec = 1000;
while( (sleepTime.tv_sec + sleepTime.tv_nsec) > 0 )
{
   nanosleep(&sleepTime, &time_left_to_sleep);
   sleepTime.tv_sec = time_left_to_sleep.tv_sec;
   sleepTime.tv_nsec = time_left_to_sleep.tv_nsec;
}


2. clock_nanosleep()
Metodun imzası şöyle
#include <time.h>
int clock_nanosleep(clockid_t clock,int flag,const timespec *rqtp,timespec *rmtp);
clock_nanosleep  ile nanosleep gibi uyumak mümkün. Fark olarak clock_nanosleep() çeşitli clock tipleri kullanmamıza imkan tanır.
Like  nanosleep(2), clock_nanosleep() allows the caller to sleep for an
   interval specified with nanosecond precision.  It differs  in  allowing
   the  caller  to select the clock against which the sleep interval is to
   be measured, and in allowing the sleep  interval  to  be  specified  as
   either an absolute or a relative value.

   The clock_id argument [...] can have one of the following values:

   CLOCK_REALTIME   A settable system-wide real-time clock.

   CLOCK_MONOTONIC  A non-settable, monotonically  increasing  clock  that
                    measures time since some unspecified point in the past
                    that does not change after system startup.

   CLOCK_PROCESS_CPUTIME_ID
                    A settable per-process clock that  measures  CPU  time
                    consumed by all threads in the process.
Özellikle ntpd ile değişebilen duvar saatine yani CLOCK_REALTIME saatine göre uyumak için nanosleep() yerine bu metod kullanılmalıdır.

Örneğin 0.5 milisaniye uyumak için şöyle yaparız.
#include <time.h>
main()
{
        struct timespec ts;
        int i;

        ts.tv_sec = 0;
        ts.tv_nsec = 500000;  /* 0.5 milliseconds */
        for (i = 0; i < 1000; i++) {
                clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
        }
}
İkinci parametre 0 olarak geçilirse, şu andan itibaren belirtilen süre kadar uyu anlamına geliyor. Eğer TIMER_ABSTIME (değeri 1 olarak tanımlıdır) kullanılırsa, belirtilen saat gelinceye kadar uyu anlamına geliyor. Yani sabit bir süre hesaplamak yerine, saat veriyoruz.

Aşağıdaki kodda şimdiki zaman alınıp, sonra mutlak zaman hesaplandığı için yukarıdaki örnek ile aynı kapıya çıkar.
struct timespec Time;
clock_gettime(CLOCK_REALTIME, &(Time));

while(1){
    Time.tv_nsec += 416000;
    if(Time.tv_nsec > 999999999){
        (Time.tv_sec)++;
        Time.tv_nsec -= 1000000000;
    }
    clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &(Time), NULL);
    //Do something
}

3. usleep() - Bir sinyal tarafından yarıda kesilirse kalan süreyi alma imkanı yok
usleep() mikrosaniye (saniyenin milyonda biri) cinsinden bir süre verilerek kullanılabiliyor. nanosleep()'ten farklı olarak yarıda kesilirse kalan süreyi alabilme seçeneği yok. Şöyle kodlarız.
#include <unistd.h>

int main()
{
  usleep(100*1000);  /* sleep for 100 milliSeconds */
  return 0;
}
En son Posix standardı sürümünde nanosleep() bu metoda tercih edilmeli denilse de nanosleep'in olmadığı durumlarda kullanılabilir. Burada dikkat edilmesi gereken nokta usleep metodunda şöyle bir cümle geçiyor.

The useconds argument shall be less than one million
Yani usleep metodu ile 1 saniyeden fazla uyuma yapmak mümkün olmayabilir.

4. sleep() - Bir sinyal tarafından yarıda kesilebilir
sleep() saniye cinsinden bir süre verilerek kullanılabiliyor. Eğer 0'dan farklı bir değer dönerse, metod yarıda kesilmiştir ve kalan süreyi belirtir.

sleep (0) yield ile aynı anlama gelir. Yani thread çalışmak için süresi varsa bile, süresinden feragat eder.

5. select
select ile mikrosaniye cinsinden uyuma yapmak ta mümkün. Hatta portable olduğu iddia ediliyor. Şöyle yaparız.
int usleep(long usec)
{
    struct timeval tv;
    tv.tv_sec = usec/1000000L;
    tv.tv_usec = usec%1000000L;
    return select(0, 0, 0, 0, &tv);
}
Şöyle yaparız
select(NULL, NULL, NULL, NULL, 1.0);
Burada EINTR biz sistem çağrısı yaparken, bir sinyal gelip gelmediğini kontrol etmemize yarıyor. Aslında yeni POSIX standardında EINTR gelse bile sistem çağrısı kısmen başarılı olmuş olabilir. Bu yüzden sistem çağrısının sonucuna -1 kontrolü yapmak yerine errno değikenini ayrıca kontrol etmek lazım sanırım.

Programlama Dillerindeki Çağrıları

QT
QThread.sleep() metod ise bildiğimiz sleep() türevlerini çağırmak yerine pthread_cond_timedwait() yöntemini tercih etmiş.
Aşağıdaki kodu buradan aldım.

boost
thread yazısına taşıdım.

C++11
std::this_thread yazısına taşıdım.

C# 
Milisaniye alır. Şöyle kodlarız.
Thread.Sleep(1000);
Thread.Sleep(0) ile program yield eder.

Java
Milisaniye alır. Şöyle kodlarız.
Thread.sleep(interval); 


Hiç yorum yok:

Yorum Gönder