Görevciye Genel Bakış

Görevci yazılı logo

Genel Bakış

Görevci Tanıtım sayfasında Görevciyi kısaca tanıtmıştım. Bu yazıda, şöyle bir genel bakış yapalım istedim. Görevci nasıl edinilebilir, API'leri (Application Programming Interface, Uygulama Programlama Arayüzü) nelerdir, bir görevin yapısı kodda neye benzer gibi soruları kısaca yanıtlamaya çalışacağım. Hadi başlayalım.

Görevciyi Edinme

Görevci farklı yöntemlerle uygulamanıza tümleştirilebilir.

  • Kaynak kodunu Github reposundan clon edip uygulamanın proje ağacına eklemek.
  • Uygulamanızda git sürümleme kullanıyorsanız bir git alt modülü (submodule) olarak eklemek.

Yakında CMake desteği eklenmesi de planlanmaktadır.

Görevci API'leri

Bir görevin bir olay beklemesi ya da gecikmesi gerekiyorsa CPU'yu boşuna meşgul etmemek, gerektiğinde beklemesi gereken görevi bloklayıp çalışmaya hazır başka bir görevin çalışmasını sağlamak Görevcinin temel amaçlarındandır. Ancak bu mekanizmanın düzgün bir şekilde işlemesi için görev bloğunun ana döngüsü içerisinde return, continue ve break gibi döngü kontrol komutları kesinlikle kullanılmamalıdır. Böyle yapmak görevin düzgün ve beklendiği gibi çalışmamasına neden olur. Bunun yerine aşağıdaki örnek durumlara uygun düşen API'ler kullanılmalıdır. Bu durumu imgeleyen örnek bir kod vermek gerekirse:

 1char gorev(gorevTutucu_t tutucu) {
 2    
 3    static char karakter;
 4    
 5    grvBASLA(tutucu);
 6
 7    /* Bu döngü bir görev bloğunun ana döngüsüdür. for(;;) biçiminde de
 8     * yazılabilir. Burada sonsuz döngüde kalmalı, kesinlikle break veya
 9     * return ile döngüden çıkılmamalıdır.
10     */
11    while(1) {
12        // break komutu ana döngüyü kıracak ve; elle yeniden çalıştırılmadığı
13        // sürece görevi sonlandıracaktır.
14        break;
15        
16        // continue olası Görevci API'lerinin görmezden gelinerek döngünün başa 
17        // dönmesine neden olacağından beklenmeyen davranışlara neden olacaktır.
18        continue;
19        
20        // return, değer verilmeden yazılırsa derleme hatasına neden olur.
21        // Bir değer ile kullanılırsa yine görevin çıkmasına neden olurken, 
22        // göreve bir dahaki girişte görevin beklendiği gibi davranmamasına 
23        // neden olabilir.
24        return; // derleme hatası!
25        return 1; // Görevden çıkar ve kestirilemez davranışlara yol açabilir.
26    }
27
28    grvBITIR(tutucu);
29}

Nitekim ana döngü içinde kullanılan alt döngülerde continue ve break komutları dikkatlice kullanılabilir:

 1char gorev(gorevTutucu_t tutucu) {
 2    
 3    static char karakter;
 4    
 5    grvBASLA(tutucu);
 6
 7    /* Ana döngü  */
 8    while(1) {
 9    
10        // Bir alt döngü
11        for(uint8_t i = 0; i < 5; i++) {
12            // break, alt döngüler için kullanılabilir, bu, ana döngüyü etkilemez.
13            if(a < i) break;
14        }
15        
16        // Veya...
17        while(a != b) {
18            ...
19            ...
20            // Bu durum da ana döngüyü ekilemeyeceği için sorun yaratmaz.
21            if(c == '0') continue;
22            ...
23        }
24    }
25
26    grvBITIR(tutucu);
27}

Bir diğer önemli konu, görev blokları içinde kullanılan değişkenlerdir. Görevci, yapısı gereği dinamik bellek yönetimi ve içerik değiştirme (context-switching) kullanmamaktadır. Görevcinin küçük ölçekli birçok aygıtta çalışabilmesini sağlayan en önemli özelliği az önce bahsedilen, bellek ve çalışma zamanı açısından küçük aygıtlar için çok maliyetli olan bu mekanizmaları kullanmamasıdır. Ancak görev blokları özünde birer işlev (function) olduğundan, görevler bloklandığında değişkenler normalde derleyici optimizasyonundan kaçamaz ve tuttukları verileri yitirirler. Bunu önlemek için değerini koruması gereken değişkenler tanımlanırken static niteleyicisi ile tanımlanmalıdır. Bu niteleyici derleyiciye ilgili değişkeni işlevden dönerken yok etmemesini, işlev bir dahaki sefer çağrıldığında bu değişkenin içeriğinin kullanılacağını bildirir.

Bir görevde gecikme yapılmak isteniyorsa

  • grvGECIK_MS() - işletim sistemlerinde sleep() işlevlerine benzer
  • grvKOSULLU_GECIK_MS() - işletim sistemlerinde sleep() işlevlerine benzer

Bir koşul veya olayın beklenmesi isteniyorsa

  • grvKOSUL_BEKLE()
  • grvBU_KOSULDA_BEKLE()

Verilere erişimde senkronizasyon gerekiyorsa

  • grvBAYRAK_BEKLE() - işletim sistemlerindeki wait() işlevlerine benzer
  • grvBAYRAK_IMLE() - işletim sistemlerindeki signal() işlevlerine benzer

CPU kontrolünden vazgeçmek gerekiyorsa

Bazen bir görev kendi isteğiyle kontrolü çalışmak için bekleyen başka bir göreve vermek isteyebilir. Böyle bir durumda:

  • grvVAZGEC() - işletim sistemlerindeki yield() işlevlerine benzer
  • grvKOSULA_DEK_VAZGEC()

Bu API'lerin herbirinin örnek kullanımları için birer yazı yazacağım, takipte kalın.

Görevlerin Temel Yapısı

Bir görevin normal bir C işlevinden pek bir farkı yoktur, yalnızca biraz daha yapılandırılmıştır ve sürekli çalışması gereken görevler içlerinde bir sonsuz döngü içerir. Bir görevin temel yapısı şu şekilde olmalıdır:

 1char gorev(gorevTutucu_t tutucu) {
 2    /* Görev kapsamında (scope) kullanılacak değişkenler burada
 3     * tanımlanabilir. Değerini koruması gereken değişkenler "static"
 4     * niteleyicisiyle tanımlanmalıdır.
 5     */
 6    static char karakter;
 7
 8    /* Bir görev ana görev döngüsünden hemen önce her zaman görev yapısına
 9     * başvuru olarak parametre alan grvBASLA() ile başlamalıdır.
10     */
11    grvBASLA(tutucu);
12
13    /* Buraya bir kereye mahsus çalışacak kodlar. Örneğin bir giriş - çıkış
14     * portunu ilkleme veya bir analog ucunu ilkleme kodları gibi.
15     * Buradaki kodlar görevin yaşam süresi boyunca yalnızca bir kez
16     * çalışacağı için ilklendirme işlemlerini yapmak için idealdir.
17     */
18
19    /* Bu döngü bir görev bloğunun ana döngüsüdür. for(;;) biçiminde de
20     * yazılabilir. Burada sonsuz döngüde kalmalı, kesinlikle break veya
21     * return ile döngüden çıkılmamalıdır.
22     */
23    while(1) {
24        // Buraya görev kodları ve bloklayıcı API çağrıları
25    }
26
27    /* Tüm görev işlevleri görev yapısına başvuru olarak parametre alan
28     * grvBITIR() ile sonlanmalıdır. Akış sonsuz döngüden buraya buraya
29     * gelmesi görevin bir daha çalışmamasına neden olabilir.
30     */
31    grvBITIR(tutucu);
32}

Pekala, yazının girişindeki soruları kısaca yanıtlamaya çalıştım. Sonraki yazılarda Görevci API'lerinin kullanımlarını örneklendirerek, onların nasıl yerine uygun bir şekilde kullanılabileceğini betimleyeceğim. Sonrakinde görüşmek üzere, herkese mutlu kodlamalar.

comments powered by Disqus