İşletim Sistemlerinin GUI Ortamlarında Mesaj Tabanlı Çalışma Modeli

Mesaj tabanlı programlama modelinde klavye ve fare gibi aygıtlarda oluşan girdileri (event'leri) programcı kendisi almaya çalışmaz. Ya da pooling mekanizması gibi bir mekanizma ile girdinin oluşup oluşmadığını kontrol etmeye çalışmaz. Fare gibi, klavye gibi girdi aygıtlarını işletim sisteminin (ya da GUI alt sistemin) kendisi izler. Oluşan girdi olayı hangi pencereye ait ise işletim sistemi ya da GUI alt sistemi, bu girdi olayını “mesaj” adı altında bir yapıya dönüştürerek o pencerenin ilişkin olduğu (pencereyi yaratan) programın “mesaj kuyruğu (message queue)” denilen bir kuyuk sistemine yerleştirir. Mesaj kuyruğu içerisinde mesajların bulunduğu FIFO (First in First outprensibiyle çalışan bir kuyruk veri yapısıdır.  Sistemin daha iyi anlaşılması için süreci maddeler halinde özetleyelim:

  1. Her programın (ya da thread’in) “mesaj kuyruğu” denilen bir kuyruk veri yapısı vardır. Mesaj kuyruğu işletim sisteminin ya da GUI mekanizmasının bıraktığı mesajlardan oluşmaktadır.
  2. İşletim sistemi ya da GUI alt sistem gerçekleşen girdi olaylarını “mesaj (message)” adı altında bir yapı formatına dönüşürmekte ve bunu pencerenin ilişkin  olduğu programın (ya da thread’in) mesaj kuyruğuna eklemektedir.
  3. Mesajlar ilgili olayı betimleyen ve ona ilişkin bazı bilgileri barındıran yapı (structure) nesleridir. Örneğin Windows’ta mesajlar MSG isimli bir yapıyla temsil edilmişleridir. Bu yapının elemanlarında mesajın ne mesajı olduğu (yani hangi olaydan dolayı gönderildiği) ve olaya ilişkin gerekli bilgiler bulunur.

Görüldüğü gibi GUI programlama modelinde girdileri programcı elde etmeye çalışmamaktadır. Girdileri bizzat işletim sisteminin kendisi ya da GUI alt sistemi elde edip programcıya mesaj adı altında iletmektedir. GUI programlama modelinde işletim sisteminin (ya da GUI alt sistemin) oluşan mesajı ilgili programın (ya da thread’in) mesaj kuyruğuna eklemenin dışında başka bir sorumluluğu yoktur. Mesajların kuyruktan  alınarak işlenmesi ilgili programın sorumluluğundadır. Böylece GUI programcısının mesaj kuyruğuna bakarak sıradaki mesajı alması ve ona uygun işlemleri yapması gerekir. Bu modelde programcı kodunu şöyle düzenler: 
  • Bir döngü içerisinde sıradaki mesajı kuyruktan alma, 
  • Onun neden gönderildiğini belirleme,
  • Uygun işlemleri yapma, 
  • Kuyrukta mesaj yoksa da blokede bekleme
İşte GUI programlarındaki mesaj kuyruğundan mesajı alıp işleyen döngüye "mesaj döngüsü (message loop)" denilmektedir. Tipik bir GUI programında programcı bir döngü içerisinde mesaj kuyruğundan sıradaki mesajı alır ve işler. Mesajın işlenmesi ise “önce ne olmuş onu anlama ve ben buna karşı ne yapmalıyım?” biçiminde oluşturulmuş olan kodlarla yapılmaktadır. Pekiyi bir GUI programı nasıl sonlanmaktadır? İşte pencerenin sağındaki veya solundaki X (çarpı) simgesine kullanıcı tıkladığında işletim sistemi ya da GUI alt sistem bunu da bir mesaj olarak o pencerenin ilişkin olduğu prosesin (ya da thread’in) mesaj kuyruğuna bırakır. Programcı da kuyruktan bu mesajı alarak mesaj döngüsünden çıkar ve program sonlanır.


GUI ortamımız ister .NET, ister Java, ister MFC, isterse Qt olsun, işletim sisteminin ya da GUI alt sistemin çalışması hep burada açıklandığı biçimde gerçekleşir. Yani örneğin biz .NET'te ya da Java'da işlemlerin sanki başka biçimlerde yapıldığını düşünebiliriz. Aslında işlemler bu ortamlar tarafından aşağı seviyede yine burada anlatıldığı gibi yapılmaktadır. Sadece bize yalıtılmış bir ortam sunulmaktadır. Bu ortamlar (frameworks) ya da kütüphaneler çeşitli yükleri üzerimizden alarak bize daha rahat bir çalışma modeli sunarlar. Şimdi GUI programlama modelindeki mesaj kavramını biraz daha inceleyelim. Yukarıda da belirttiğimiz gibi bu modelde programcıyı ilgilendiren çeşitli olaylara mesaj denilmektedir. Örneğin klavyeden bir tuşa basılması, pencere üzerinde fare ile tıklanması, pencere içerisinde farenin hareket ettirilmesi gibi olaylar hep birer mesaj oluşturmaktadır. İşletim sistemleri ya da GUI alt sistemler mesajları birbirinden ayırmak için onlara birer numara karşılık verirler. Örneğin Windows’ta mesaj numaraları WM_XXX biçiminde sembolik sabitlerle kodlanmıştır. Programcılar da konuşurken ya da kod yazarken mesaj numaralarını değil, bu sembolik sabitleri kullanırlar. (Örneğin WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_KEYDOWN gibi) Mesajların numaraları yalnızca gerçekleşen olayın türünü belirtmektedir. Oysa bazı olaylarda gerçekleşen olaya ilişkin bazı bilgiler de söz konusudur. İşte bir mesaja ilişkin o mesaja özgü bazı parametrik bilgiler de işletim sistemi ya da GUI alt sistem tarafından mesajın bir parçası olarak mesajın içerisine kodlanmaktadır. Örneğin Windows’ta biz klavyeden bir tuşa bastığımızda Windows WM_KEYDOWN isimli mesajı programın mesaj kuyruğuna bırakır. Bu mesajı kuyruktan alan programcı mesaj numarasına bakarak klavyenin bir tuşuna basılmış olduğunu anlar. Fakat hangi tuşa basılmıştır? İşte Windows basılan tuşun bilgisini de ayrıca bu mesajın içerisine kodlamaktadır. Örneğin WM_LBUTTONDOWN mesajını Windows farenin sol tuşuna tıklandığında kuyruğa bırakır. Ayrıca basım koordinatını da mesaja ekler. Yani bir mesaj oluştuğunda yalnızca o mesajın hangi tür bir olay yüzünden oluştuğu bilgisini değil aynı zamanda o olayla ilgili bazı bilgileri de kuyruktaki mesajın içerisinden alınabilmektedir.

GUI programlama modelinde bir mesaj oluştuğunda o mesajın bir an evvel işlenmesi ve akışın çok bekletilmemesi gerekir. Aksi takdirde programcı kuyruktaki diğer mesajları işleyemez bu da “kullanıcı açısından program donmuş etkisi” yaratmaktadır. Eğer bir mesaj alındığında uzun süren bir işlem yapılmak isteniyorsa bir thread oluşturulup o işi o thread’e devretmek ve böylece mesaj döngüsünün işlemesini sağlamak gerekir. GUI programlama modellerinde genel olarak mesaj kavramı pencere kavramıyla ilişkilendirilmiştir. Yani bir pencere yaratılmadıktan sonra bir mesajın oluşma durumu da yoktur. Bu nedenle mesaj döngüsüne girmeden önce programcının en az bir pencere yaratmış olması gerekir. Windows gibi bazı sistemlerde pencereler thread’lerle ilişkilendirilmiştir. Bu sistemlerde prosesin tek bir mesaj kuyruğu yoktur. Her thread’in ayrı bir mesaj kuyruğu vardır. Bu durumda işletim sistemi ya da GUI alt sistem bir pencereye ilişkin bir işlem gerçekleştiğinde o pencerenin hangi prosesin thread’i tarafından yaratılmış olduğunu belirler ve mesajı o thread’in mesaj kuyruğuna bırakır. Böylece biz bir thread oluşturup o thread’te de bir pencere yaratmışsak artık bizim de o thread’te o pencerenin mesajlarını işlemek için mesaj döngüsü oluşturmamız gerekir. Tabii eğer thread’imizde biz hiçbir pencere oluşturmamışsak böyle bir mesaj döngüsünü oluşturmamıza da gerek yoktur. Örneğin Microsoft işletim sisteminde bir thread bir pencere yaratmışsa böyle thread’lere “GUI thread’ler” yaratmamışsa “worker thread’ler” denilmektedir.