Görevciye Genel Bakış

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 sistemlerindesleep()
işlevlerine benzergrvKOSULLU_GECIK_MS()
- işletim sistemlerindesleep()
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 sistemlerindekiwait()
işlevlerine benzergrvBAYRAK_IMLE()
- işletim sistemlerindekisignal()
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 sistemlerindekiyield()
işlevlerine benzergrvKOSULA_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.