Normal olarak dosya açılırken O_NONBLOCK özelliği kullanılmamışsa dosya blokeli
 modda açılır. Blokeli I/O işlemlerinde read ve write fonksiyonları belirtilen miktarda byte’ın
 tamamı yazılana kadar ya da okunana kadar process çizelge dışına çıkarılarak bloke edilir.
 Eğer dosya disk üzerinde açılmışsa modern sistemlerde bu işlemler için geniş cache alanları
 kullanıldığı için genellikle bir bloke durumu oluşmaz. Ancak donanım aygıtlarından okumalar
 sırasında, pipe’lara okuma yazma sırasında, mesaj kuyruklarına okuma yazma sırasında bloke
 durumunun oluşmasına sıklıkla rastlanır. Blokeli çalışma bazı durumlarda problemli
 olabilmektedir.
 1) Yavaş aygıtlardan okuma yazma yapıldığı zaman aynı zamanda arka planda başka
 işlemler de yapılacaksa bloke durumu istenmez.
 2) Birden fazla yavaş aygıttan okuma yapılmak istensin. Bu durumda blokeli çalışma
 problemli olur, çünkü bir betimleyicide process bloke edilebilir, ancak diğerlerine bilgi gelmiş
 olabilir. Halbuki istenen şey hangi betimleyiciye bilgi gelirse onu işleme sokmaktır.
 Blokesiz moda geçebilmek için open fonksiyonunda O_NONBLOCK kullanılır. SystemV IPC
 mekanizmasında msgrcv ve msgsnd fonksiyonlarında IPC_NOWAIT parametresi
 kullanılırsa mesaj kuyruğu blokesiz moda geçirilebilir. Benzer biçimde soketler de blokesiz
 çalışabilmektedir.
 Blokesiz modda read fonksiyonu hiç bekleme yapmadan o anda okuyabildiği kadar bilgiyi
 okur. Örneğin biz 100 byte okumak isteyelim; read fonksiyonu hazırda 10 byte varsa bu 10
 byte’ı okur ve geri döner. Eğer read fonksiyonu o anda hiçbir okuyacak bilgiye sahip değilse
 başarısızlık anlamında –1 değerine geri döner ve errno EAGAIN olarak set edilir. read
 fonksiyonu 0 değerine geri döndüyse bu durum EOF anlamına gelir.
 Blokesiz olarak SIZE uzunlukta bir bilgi bir döngü içerisinde okunarak bir diziye
 yerleştirilecek olsun. Bu işlem şöyle yapılabilir:
 char buf[SIZE];
 int nleft, nindex, nread;
 nleft = SIZE;
 nindex = 0;
 while (nleft >= 0) {
 nread = read(fd, buf + nindex, nleft);
 if (nread == -1) {
 if (errno == EAGAIN)
 continue;
 else {
 perror(“read”);
 exit(1);
 }
 }
 nleft -= nread;
 nindex += nread;
 }
 
 Birden fazla betimleyiciden okuma yapılması gerektiğinde yukarıdaki algoritma nasıl
 düzenlenmelidir?
 typedef struct _MSG {
 int fd;
 int nindex;
 int nleft;
 char buf[SIZE];
 } MSG;
 MSG msg[10];
 Initiaize(....); /* tüm dizideki elemanlar initialize edilir... */
 for (;;) {
 for (i = 0; i < 10; ++i) {
 nread = read(msg[i].fd, msg[i].buf + msg[i].nindex,
 msg[i].nleft);
 if (nread == -1) {
 if (errno == EAGAIN)
 continue;
 else {
 perror(“read”);
 exit(1);
 }
 }
 msg[i].nleft -= nread;
 msg[i].nindex += nread;
 if (msg[i].nleft == 0) {
 Proc(&msg[i]);
 Initialize(....); // ilgili eleman initialize edilir...
 }
 }
 }
 Yukarıdaki algoritmalar blokesiz okumada kullanılan klasik algoritmalardır. Ancak blokesiz
 okumada küçük bir problem daha vardır. Yukarıdaki gibi bir döngüde eğer aygıttan bilgi çok
 yavaş geliyorsa gereksiz yinelemeler oluşacaktır. Bu da gereksiz bir CPU zamanı
 harcanmasına neden olur. Bu durumda en iyi seçenek process’in herhangi bir betimleyiciye
 bilgi gelene kadar bloke edilmesini sağlamaktır. İşte select fonksiyonu böyle bir işlem
 yapmaktadır. Select fonksiyonuna bir grup betimleyici verilir. select fonksiyonu bunlardan
 herhangi birinde okuma ya da yazma oluşana kadar process’i bloke eder. select fonksiyonu
 yalnızca bu olayların gerçekleştiğini belirtmektedir. Ancak kaç byte’lık bir okuma ya da
 yazma olduğunu vermemektedir.
 select Fonksiyonu
 select fonksiyonu bir POSIX fonksiyonu olmamakla birlikte POSIX’in son eklemelerine
 yerleştirileceği düşünülmektedir. select fonksiyonunun benzeri BSD sistemlerinde poll
 fonksiyonu biçiminde kullanılmaktadır. Bu iki fonksiyon da modern Unix sistemlerinin
 hemen hepsi tarafından desteklenmektedir. Ancak select fonksiyonu çok daha yaygın
 kullanıma sahiptir.
 int select(int maxfd, fd_set *readfds, fd_set *writefds,
 fd_set *exceptfds, struct timeval *tv);
 
 fd_set türü bitsel bir yapıya sahip olan bir türü temsil eder. Bu tür genellikle bir yapı
 biçimindedir. Fonksiyonun fd_set türünden gösterici parametrelerine NULL geçilebilir. Bu
 durum bu parametrelerle ilgilenilmediği anlamına gelir. fd_set bir betimleyici kümesi belirtir.
 fd_set sistemden sisteme değişebilecek bir türü belirttiğine göre bu kümeye betimleyici
 eklemek ya da bu kümeden betimleyici çıkartmak için özel makrolar kullanılmalıdır. fd_set
 genellikle long türden bir dizi elemanına sahip bir yapı biçimindedir. İlgili betimleyici
 kümeye dahilse ilgili bit 1, değilse 0 biçimindedir. fd_set için kullanılan makrolar şunlardır:
 FD_ZERO(fd_set *fdset);
 FD_SET(int fd, fd_set *fdset);
 FD_CLR(int fd, fd_set *fdset);
 FD_ISSET(int fd, fd_set *fdset);
 FD_ZERO tüm kümeyi sıfırlar. FD_SET belirli bir betimleyiciyi kümeye ekler. FD_CLR
 belirli bir betimleyiciyi kümeden çıkartır. FD_ISSET ise belirli bir betimleyicinin kümeye
 dahil olup olmadığına bakar. select fonksiyonu okuma, yazma ve istisna durumları kontrol
 eder. Genellikle programcılar tek bir kümeyi belirleyip diğerlerini NULL olarak geçerler.
 Fonksiyon verilen kümeler içerisindeki herhangi bir betimleyici içerisinde okuma ya da
 yazma olayı gerçekleştiğinde sonlanır. select fonksiyonunun geri dönüş değeri üç biçimde
 olabilir.
 1) Geri dönüş değeri –1 ise bu durum error anlamına gelir. Örneğin bir signal oluştuğunda
 fonksiyon –1 değeriyle geri döner.
 2) Geri dönüş değeri 0 ise herhangi bir betimleyicide istenilen olay gerçekleşmemiştir.
 Fonksiyon timeout nedeniyle çıkmıştır.
 3) Fonksiyonun geri dönüş değeri 0’dan büyükse bu değer tüm kümelerdeki gerçekleşen
 toplam olay sayısını belirtir.
 select fonksiyonunun birinci parametresi işletim sisteminin işini kolaylaştırmak için
 düşünülmüştür. Bu parametre tüm kümelerdeki arama için yapılacak maksimum betimleyici
 sayısını belirtir. Örneğin birinci parametreyi 10 olarak girsek işletim sistemi fd_set
 kümelerinin yalnızca ilk 10 slotu için araştırma yapar (yani 0-9 arası). Normal olarak bu
 sayının toplam kümelere dahil edilmiş olan en büyük betimleyici numarasının bir fazlası
 biçiminde verilmesi uygundur. Fonksiyonun son parametresi timeval türünden bir yapı
 değişkeninin adresini alır.
 struct timeval {
 long tv_sec;
 long tv_usec;
 };
 Programcı son parametreyi NULL geçerse zaman aşımı vermemiş olur. Yapının tv_sec ve
 tv_usec elemanlarının her ikisi de 0 olarak girilirse select fonksiyonu hiç bekleme yapmadan
 olayın gerçekleştiği betimleyicileri rapor eder. select fonksiyonu başarılı bir biçimde geri
 döndüğünde fd_set kümeleri yalnızca olayın gerçekleştiği betimleyicileri içerecek hale
 getirilmiş olur. Yani bu durumda fonksiyon çıkışında FD_ISSET uygulanarak ilgilenilen
 betimleyicide olayın gerçekleşip gerçekleşmediği sorgulanabilir. Aşağıda select fonksiyonu
 kullanılarak bir döngü içersinde etkin bekleme sağlayan kalıp verilmiştir.
 int fd1, fd2, maxfd;
 fd_set rdset;
 for (;;) {
 FD_ZERO(&rdset);
 FD_SET(fd1, &rdset);
 FD_SET(fd2, &rdset);
 maxfd = MAX(fd1, fd2);
 if (select(maxfd + 1, &rdset, NULL, NULL, NULL) == -1) {
 perror(“select”);
 exit(1);
 }
 if (FD_ISSET(fd1, &rdset)) {
 ...
 }
 if (FD_ISSET(fd2, &rdset)) {
 ...
 }
 }
 Bu döngü sırasında bazı betimleyiciler zamanla kapanabilir. Kapanan betimleyicilerin
 kümeden çıkartılması kolay değildir. Zaten kümeden çıkartılmalarına da gerek yoktur, nasıl
 olsa kapanan betimleyicilere ilişkin olaylar hiç gerçekleşmeyecektir. FD_ZERO taşınabilirlik
 açısından döngü içerisinde tutulmalıdır. Çünkü tüm betimleyicilerin sıfırlanması işleminde
 fd_set içerisindeki başka elemanlar çeşitli değerlerle set ediliyor olabilir. select
 fonksiyonundan çıkıldığında yalnızca olay gerçekleşen betimleyiciler fonksiyon tarafından
 kümede bırakılır diğerleri küme dışına alınır.
 /* select.c */
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <fcntl.h>
 #include <unistd.h>
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
 #define PIPE_BUF 10
 typedef struct _PIPEREC {
 int nleft;
 int index;
 char buf[PIPE_BUF + 1];
 } PIPEREC;
 void process(int pipeno, char *str)
 {
 str[PIPE_BUF] = '\0';
 printf("pipe %d: %s\n", pipeno, str);
 }
 int main(void)
 {
 int pipe1, pipe2, maxfd, n;
 fd_set rdset;
 PIPEREC prec1 = {PIPE_BUF, 0 }, prec2 = {PIPE_BUF, 0};
 if ((pipe1 = open("pipe1", O_RDONLY|O_NONBLOCK)) == -1) {
 perror("pipe1");
 exit(1);
 C ve Sistem Programcıları Derneği 159
 }
 if ((pipe2 = open("pipe2", O_RDONLY|O_NONBLOCK)) == -1) {
 perror("pipe1");
 exit(1);
 }
 maxfd = MAX(pipe1, pipe2);
 for (;;) {
 FD_ZERO(&rdset);
 FD_SET(pipe1, &rdset);
 FD_SET(pipe2, &rdset);
 if (select(maxfd + 1, &rdset, NULL, NULL, NULL) == -1) {
 perror("select");
 exit(1);
 }
 if (FD_ISSET(pipe1, &rdset)) {
 n = read(pipe1, prec1.buf + prec1.index, prec1.nleft);
 if (n == -1) {
 perror("read");
 exit(1);
 }
 if (n == 0) {
 prec1.nleft = PIPE_BUF;
 prec1.index = 0;
 }
 prec1.nleft -= n;
 prec1.index += n;
 if (prec1.nleft == 0) {
 process(1, prec1.buf);
 prec1.index = 0;
 prec1.nleft = PIPE_BUF;
 }
 }
 if (FD_ISSET(pipe2, &rdset)) {
 n = read(pipe2, prec2.buf + prec2.index, prec2.nleft);
 if (n == -1) {
 perror("read");
 exit(1);
 }
 if (n == 0) {
 prec2.nleft = PIPE_BUF;
 prec2.index = 0;
 }
 prec2.nleft -= n;
 prec2.index += n;
 if (prec2.nleft == 0) {
 process(2, prec2.buf);
 prec2.index = 0;
 prec1.nleft = PIPE_BUF;
 }
 }
 }
 close(pipe1);
 close(pipe2);
 }
 Ders Sahibi;
						Ders Sahibi;