> POSIX sistemlerinde "sleep" işlemleri: Bir "thread" in akışını belli bir süre bekletmek için POSIX ve türevi sistemlerde "sleep" fonksiyonları bulundurulmaktadır. Bu fonksiyonlar "thread" i bloke ederek çalışma kuyruğundan çıkartır, özel "sleep" kuyruklarına yerleştirir. Vakit dolduğunda da yeniden çalışma kuyruğuna aktarılır. Böylece CPU zamanı harcamadan istenen bekleme sağlanmış olur. Çok eski bir fonksiyondur. Bu fonksiyonlar sırasıyla, "sleep", "usleep", "nanosleep" ve "clock_nanosleep" isimli fonksiyonlardır. >> "sleep" : Saniye cinsinden duyarlılığa sahiptir. Fonksiyonun prototipi aşağıdaki gibidir; #include unsigned sleep(unsigned seconds); Fonksiyon parametre olarak beklenecek sürenin saniye cinsinden değerini alır. Geri dönüş değeri ise, sinyal dolayısıyla gerçekleşen, erken sonlanmalarda kalan saniyeyi belirtir. "0" değeri ile sonlanması ise normal sonlanma olduğunu belirtmektedir. Sinyal gelmesi durumunda ya proses sonlandırılır ya da ilgili "signal_handler" fonksiyonu çağrılır. Fakat bir sinyal geldiğinde "sleep" fonksiyonu hiçbir zaman yeniden başlatılmaz, yani "SA_RESTART" bayrağını kullansak bile. >> "usleep" : Bir POSIX fonksiyonu değildir fakat Linux ve bazı Linux türevi sistemlerde bulunmaktadır. Bu fonksiyon mikrosaniye çözünürlüğe sahiptir. Prototipi aşağıdaki gibidir; #include int usleep(useconds_t usec); Fonksiyon başarı durumunda "0", hata durumunda "-1" değerine geri döner ve "errno" değişkenini uygun değere çeker. Bu fonksiyon da tıpkı "sleep" gibi yeniden başlatılamaz. >> "nanosleep" : Nanosaniye çözünürlüğe sahiptir. Bir POSIX fonksiyonudur. Fonksiyonun prototipi aşağıdaki gibidir; #include int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); Fonksiyonun "timespec" yapısını daha önceki örneklerde de kullanmıştık. Aşağıdaki gibi bir tanımı vardır: struct timespec { time_t tv_sec; /* Seconds */ long tv_nsec; /* Nanoseconds [0, 999'999'999] */ }; Fakat POSIX standartları bu çözünürlüğü garanti etmemektedir çünkü işlemcimizin de nanosaniye mertebesinde çalışabilmesi gerekmektedir. Dolayısıyla işlemci izin verdiği müddetçe nanosaniyeye doğru bir yaklaşma söz konusudur. Fonksiyonun birinci parametresine beklemek istediğimiz süre miktarını, ikinci parametresine bir başarısızlık durumunda geri kalan sürenin yazılacağı adres bilgisini geçeriz. Bu ikinci parametreye "NULL" değerini geçebiliriz. Her iki parametreye aynı nesnenin adresini geçmenin bir sakıncası yoktur. Fonksiyon başarı durumunda "0", hata durumunda "-1" ile geri döner ve "errno" değişkenini uygun değere çeker. * Örnek 1, #include #include #include void exit_sys(const char* msg); int main(void) { /* # OUTPUT # Waiting for 3.5 seconds... :> OK */ printf("Waiting for 3.5 seconds...\n"); struct timespec ts; ts.tv_sec = 3; ts.tv_nsec = 500000000; // 500M nanoseconds = 0.5 seconds if (nanosleep(&ts, NULL) == -1) exit_sys("nanosleep"); printf("OK\n"); return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 2, #include #include #include #include #include #include void sig_handler(int sig); void exit_sys(const char* msg); int main(void) { /* # OUTPUT # Waiting for 3.5 seconds... :> [2] : signal handler OK Time Left: 2 seconds Time Left: 392434000 nanoseconds */ struct sigaction sa; sa.sa_handler = sig_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGINT, &sa, NULL) == -1) exit_sys("sigaction"); printf("Waiting for 3.5 seconds...\n"); struct timespec ts, ts_remaining; ts.tv_sec = 3; ts.tv_nsec = 500000000; // 500M nanoseconds = 0.5 seconds if (nanosleep(&ts, &ts_remaining) == -1 && errno != EINTR) exit_sys("nanosleep"); printf("OK\n"); printf("Time Left: %ju seconds\n", (intmax_t)ts_remaining.tv_sec); printf("Time Left: %ld nanoseconds\n", (intmax_t)ts_remaining.tv_nsec); return 0; } void sig_handler(int sig) { printf("[%d] : signal handler\n", sig); } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } >> "clock_nanosleep" : "nanosleep" fonksiyonu şöyle bir handikaba sahiptir; gerçek zamana göre bekleme yapmasıdır. Yani bekleme esnasında sistem saatinin değişmesi durumunda, bekleme işlemi daha kısa sürebilir. Yani "nanosleep" fonksiyonu sistem saatinin değişmesinden etkilenmektedir. İşte sistem saatindeki değişiklikten etkilenmeyen "clock_nanosleep" fonksiyonu POSIX standardında eklenmiştir. Fonksiyonun prototipi aşağıdaki gibidir; #include int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, struct timespec *rmtp); Fonksiyonun birinci parametresi arka planda kullanılacak saat cinsini belirtmektedir. POSIX standartlarınca bu saat cinsleri şunlardır: -> "CLOCK_REALTIME" : Bekleme işleminin sistem saatindeki değişiklikten etkilenmesini sağlar. Örneğin, 30 saniye beklemek isterken, sistem saatinin ileri alınmasından dolayı, daha az bekleme söz konusu olabilmektedir. -> "CLOCK_MONOTONIC" : Bekleme işleminin sistem saatining değiştirilmesi ve diğer faktörlerden etkilenmemesini sağlar. Bu tip kararlı beklemeler için tercih edilmesi gereken tiptir. -> "CLOCK_PROCESS_CPUTIME_ID" : İşlemci zamanının ölçülmesinde kullanılır. Genel olarak bu tip, "timer tick" ler ile birlikte kullanılır. Bu belli bir prosesin bütün "thread" lerinin harcadığı CPU zamanının ölçülmesinde kullanılır. Yani prosesin "sleep" gibi uykuda beklediği veya blokede beklediği zamanlar hesapa dahil edilmeyecektir. -> "CLOCK_THREAD_CPUTIME_ID" : "thread" zamanının ölçülmesinde kullanılır. Genel olarak bu tip, "timer tick" ler ile birlikte kullanılır. Bu belli bir "thread" in harcadığı CPU zamanının ölçülmesinde kullanılır. Yani prosesin "sleep" gibi uykuda beklediği veya blokede beklediği zamanlar hesapa dahil edilmeyecektir. -> "clock_getcpuclockid()" : BURAYA TEKRAR GELİNECEKTİR. -> "pthread_getcpuclockid(): BURAYA TEKRAR GELİNECEKTİR. Fonksiyonun ikinci parametresi şu değerlerden birisini alır: -> "TIMER_ABSTIME" : Bu durumda bekleme zamanı göreli değil, mutlak olacaktır. Dolayısıyla bekleme miktarını belirtilen "timespec" yapısında beklemenin sonlandırılacağı mutlak zaman bilgisi bulunmalıdır. Mutlak zamanlı beklemeleri "thread" konusundaki zaman aşımlı senkronizasyon nesnelerini incelerken görmüştük. -> "0" : Beklemenin göreli olduğunu belirtir. Fonksiyonun üçüncü parametresi bekleme zamanını, dördüncü parametre ise fonksiyonun hata durumunda sonlanması durumunda geriye kalan zamanı belirtmektedir. Bu son parametreye "NULL" değerini geçebiliriz. Fonksiyonumuz hata durumunda hata kodunun kendisiyle, başarı durumunda "0" ile geri döner. * Örnek 1, #include #include #include #include #include #include void sig_handler(int sig); void exit_sys(const char* msg); int main(void) { /* # OUTPUT # Waiting for 3.5 seconds... :> [2] : signal handler OK Time Left: 1 seconds Time Left: 184699300 nanoseconds */ struct sigaction sa; sa.sa_handler = sig_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGINT, &sa, NULL) == -1) exit_sys("sigaction"); printf("Waiting for 3.5 seconds...\n"); struct timespec ts, ts_remaining; ts.tv_sec = 3; ts.tv_nsec = 500000000; // 500M nanoseconds = 0.5 seconds if (clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts_remaining) == -1 && errno != EINTR) exit_sys("nanosleep"); printf("OK\n"); printf("Time Left: %ju seconds\n", (intmax_t)ts_remaining.tv_sec); printf("Time Left: %ld nanoseconds\n", (intmax_t)ts_remaining.tv_nsec); return 0; } void sig_handler(int sig) { printf("[%d] : signal handler\n", sig); } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } > Şimdiki zamanın elde edilmesi: Şu andaki zamanı almak, zaman ölçümü yapmak vb. işler için halihazırda Standart C fonksiyonları bulundurulmuştur. Ancak bu fonksiyonlar genel olarak düşük bir çözünürlülüğe sahiptir. Bu fonksiyonlar, "time", "localtime", "gmtime", "ctime" ve "asctime", "mktime", "difftime", "strftime", "clock". isimli fonksiyonlardır. Bunlardan, >> "time" : "epoch" dan geçen zamanı saniye sayısını "time_t" türünden veren bir fonksiyondur. Prototipi şöyledir: #include time_t time(time_t *tloc); Fonksiyona "NULL" değerini geçersek, zaman bilgisini geri dönüş değeri olarak döndürür. Aksi halde adresini geçtiğimiz nesneye yazar. Buradaki "time_t" türü C standartlarına göre herhangi bir nümerik tür(`float`, "double", "int" vs.) olabilir. Fakat POSIX standartlarına göre tam sayı türü olması gerekmektedir. Diğer taraftan C standartlarında "epoch" belirtilmemişken, POSIX'e göre "epoch" noktası "01.01.1970" olarak belirlenmiştir. Zaten çoğu C derleyicisi de "epoch" olarak bu tarihi almaktadır. >> "localtime" : Fonksiyonun prototipi şöyledir: #include struct tm *localtime(const time_t *timer); Fonksiyon argüman olarak aldığı "time_t" türünden yapıyı bileşenlerine ayrıştırır ve "struct tm" biçiminde geri döndürür. "struct tm" yapısının bileşenleri şöyledir: struct tm { int tm_sec; // Seconds [0,60]. int tm_min; // Minutes [0,59]. int tm_hour; // Hour [0,23]. int tm_mday; // Day of month [1,31]. int tm_mon; // Month of year [0,11]. int tm_year; // Years since 1900. int tm_wday; // Day of week [0,6] (Sunday =0). int tm_yday; // Day of year [0,365]. int tm_isdst; // Daylight Savings flag. }; >> "gmtime" : Bu fonksiyon ise tarih ve zamanı UTC(eski adıyla GMT) olarak geri döndüren fonksiyondur. Yani "localtime" fonksiyonunnu UTC biçiminde döndüren halidir. Özünde her iki fonksiyon da aynı şeyi yapmaktadır. Fonksiyonun prototipi aşağıdaki gibidir: #include struct tm *gmtime(const time_t *timer); >> "ctime" ve "asctime" : Tarih ve zaman bilgisini bize doğrudan bir yazı biçiminde vermektedir. İkisi arasındaki tek fark aldığı argümanların türlerinin farklı olmasıdır. Fonksiyonların prototipleri aşağıdaki gibidir: #include char *ctime(const time_t *clock); char *asctime(const struct tm *timeptr); >> "mktime" : Argüman olarak verdiğimiz "timepoint" ile "epoch" arasında geçen saniyeyi bize döndürür. Fonksiyonun prototipi şöyledir: #include time_t mktime(struct tm *timeptr); >> "difftime" : C Standartlarına göre "epoch" ve "time_t" türü açıkça belirtilmediğinden, iki "time_t" arasındaki farkı almak için, böyle bir fonksiyon geliştirilmiştir. POSIX standartlarında bu fonksiyonun kullanılmasına gerek yoktur çünkü "epoch" ve "time_t" zaten tanımlıdır. Fonksiyonun prototipi aşağıdaki gibidir: #include double difftime(time_t time1, time_t time0); >> "strftime" : Adeta "snprintf" fonksiyonu gibi, tarih-zaman üzerinde formatlama yapmaktadır. Böylece tarih-zaman bilgisini istediğimiz gibi yazdırabiliriz. Fonksiyonun prototipi şöyledir: #include size_t strftime(char * s, size_t maxsize, const char * format, const struct tm * timeptr); Kullanılan formatlama karakterleri için fonksiyonun dökümantasyonuna bakınız. >> "clock" : Anımsanacağı üzere "time" fonksiyonu bize saniye bilgisini vermektedir fakat C Standartlarına göre "epoch" ve "time_t" bilgisi tanımlı değildir, yani derleyiciye bağlıdır. Dolayısıyla "time" fonksiyonu kullanarak, taşınabilir bir şekilde, iki "timepoint" arasında geçen süreyi hesaplamamız mümkün değildir. İşte bu problemi gidermek için "clock" fonksiyonu geliştirilmiştir. Fonksiyonun prototipi aşağıdaki gibidir: #include clock_t clock(void); Fonksiyonun geri dönüş türü "clock_t" türü aslında bir "time-tick" sayısını belirtmektedir ve "clock_t" türü ise C standartlarınca ve POSIX standartlatınca nümerik(tam sayı ya da gerçek sayı) bir tür olabilir. Ancak bir saniye için kaç adet "time-tick" gerektiği ise "CLOCKS_PER_SEC" sembolik sabiti ile tanımlanmıştır. "CLOCKS_PER_SEC" sembolik sabiti, -> Güncel Linux sistemlerinde bir milyon olarak tanımlanmıştır. Dolayısıyla Linux sistemlerindeki duyarlılık mikrosaniye mertebesindedir. -> Güncel Windows sistemlerinde ise bin olarak tanımlanmıştır. Dolayısıyla Windows sistemlerinde milisaniye mertebesinde duyarlılık vardır. O halde programın farklı iki noktasında bu fonksiyonu çağırıp, daha sonra geri dönüş değerlerinin farkını alıp ve en sonunda da sonucu iş bu sembolik sabite bölersek bahsi geçen iki nokta arasında geçen saniyeyi hesaplamış oluruz. Takribi olarak şöyledir; clock_t start, stop; //... start = clock(); //... stop = clock(); // Burada sonucu "double" türüne dönüştürmemiz önemli. // Aksi halde küsüratlar atılacaktır. double result = (double)(stop - start) / CLOCKS_PER_SEC; Bunlara ek olarak zaman ölçülmesinde kullanılan POSIX fonksiyonları da vardır. Bunlar, "clock_gettime", "clock_getres", "clock_settime", "times" isimli fonksiyonlardır. Bunlardan, >> "clock_gettime" : Fonksiyonun prototipi aşağıdaki gibidir: #include int clock_gettime(clockid_t clock_id, struct timespec *tp); Birinci parametremiz zaman ölçümünde kullanılacak saatin türünü almaktadır. Bu tür, "CLOCK_REALTIME", "CLOCK_MONOTONIC", "CLOCK_PROCESS_CPUTIME_ID", "CLOCK_THREAD_CPUTIME_ID", "clock_getcpuclockid()", "pthread_getcpuclockid() değerlerinden birisi olabilir. Fonksiyonun ikinci parametresi "timespec" yapı nesnesinin adresini alır. Zaman bilgisi bu adrese yerleştirilir. Fonksiyon başarı durumunda "0", hata durumunda "-1" değerine geri döner ve "errno" değişkenini uygun değere çeker. * Örnek 1, #include #include #include void exit_sys(const char* msg); int main(void) { /* # OUTPUT # Elapsed Time in nanoseconds: 1609367200 Elapsed Time in seconds : 1.609367 */ struct timespec ts_start, ts_end; if (clock_gettime(CLOCK_MONOTONIC, &ts_start) == -1) exit_sys("clock_gettime"); for (int i = 0; i < 1000000000; ++i) ; if (clock_gettime(CLOCK_MONOTONIC, &ts_end) == -1) exit_sys("clock_gettime"); long long elapsed_time = (ts_end.tv_sec * 1000000000LL + ts_end.tv_nsec) - (ts_start.tv_sec * 1000000000LL + ts_start.tv_nsec); printf("Elapsed Time in nanoseconds: %lld\n", elapsed_time); printf("Elapsed Time in seconds : %f\n", elapsed_time / 1000000000.); return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 2, #include #include #include #include void do_work(); void do_work_in_detail(); void exit_sys(const char* msg); int main(void) { /* # OUTPUT # Elapsed Time in nanoseconds: 13187545600 Elapsed Time in seconds : 13.187546 Elapsed Time in nanoseconds: 8058837800 Elapsed Time in seconds : 8.058838 */ do_work(); puts("\n"); do_work_in_detail(); return 0; } void do_work() { struct timespec ts_start, ts_end; if (clock_gettime(CLOCK_MONOTONIC, &ts_start) == -1) exit_sys("clock_gettime"); for (int j = 0; j < 5; ++j) { for (int i = 0; i < 1000000000; ++i) ; sleep(1); } if (clock_gettime(CLOCK_MONOTONIC, &ts_end) == -1) exit_sys("clock_gettime"); long long elapsed_time = (ts_end.tv_sec * 1000000000LL + ts_end.tv_nsec) - (ts_start.tv_sec * 1000000000LL + ts_start.tv_nsec); printf("Elapsed Time in nanoseconds: %lld\n", elapsed_time); printf("Elapsed Time in seconds : %f\n", elapsed_time / 1000000000.); } void do_work_in_detail() { struct timespec ts_start, ts_end; if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts_start) == -1) exit_sys("clock_gettime"); for (int j = 0; j < 5; ++j) { for (int i = 0; i < 1000000000; ++i) ; sleep(1); } if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts_end) == -1) exit_sys("clock_gettime"); long long elapsed_time = (ts_end.tv_sec * 1000000000LL + ts_end.tv_nsec) - (ts_start.tv_sec * 1000000000LL + ts_start.tv_nsec); printf("Elapsed Time in nanoseconds: %lld\n", elapsed_time); printf("Elapsed Time in seconds : %f\n", elapsed_time / 1000000000.); } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } >> "clock_getres" : Her ne kadar "clock_gettime" fonksiyonu nanosaniye mertebesinde bir duyarlılığa sahip olsa bile gerçekte duyarlılık öyle olmayabilir. Çünkü duyarlılık işlemcinin o anki durumuna, işletim sisteminin çekirdek yapısı gibi faktörlere bağlı olarak değişkenlik gösterebilir. İşte duyarlılığın ne derece olduğu bilgisini "clock_getres" fonksiyonu ile öğrenebiliriz. Fonksiyonun prototipi aşağıdaki gibidir: #include int clock_getres(clockid_t clock_id, struct timespec *res); Fonksiyonun birinci parametresi duyarlılığı ölçülecek olan saat türünü belirtmektedir. İkinci parametre ise duyarlılık bilgisinin yerleştirileceği "timespec" yapısının adresini alır. * Örnek 1, Bir nanosaniye mertebesinde duyarlılığa sahip olduğu görülmüştür. #include #include #include void exit_sys(const char* msg); int main(void) { struct timespec ts; if (clock_getres(CLOCK_MONOTONIC, &ts) == -1) exit_sys("clock_gettime"); long long result = (ts.tv_sec * 1000000000LL + ts.tv_nsec); printf("Result: %lld\n", result); // Result: 1 return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } >> "clock_settime" : Söz konusu saat türünü "set" etmek için kullanılır ancak çağıran prosesin uygun önceliğe sahip olması gerekmektedir. Öte yandan her saat türü "set" EDİLEMEYEBİLİR. Fonksiyonun prototipi aşağıdaki gibidir: #include int clock_settime(clockid_t clock_id, const struct timespec *tp); >> "times" : Bir prosesin "user-mode" ve "kernel-mode" olarak çalışırken harcadığı zamanlar, işletim sistemi tarafından, o prosesin kontrol bloğunda saklanır. İşte saklanan bu bilgileri bu fonksiyonla elde edebiliriz. Yine bir POSIX fonksiyonudur. Fonksiyonun prototipi aşağıdaki gibidir: #include clock_t times(struct tms *buffer); Fonksiyonumuz "tms" türünden bir yapı nesnesinin adresini alarak, çağrıldığı ana kadar geçen zamanı bu adrese yerleştirir. "tms" yapı nesnesi aşağıdaki gibidir: struct tms { clock_t tms_utime; // Prosesin "user-mode" da harcadığı zaman. clock_t tms_stime; // Prosesin "kernel-mode" da harcadığı zaman. clock_t tms_cutime; // "wait" fonksiyonlarıyla beklenen bütün alt proseslerin "user-mode" da harcadığı zaman. clock_t tms_cstime; // "wait" fonksiyonlarıyla beklenen bütün alt proseslern "kernel-mode" da harcadığı zaman. }; Fonksiyonun geri dönüş değeri; -> Başarı durumunda, belli bir orjinden geçen gerçek zamanı belirten bir değerdir ancak bu değer tek başına bir anlam ifade etmeyecektir. -> Hata durumunda "-1" değerine geri döner ve "errno" değişkenini uygun değere çeker. İşte kabuk komutu olan "time" komutu bu fonksiyonu çağırmaktadır. * Örnek 1, Aşağıdaki programı "./main "ls -l" komutuyla çalıştırınız. #include #include #include #include #include void exit_sys(const char* msg); int main(int argc, char* argv[]) { /* # OUTPUT # Sleep in 2 seconds... :> Start working... :> total 24 -rw-r--r-- 1 ahmopasa ahmopasa 50 Feb 25 21:28 log.txt -rwxr-xr-x 1 ahmopasa ahmopasa 16384 Feb 27 10:06 main -rw-r--r-- 1 ahmopasa ahmopasa 979 Feb 27 10:06 main.c Itself: User Mode: 160 Kernel Mode: 0 Childs: User Mode: 0 Kernel Mode: 0 */ if (argc != 2) { fprintf(stderr, "wrong number of arguments\n"); exit(EXIT_FAILURE); } printf("Sleep in 2 seconds...\n"); sleep(2); printf("Start working...\n"); for (int i = 0; i < 1000000000; ++i) ; if (system(argv[1]) == -1) exit_sys("system"); struct tms ts; if (times(&ts) == -1) exit_sys("times"); puts("Itself:"); printf("User Mode: %jd\n", (intmax_t)ts.tms_utime); printf("Kernel Mode: %jd\n", (intmax_t)ts.tms_stime); puts("Childs:"); printf("User Mode: %jd\n", (intmax_t)ts.tms_cutime); printf("Kernel Mode: %jd\n", (intmax_t)ts.tms_cstime); return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 2.0, Komut satırından "./main ./sample" çağırarak "./sample" programının süresini ölçebiliriz. // ./sample int main() { for (int i = 0; i < 1000000000; ++i) ; } // ./mample #include #include #include #include #include #include void exit_sys(const char* msg); int main(int argc, char* argv[]) { /* # OUTPUT # Itself: User Mode: 0.000000 Kernel Mode: 0.000000 Childs: User Mode: 0.000163 Kernel Mode: 0.000000 */ if (argc != 2) { fprintf(stderr, "wrong number of arguments\n"); exit(EXIT_FAILURE); } if (system(argv[1]) == -1) exit_sys("system"); struct tms ts; if (times(&ts) == -1) exit_sys("times"); puts("Itself:"); printf("User Mode: %f\n", (double)ts.tms_utime / CLOCKS_PER_SEC); printf("Kernel Mode: %f\n", (double)ts.tms_stime / CLOCKS_PER_SEC); puts("Childs:"); printf("User Mode: %f\n", (double)ts.tms_cutime / CLOCKS_PER_SEC); printf("Kernel Mode: %f\n", (double)ts.tms_cstime / CLOCKS_PER_SEC); return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 2.1, // ./sample int main() { for (int i = 0; i < 1000000000; ++i) ; } // ./mample #include #include #include #include #include #include #include void exit_sys(const char* msg); int main(int argc, char* argv[]) { /* # OUTPUT # Itself: User Mode: 0.000000 Kernel Mode: 0.000000 Childs: User Mode: 0.000166 Kernel Mode: 0.000000 */ if (argc < 2) { fprintf(stderr, "wrong number of arguments\n"); exit(EXIT_FAILURE); } pid_t pid; if ((pid = fork()) == -1) exit_sys("fork"); if (pid == 0 && execvp(argv[1], &argv[1]) == -1) _exit(EXIT_FAILURE); if (wait(NULL) == -1) exit_sys("wait"); struct tms ts; if (times(&ts) == -1) exit_sys("times"); puts("Itself:"); printf("User Mode: %f\n", (double)ts.tms_utime / CLOCKS_PER_SEC); printf("Kernel Mode: %f\n", (double)ts.tms_stime / CLOCKS_PER_SEC); puts("Childs:"); printf("User Mode: %f\n", (double)ts.tms_cutime / CLOCKS_PER_SEC); printf("Kernel Mode: %f\n", (double)ts.tms_cstime / CLOCKS_PER_SEC); return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } > "Interval Timer Processes" : Bazen belli periyotta sürekli işlemlerin yapılması gerekebilmektedir. Örneğin, saniyede bir olacak şekilde bir fonksiyon çağrılması. Bu mekanizmayı oluşturmanın bir yolları şunlardır; -> Bir "thread" oluşturmak, bu "thread" içerisinde bir döngü yazmak ve "clock_nanosleep" gibi bir fonksiyonla bekleme yapmak. Ancak bunun için "thread" e ihtiyaç duymaktayız. -> "setitimer" isimli POSIX fonksiyonlarını kullanmak. Şimdi de bu "setitimer" fonksiyonunu inceleyelim: >> "setitimer" : Bu fonksiyon ile iş bu mekanizma basit bir biçimde oluşturulabilmektedir. Ancak bu fonksiyon "deprecated" edilmiştir. Fonksiyonun prototipi aşağıdaki gibidir: #include int setitimer(int which, const struct itimerval * value, struct itimerval * ovalue); Fonksiyonun birinci parametresi "Interval Timer" mekanizmasına ilişkin bir tür bilgisidir. Bu tür bilgisine göre de değişik sinyaller gönderilir. Bu tür bilgisi aşağıdakilerden birisini alır: -> ITIMER_REAL: Gerçek zamana dayalı ölçüm için kullanılır. Zaman dolduğunda "SIGALRM" sinyali gönderilir. -> ITIMER_VIRTUAL: Prosesin çalışmasına göre ölçüm yapılır. Yani proses çalışmadığı sürece saat işlememektedir. Dolayısıyla uykuda geçen zaman dikkate alınmamaktadır. Zaman dolduğunda "SIGVTALRM" sinyali gönderilir. -> ITIMER_PROF: Prosesin çalışmasına göre ölçüm yapılır. Ancak prosesin uykuda geçirdiği zamanlar bu sefer dikkate alınır. Zaman dolduğunda "SIGPROF" sinyali gönderilir. Fonksiyonun ikinci parametresi "itimerval" yapı türünden türündendir. Bu yapı aşağıdaki gibidir: struct itimerval { struct timeval it_interval; // Periyodik işleme başlamak için gereken zaman bilgisi. struct timeval it_value; // Periyot zamanının bilgisi }; Ancak yapının elemanlarının "timeval" türünden olduğuna dikkat ediniz. Bu yapı ise aşağıdaki gibidir: struct timeval { time_t tv_sec; // Seconds. suseconds_t tv_usec; // Microseconds. }; Fonksiyonun üçüncü parametresi ise bir önceki "setitimer" çağrısıyla "set" edilen bilginin elde edilmesi için kullanılır, "NULL" değeri geçilebilir. Fonksiyon başarı durumunda "0", hata durumunda "-1" değerine geri döner ve "errno" değişkenini uygun değere çeker. * Örnek 1, #include #include #include #include #include void sig_handler(int sig); void exit_sys(const char* msg); int main(void) { /* # OUTPUT # :> sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... ... */ struct sigaction sa; sa.sa_handler = sig_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(SIGALRM, &sa, NULL) == -1) exit_sys("sigaction"); struct itimerval itval; itval.it_value.tv_sec = 5; // İlk periyoda kadar beş saniye geçecek. itval.it_value.tv_usec = 0; itval.it_interval.tv_sec = 1; // Her bir saniyede bir tetiklenecek. itval.it_interval.tv_usec = 0; if (setitimer(ITIMER_REAL, &itval, NULL) == -1) exit_sys("setitimer"); // Asenkron çalışma olduğundan, "main thread" i bekletmeliyiz. for(;;) pause(); return 0; } void sig_handler(int sig) { printf("sig_handler...\n"); } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 2, Pekala mekanizmayı durdurmak da mümkündür. #include #include #include #include #include void sig_handler(int sig); void exit_sys(const char* msg); int g_count; int main(void) { /* # OUTPUT # :> sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... The signal has been called 10 time until now. */ struct sigaction sa; sa.sa_handler = sig_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(SIGALRM, &sa, NULL) == -1) exit_sys("sigaction"); struct itimerval itval; itval.it_value.tv_sec = 5; // İlk periyoda kadar beş saniye geçecek. itval.it_value.tv_usec = 0; itval.it_interval.tv_sec = 1; // Her bir saniyede bir tetiklenecek. itval.it_interval.tv_usec = 0; if (setitimer(ITIMER_REAL, &itval, NULL) == -1) exit_sys("setitimer"); // Asenkron çalışma olduğundan, "main thread" i bekletmeliyiz. for(;;) { if (g_count == 10) { // Mekanizma "disable" edilecektir. itval.it_value.tv_sec = 0; itval.it_value.tv_usec = 0; itval.it_interval.tv_sec = 0; itval.it_interval.tv_usec = 0; if (setitimer(ITIMER_REAL, &itval, NULL) == -1) exit_sys("setitimer"); break; } else { pause(); } } printf("The signal has been called %d time until now.\n", g_count); return 0; } void sig_handler(int sig) { printf("sig_handler...\n"); ++g_count; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } Fakat bu fonksiyonun "deprecated" edildiğini söylemiştik. Çünkü şu dezavantajlara sahiptir; -> Haberdar etme mekanizması sadece sinyaller ile olması. Dolayısıyla "thread" kullanarak haberdar edilebilir olmamaları. -> Kullanılan sinyallerin Gerçek Zamanlı sinyaller olmamasından dolayı biriktirilebilir olmamaları, ekstra bilginin iletilememeleri vb. -> Zaman duyarlılığının mikrosaniye düzeyinde olmasıdır. İşte bu dezavantajları egale etmek için POSIX standartlarına bir takım fonksiyonlar eklenmek suretiyle bir mekanizma kurulmuştur. Fakat bu mekanizmanın kullanılması, "setitimer" fonksiyonunun kullanılmasına nazaran daha zahmetlidir. Kullanım biçimi şöyledir: -> İlk önce "timer_create" isimli fonksiyonu çağırarak bir adet "Internal Timer" nesnesi oluşturulur. -> Daha sonra "timer_settime" fonksiyonu ile periyot belirlenmelidir ve çalışmaya başlarız. -> En sonunda "timer_delete" fonksiyonu ile yukarıda oluşturduğumuz "Internal Timer" nesnesini yok ederiz. Bu fonksiyonlardan, >> "timer_create" : Fonksiyonun prototipi aşağıdaki gibidir. #include #include int timer_create(clockid_t clockid, struct sigevent * evp, timer_t * timerid); Fonksiyonun birinci parametresi kullanılacak saat türüdür. Burada kullanılacak saat türü daha evvel gördüğümüz "CLOCK_REALTIME", "CLOCK_MONOTONIC", "CLOCK_PROCESS_CPUTIME_ID", "CLOCK_THREAD_CPUTIME_ID", "clock_getcpuclockid()", "pthread_getcpuclockid()" değerlerinden birisi olabilir. Fonksiyonun ikinci parametresi "sigevent" yapı türünden bir nesne adresi almaktadır. Bu yapı aşağıdaki gibi tanımlanmıştır: struct sigevent { int sigev_notify; // Notification type. int sigev_signo; // Signal number. union sigval sigev_value; // Signal value. void (*sigev_notify_function)(union sigval); // Notification function. pthread_attr_t *sigev_notify_attributes; // Notification attributes. }; Bu yapı türünden bir nesneyi bizler doldurduktan sonra fonksiyona geçmeliyiz. Dolayısıyla bu yapının, -> "sigev_notify" : Periyot tamamlandığında haberdar edilmenin nasıl yapıldığını belirtmek içindir ve şu değerlerden birisini yerleştirebiliriz; -> "SIGEV_NONE" : Haberdar edilme yapılmayacaktır. -> "SIGEV_SIGNAL" : Haberdar edilme sinyal yoluyla yapılacaktır. -> "SIGEV_THREAD" : Haberdar edilme, bu mekanizma tarafından oluşturulan, bir "thread" yoluyla yapılacaktır. Fakat toplamda bu iş için kaç adet "thread" oluşturulacağı POSIX standartlarınca işletim sistemini yazanların isteğine bırakılmıştır. -> "sigev_signo" : Haberdar edilme eğer sinyal yoluyla yapılacaksa, oluşturulacak sinyalin numarasıdır. Bu numara normal bir sinyale ait olabileceği gibi Gerçek Zamanlı bir sinyale de ait olabilir. Eğer normal sinyal kullanılırsa bir biriktirme yapılamayacağı için, ilgili proses bloke edildiğinde kaç periyot geçtiğini anlayamayız. İşte bunun için POSIX standartlarında "timer_getoverrun" isimli bir fonksiyon bulundurulmuştur. Bu fonksiyon sayesinde sinyalden dolayı bloke oluştuğunda kaç periyot geçtiği bilgisini öğrenebiliriz. -> "sigev_value" : Gerçek Zamanlı sinyallerde sinyale iliştirilecek bilgiyi belirtmektedir. Bu eleman aynı zamanda "thread" ile haberdar etme yönteminde de kullanılır. Bu tür de bir yapı türü olup, aşağıdaki gibi tanımlanmıştır: union sigval { int sival_int; // Integer signal value. void *sival_ptr; // Pointer signal value. }; -> "sigev_notify_function" : Eğer haberdar edilme "thread" yoluyla yapılacaksa, "thread" in çağıracağı fonksiyonu belirtmektedir. -> "pthread_attr_t" : "thread" oluştururken "thread" e ilişkin bir takım özellikleri "set" etmek için kullanılır. "NULL" geçilmesi halinde varsayılan özelliklerle "thread" oluşturulur. Fonksiyonun ikinci parametresine "NULL" değerini geçebiliriz. Bu durumda haberdar edilme sinyal yoluyla yapılacaktır ve varsayılan sinyal gönderilecektir. Fakat POSIX standartlarına göre varsayılan sinyalin ne olduğu belirtilmemiştir. Linux sistemlerinde bu "SIGALRM" sinyalidir. Fonksiyonun üçüncü parametresi ise oluşturulan "Interval Timer" nesnesinin ID değerinin yerleştirileceği adres bilgisidir. Fonksiyonun geri dönüş değeri başarı durumunda "0", hata durumunda "-1" olur ve "errno" değişkeni uygun değere çekilir. >> "timer_settime" : Fonksiyonun prototipi aşağıdaki gibidir. #include int timer_settime(timer_t timerid, int flags, const struct itimerspec * value, struct itimerspec * ovalue); Fonksiyonun birinci parametresi, "timer_create" fonksiyonunun son parametresine geçtiğimiz nesne olmalıdır. Fonksiyonun ikinci parametresine ya "0" ya da "TIMER_ABSTIME" değerlerinden birini geçmeliyiz. "0" değeri geçilirse göreli zaman, "TIMER_ABSTIME" geçilirse de mutlak zaman dikkate alınır. Genellikle göreli zaman kullanılır, yani "0" değeri geçilir. Fonksiyonun üçüncü parametresi ilk haberdar edilmenin ve periyodik haberdar edilmenin ayarlandığı parametredir. Bu parametre "itimerspec" yapı türünden olup, programcı tarafından doldurulduktan sonra fonksiyona gönderilmelidir. Aşağıdaki gibi tanımlanmıştır: struct itimerspec { struct timespec it_interval; Timer period. struct timespec it_value; Timer expiration. }; Yapının, -> "it_value" : İlk haberdar edilmeye kadar geçecek zamanı belirtir. -> "it_interval" : Haberdar edilme periyodunu belirtir. Bu yapının elemanları ise "timespec" türünden olup, aşağıdaki gibi tanımlanmıştır: struct timespec { time_t tv_sec; /* Seconds. */ long tv_nsec; /* Nanoseconds. */ }; Fonksiyonun son parametresi ise eski değerleri "get" etmek için kullanılır. Bu parametre "NULL" geçilebilir. Fonksiyonun geri dönüş değeri ise başarı durumunda "0", hata durumunda ise "-1" olur ve "errno" uygun değere çekilir. >> "timer_delete" : Fonksiyonun prototipi aşağıdaki gibidir. #include int timer_delete(timer_t timerid); Fonksiyonun parametresi "timer_create" ile oluşturduğumuz, "timer_settime" ile kullandığımız o nesnedir. Başarı durumunda "0", hata durumunda "-1" ile geri döner. Fakat normal şartlarda bu fonksiyonun geri dönüş değerinin kontrolüne gerek yoktur. Aşağıda bu mekanizmanın kullanılmasına ilişkin örnekler verilmiştir: * Örnek 1, Aşağıdaki örnekte oluşturulan "Interval Timer" nesnesi, zaten program sonlanacağı için, yok edilmemiştir. #include #include #include #include #include void sig_handler(int signo, siginfo_t* info, void* context); void exit_sys(const char* msg); int main(void) { /* # OUTPUT # Ok :> sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... sig_handler... ... */ struct sigaction sa; sa.sa_sigaction = sig_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_SIGINFO; if (sigaction(SIGRTMIN, &sa, NULL) == -1) exit_sys("sigaction"); struct sigevent se; se.sigev_notify = SIGEV_SIGNAL; se.sigev_signo = SIGRTMIN; se.sigev_value.sival_int = 31; timer_t it; if (timer_create(CLOCK_MONOTONIC, &se, &it) == -1) exit_sys("timer_create"); struct itimerspec ts; ts.it_value.tv_sec = 5; ts.it_value.tv_nsec = 0; ts.it_interval.tv_sec = 1; ts.it_interval.tv_nsec = 0; if (timer_settime(it, 0, &ts, NULL) == -1) exit_sys("timer_settime"); puts("Ok"); for (;;) pause(); return 0; } void sig_handler(int signo, siginfo_t* info, void* context) { printf("sig_handler...\n"); } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 2, Aşağıdaki örnekte oluşturulan "Interval Timer" nesnesi, zaten program sonlanacağı için, yok edilmemiştir. #include #include #include #include #include #include void sig_handler(int signo, siginfo_t* info, void* context); void exit_sys(const char* msg); jmp_buf g_jumper; int g_counter; int main(void) { /* # OUTPUT # Starting Continues :> sig_handler... Continues sig_handler... Continues sig_handler... Continues sig_handler... Continues sig_handler... Continues sig_handler... Continues sig_handler... Continues sig_handler... Continues sig_handler... Continues sig_handler... Continues sig_handler... Finished */ struct sigaction sa; sa.sa_sigaction = sig_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_SIGINFO; if (sigaction(SIGRTMIN, &sa, NULL) == -1) exit_sys("sigaction"); struct sigevent se; se.sigev_notify = SIGEV_SIGNAL; se.sigev_signo = SIGRTMIN; se.sigev_value.sival_int = 31; timer_t it; if (timer_create(CLOCK_MONOTONIC, &se, &it) == -1) exit_sys("timer_create"); struct itimerspec ts; ts.it_value.tv_sec = 5; ts.it_value.tv_nsec = 0; ts.it_interval.tv_sec = 1; ts.it_interval.tv_nsec = 0; if (timer_settime(it, 0, &ts, NULL) == -1) exit_sys("timer_settime"); puts("Starting"); for (;;) { if (setjmp(g_jumper) == 1) { break; } else { puts("Continues"); pause(); } } puts("Finished"); return 0; } void sig_handler(int signo, siginfo_t* info, void* context) { printf("sig_handler...\n"); if (g_counter++ == 10) { longjmp(g_jumper, 1); } } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 3, Aşağıdaki örnekte ise hem sinyalle birlikte bir bilgi iliştirilmiş hem de ilgili nesne de yok edilmiştir. #include #include #include #include #include #include void sig_handler(int signo, siginfo_t* info, void* context); void exit_sys(const char* msg); jmp_buf g_jumper; int g_counter; int main(void) { /* # OUTPUT # Starting Continues :> [31] : sig_handler Continues [31] : sig_handler Continues [31] : sig_handler Continues [31] : sig_handler Continues [31] : sig_handler Continues [31] : sig_handler Continues [31] : sig_handler Continues [31] : sig_handler Continues [31] : sig_handler Continues [31] : sig_handler Continues Finished */ struct sigaction sa; sa.sa_sigaction = sig_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_SIGINFO; if (sigaction(SIGRTMIN, &sa, NULL) == -1) exit_sys("sigaction"); struct sigevent se; se.sigev_notify = SIGEV_SIGNAL; se.sigev_signo = SIGRTMIN; se.sigev_value.sival_int = 31; timer_t it; if (timer_create(CLOCK_MONOTONIC, &se, &it) == -1) exit_sys("timer_create"); struct itimerspec ts; ts.it_value.tv_sec = 5; ts.it_value.tv_nsec = 0; ts.it_interval.tv_sec = 1; ts.it_interval.tv_nsec = 0; if (timer_settime(it, 0, &ts, NULL) == -1) exit_sys("timer_settime"); puts("Starting"); for (;;) { if (setjmp(g_jumper) == 1) { break; } else { puts("Continues"); pause(); } } timer_delete(it); puts("Finished"); return 0; } void sig_handler(int signo, siginfo_t* info, void* context) { if (g_counter++ == 10) { longjmp(g_jumper, 1); } printf("[%d] : sig_handler\n", info->si_int); } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 4, Aşağıda da "thread" yoluyla haberdar edilme yapılmıştır. #include #include #include #include #include #include void thread_handler(union sigval sval); void exit_sys(const char* msg); int main(void) { /* # OUTPUT # Press ENTER to exit!... :> [69] : thread_handler... [69] : thread_handler... [69] : thread_handler... [69] : thread_handler... [69] : thread_handler... [69] : thread_handler... ... Finished */ struct sigevent se; se.sigev_notify = SIGEV_THREAD; se.sigev_notify_function = thread_handler; se.sigev_notify_attributes = NULL; se.sigev_value.sival_int = 69; timer_t it; if (timer_create(CLOCK_MONOTONIC, &se, &it) == -1) exit_sys("timer_create"); struct itimerspec ts; ts.it_value.tv_sec = 5; ts.it_value.tv_nsec = 0; ts.it_interval.tv_sec = 1; ts.it_interval.tv_nsec = 0; if (timer_settime(it, 0, &ts, NULL) == -1) exit_sys("timer_settime"); printf("Press ENTER to exit!...\n"); getchar(); timer_delete(it); puts("Finished"); return 0; } void thread_handler(union sigval sval) { printf("[%d] : thread_handler...\n", sval.sival_int); } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); }