Friday 28 December 2007

LINUX Programlamağa Giriş – Süreçler 6

Ali Rıza SARAL(1)


(1) Neil Matthew ve Richard Stones, Beginning Linux Programming ‘den faydalanarak derlenmiştir.


Hortlak Süreçler (Zombies)
Süreçler yaratmak için çatal (fork) ‘ı kullanmak çok kullanışlı olabilir, fakat çocuk süreçlerin izini takip etmek zorundasınız. Bir çocuk süreç sona erdiğinde, ebeveyni ile ilişkisi ebeveyn kendi sırası ile sona erince veya bekle (wait) ‘yi çağırıncaya kadar canlılığını sürdürür.

Süreç tablosundaki çocuk süreç hanesi bu yüzden hemen serbest bırakılmaz. Artık canlı olmasa da, çocuk süreç hala sistem içindedir çünkü ebeveyn sonradan bekle (wait) ‘yi çağırırsa diye, çıkış kodunu saklamak gerekir. O işe yaramayan, veya hortlak süreç (zombie) olarak bilinir.

Fork örnek programında mesajların sayısını değiştirirsek hortlak süreçlerin yaratıldığını görebiliriz. Eğer çocuk ebeveyninden daha az sayıda mesaj basarsa, önce biter ve ebeveyni bitinceye kadar bir hortlak olarak var olur.

Örnek: Hortlaklar
Fork2.c fork1.c ile çocuk ve ebeveyn süreçlerin bastığı mesaj sayılarının değiş tokuş edilişi dışında aynıdır. İlgili kod satırları aşağıdadır:

switch(pid)
{
case -1:
perror(“fork failed”);
exit(1);
case 0:
message = “This is the child”;
n = 3;
break;
default:
message = “This is the parent”;
n = 5;
break;
}

Eğer bundan önceki programı ./fork2 & ile çalıştırır ve sonra ps programını çocuk bittikten sonra fakat ebevyn bitmeden çalıştırırsak aşağıdaki çıktıyı görürüz (Bazı sistemler işlevsiz (defunct) yerine hortlak (zombie) diyebilir.)

$ ps –al
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
004 S 0 1273 1259 0 75 0 - 589 wait4 pts/2 00:00:00 su
000 S 0 1274 1273 0 75 0 - 731 schedu pts/2 00:00:00 bash
000 S 500 1463 1262 0 75 0 - 788 schedu pts/1 00:00:00 oclock
000 S 500 1465 1262 0 75 0 - 2569 schedu pts/1 00:00:01 emacs
000 S 500 1603 1262 0 75 0 - 313 schedu pts/1 00:00:00 fork2
003 Z 500 1604 1603 0 75 0 - 0 do_exi pts/1 00:00:00 fork2
000 R 500 1605 1262 0 81 0 - 781 - pts/1 00:00:00 ps

Eğer ebeveyn o zaman anormal şekilde sona ererse, çocuk süreç SüreçKimlikNo’su 1 olan süreci sıfırdan-başla (init) ebeveyn olarak alır. Çocuk süreç şimdi artık çalışmayan bir hayalettir ama ebeveyninin anormal sona erişi yüzünden sıfırdan-başla (init) tarafından miras alınmıştır.

Hortlak sıfırdan-basla tarafından toplanıncaya kadar süreç tablosunda kalacaktır. Tablo ne kadar büyük olursa bu süreç o kadar yavaş olur. Sıfırdan-başla onları temizleyinceye kadar sistem kaynaklarını kullanan hortlak süreçlerden kaçınmanız gerekir.

Çocuk süreçleri çağırmak için kullanabileceğiniz bir başka sistem çağrısı var. Buna SüreçKimlikNosunuBekle (waitpid) adı verilir, ve belirli bir sürecin sona erişini beklemek için bunu kullanabilirsiniz.

#include
#include
pid_t waitpid(pid_t pid, int *stat_loc, int options);

pid başlangıç değişkeni beklenecek belirli bir çocuk sürecin Süreç Kimlik No’sunu belirler. Eğer -1 ise, herhangi bir çocuk süreç için geçerli bilgileri getirir. Bekle (wait) gibi, eğer stat_loc boş işaretedici değil ise, onun belirttiği konuma durum bilgisini yazar.

Options başlangıç değişkeni waitpid’nin davranışını değiştirişimize izin verir. En kullanışlı seçenek, SüreçKimlikNosunuBekle (waitpid) tarafından, çağıranın çalışışının askıya alınışını engelleyen WNOHANG ‘dır. Bunu herhangi bir çocuk sürecin sona erip ermediğini bulmak, ve eğer değilse devam etmek için kullanabilirsiniz. Diğer seçenekler bekle (wait) ile aynıdır.

Dolayısıyla, eğer bir ebeveyn süreç tarafından düzenli olarak belirli bir çocuk sürecin sona erip ermediğini sınamasını isterseniz aşağıdaki çağrıyı kullanabilirsiniz:

waitpid(child_pid, (int *) 0, WNOHANG);

Eğer çocuk sona ermiş ya da durmuş ise bu komut 0 döndürür, veya eğer var ise child_pid. SüreçKimlikNosunuBekle (waitpid) hata durumunda -1 döndürür ve hatanosu (errno) ’ya uygun değer verir. Bu eğer hiçbir çocuk süreç yok ise (hatanosu (errno) ECHILD değerli) olur, eğer çağrı bir işaret (signal(EINTR)) ile kesilirse veya seçenek giriş değişkeni geçersizse (EINVAL) ).

Giriş ve Çıkış Yeniden Yönlendiriş (Input Output Redirection)
Süreçler hakkındaki bilgimizi faydalanarak açık dosya tanımlayıcıların çatal (fork) ve çalıştır (exec) komutları içinden geçerek korunduğu gerçeğini kullanacağız.

Örnek: Redirection
Standart girişini okur ve kullanışlı bir dönüşüm uygulayıp standart çıkışına yazar.
upper.c, girişi okuyup büyük harfe çevirir.

#include
#include
int main()
{
int ch;
while((ch = getchar()) != EOF) {
putchar(toupper(ch));
}
exit(0);
}

Programı çalıştırınca, beklediğimizi yapar:

$ ./upper
hello THERE
HELLO THERE
^D
$

Tabii ki, onu, kabuk yeniden yönlendirişini kullanarak, bir dosyayı büyük harfe çevirmek için kullanabiliriz.

$ cat file.txt
this is the file, file.txt, it is all lower case.
$ ./upper < file.txt
THIS IS THE FILE, FILE.TXT, IT IS ALL LOWER CASE.

Eğer bu filtreyi bir program içinden kullanmak istersek ne olur? Bu program, upper.c ‘yi kullanır, bir dosya ismini başlatış değişkeni olarak alır ve eğer yanlış çağırılırsa bir hata ile yanıt verir.

#include
#include
int main(int argc, char *argv[])
{
char *filename;
if (argc != 2) {
fprintf(stderr, “usage: useupper file\n”);
exit(1);
}
filename = argv[1];

Standart tekrar açarız, bunu yaparken tekrar herhangi bir hata olup olmadığını kontrol ederiz, ve upper ‘ı çağırırken execl ‘i kullanırız.

if(!freopen(filename, “r”, stdin)) {
fprintf(stderr, “could not redirect stdin from file %s\n”, filename);
exit(2);
}
execl(“./upper”, “upper”, 0);

execl ‘in şu andaki sürecin yerine geçtiğini unutmayınız; eğer bir hata var ise, kalan satırlar çalıştırılmaz.

perror(“could not exec ./upper”);
exit(3);
}

LINUX Programlamağa Giriş– Süreçler 5

Ali Rıza SARAL(1)


(1) Neil Matthew ve Richard Stones, Beginning Linux Programming ‘den faydalanarak derlenmiştir.


Bir Süreci Bekleyiş
çatal (fork) ile bir çocuk süreç başlattığımızda, çocuk hayatını kendi ellerine alır ve bağımsız olarak koşar. Bazan, bir çocuk sürecin ne zaman bittiğini bulmak isteriz. Örn., bundan önceki programda ebeveyn çocuktan önce biter ve çocuk devam ederken kirli bir çıktı alırız. Çocuk bitinceye kadar ebeveyn sürecini bekleyişini bekle (wait) ‘yi çağırarak düzenleyebiliriz.

#include
#include
pid_t wait(int *stat_loc);

bekle sistem çağrısı bir ebeveyn sürecin, çocuk süreci duruncaya kadar duraklayışına neden olur. Çağrı çocuk sürecin SüreçKimlikNo’sunu (PID) geri döndürür. Bu normalde sona ermiş olan bir çocuk süreçtir. Durum bilgisi (status information) ebeveyn sürece çocuk sürecin çıkış durumunu belirleyiş imkanı sağlar, yani, anagövde (main) tarafından döndürülen değer veya çıkışa (exit) iletilen değer.

Eğer stat_loc bir boş işaretedici (null pointer) değilse, durum bilgisi işaret ettiği yere yazılacaktır. Durum bilgisini sys/wait.h ‘da tanımlanan makroları kullanarak tercüme edebiliriz. Bunlar

Macro Definition
WIFEXITED(stat_val) Çocuk normal bittiyse sıfır değil.
WEXITSTATUS(stat_val) Eğer WIFEXITED sıfır değilse, bu çocuk çıkış kodunu döndürür. WIFSIGNALED(stat_val) Eğer çocuk bir yakalanmadı (uncaught) işareti ile sona erdiyse sıfır değil.
WTERMSIG(stat_val) Eğer WIFSIGNALED sıfır değilse, bu bir işaret sayısı döndürür.
WIFSTOPPED(stat_val) Sıfır değilse çocuk durdu.
WSTOPSIG(stat_val) Eğer WIFSTOPPED sıfır değilse, bu bir işaret sayısı döndürür.
Örnek:
Programı çocuk süreci bekleyip onun bitiş durumunu yoklayabilecek şekilde değiştirelim. Wait.c

#include
#include
#include
#include
int main()
{
pid_t pid;
char *message;
int n;
int exit_code;
printf(“fork program starting\n”);
pid = fork();
switch(pid)
{
case -1:
perror(“fork failed”);
exit(1);
case 0:
message = “This is the child”;
n = 5;
exit_code = 37;
break;
default:
message = “This is the parent”;
n = 3;
exit_code = 0;
break;
}
for(; n > 0; n--) {
puts(message);
sleep(1);
}
Programın bu kısmı çocuk sürecin bitişini bekler.
if (pid != 0) {
int stat_val;
pid_t child_pid;
child_pid = wait(&stat_val);
printf(“Child has finished: PID = %d\n”, child_pid);
if(WIFEXITED(stat_val))
printf(“Child exited with code %d\n”, WEXITSTATUS(stat_val));
else
printf(“Child terminated abnormally\n”);
}
exit(exit_code);
}
Bu programı çalıştrınca, ebeveynin çocuğu beklediğini görürüz. When we run this program, we see the

$ ./wait
fork program starting
This is the child
This is the parent
This is the parent
This is the child
This is the parent
This is the child
This is the child
This is the child
Child has finished: PID = 1582
Child exited with code 37
$

Thursday 27 December 2007

LINUX Programlamağa Giriş– Süreçler 4

Ali Rıza SARAL(1)


(1) Neil Matthew ve Richard Stones, Beginning Linux Programming ‘den faydalanarak derlenmiştir.

Yeni Süreçler Başlatış
Bir program içinden başka bir programın çalışışına neden olabiliriz ve böylece sistem kütüphanesi fonksiyonunu kullanarak yeni bir süreç yaratabiliriz.

#include
int system (const char *string);

Sistem fonksiyonu kendisine bir karakter dizisi olarak geçirilen komutu koşturur ve bitişi için bekler. Komut aşağıdaki şekilde

$ sh -c string

bir kabuğa verilmiş gibi çalıştırılır. Sistem eğer komutu koşturmak için bir kabuk açılamazsa 127 ve eğer başka bir hata oluşursa -1 döndürür. Başka durumlarda, sistem komutun geri dönüş kodunu döndürür.

Örnek:
#include
#include
int main()
{
printf(“Running ps with system\n”);
system(“ps -ax”);
printf(“Done.\n”);
exit(0);
}

Çıktı:
$ ./system1
Running ps with system
PID TTY STAT TIME COMMAND
1 ? S 0:05 init
2 ? SW 0:00 [keventd]
...
1262 pts/1 S 0:00 /bin/bash
1273 pts/2 S 0:00 su -
1274 pts/2 S 0:00 -bash
1463 pts/1 S 0:00 oclock -transparent -geometry 135x135-10+40
1465 pts/1 S 0:01 emacs Makefile
1480 pts/1 S 0:00 ./system1
1481 pts/1 R 0:00 ps -ax
Done.

Alternatif:
System1.c içindeki fonksiyon çağrısını şununla değiştiriniz:

system(“ps -ax &”);

Çıktı:
$ ./system2
Running ps with system
PID TTY STAT TIME COMMAND
1 ? S 0:05 init
2 ? SW 0:00 [keventd]
...
Done.
$ 1246 ? S 0:00 kdeinit: klipper -icon klipper -miniicon klipper
1274 pts/2 S 0:00 -bash
1463 pts/1 S 0:00 oclock -transparent -geometry 135x135-10+40
1465 pts/1 S 0:01 emacs Makefile
1484 pts/1 R 0:00 ps -ax
.
Bir Süreç Görüntüsünün Yerine Geçiş
Exec başlığı altında büyük bir ilişkili fonksiyonlar ailesi var. Süreçleri başlatış ve program başlatış değişkenlerini Sunuş açısından farklılaşırlar. Bir exec fonksiyonu şu andaki sürecin yerine yeni bir süreç koyar, bir patika veya dosya başlangıç değişkeni belirleyerek .

Genelde, sistem komutunu kullanmak süreçleri başlatmak için idealden çok uzak bir yaklaşımdır.

Bu fonksiyonlar iki gruba aittir. execl, execlp, ve execle sonu boş işaret edici ile biten değişik sayıda başlangıç değişkeni alırlar. Eğer exec fonksiyonunu ps programını başlatmak için kullanmak isterseniz, aşağıda gösterilen altı exec ailesinin birinden seçebilirsiniz.

#include
/* Example of an argument list */
/* Note that we need a program name for argv[0] */
char *const ps_argv[] =
{“ps”, “-ax”, 0};
/* Example environment, not terribly useful */
char *const ps_envp[] =
{“PATH=/bin:/usr/bin”, “TERM=console”, 0};
/* Possible calls to exec functions */
execl(“/bin/ps”, “ps”, “-ax”, 0); /* assumes ps is in /bin */
execlp(“ps”, “ps”, “-ax”, 0); /* assumes /bin is in PATH */
execle(“/bin/ps”, “ps”, “-ax”, 0, ps_envp); /* passes own environment */
execv(“/bin/ps”, ps_argv);
execvp(“ps”, ps_argv);
execve(“/bin/ps”, ps_argv, ps_envp);

Alternatif:
#include
#include
int main()
{
printf(“Running ps with execlp\n”);
execlp(“ps”, “ps”, “-ax”, 0);
printf(“Done.\n”);
exit(0);
}

Pexec.c’yi çalıştırınca, alışılagelmiş ps çıktısını alırız fakat done çıkmaz. Ayrıca çıkışta pexec’ten bahsedilmez.

$ ./pexec
Running ps with execlp
PID TTY STAT TIME COMMAND
1 ? S 0:05 init
2 ? SW 0:00 [keventd]
...
1262 pts/1 S 0:00 /bin/bash
1273 pts/2 S 0:00 su -
1274 pts/2 S 0:00 -bash
1463 pts/1 S 0:00 oclock -transparent -geometry 135x135-10+40
1465 pts/1 S 0:01 emacs Makefile
1514 pts/1 R 0:00 ps –ax

Süreç Görüntüsünü Katlayış
Aynı anda birden çok fonksiyon çalıştıran süreçler kullanmak için, bağlar(threads) kullanabiliriz veya init’in yaptığı gibi program içinde tamamen ayrı bir süreç yaratırız, exec durumunda olduğu gibi, şu anda çalışan bağın yerine geçirmek yerine.

Fork’u çağırarak yeni bir süreç yaratabiliriz. Bu sistem çağrısı şu andaki sürecin çok sayıda özellikleri ile süreç tablosunda yeni bir hane açarak şu andaki süreci tekrar yaratır.
Yeni süreç hemen hemen aslı ile aynıdır, aynı kodu çalıştırır fakat kendi adres alanı, ortamı, ve dosya tanımlayıcıları ile. Exec fonksiyonları ile birlikte fork yeni süreçler yaratmak için ihtiyacımız olan yegane şeydir.

#include
#include
pid_t fork(void);

Şekil 11-2’de gördüğünüz gibi, ebeveyn içinde fork’a yapolan çağrı yeni çocuk sürecin SüreçKimlikNo’sunu (PID) döndürür. Yeni süreç aslının aynı şekilde çalışmağa devam eder, çocuk sürecin fork’a çağrısı 0 döndürmek istisnası ile. Bu hem ebeveynin hem de çocuğun kimin kim olduğunu belirleyişini mümkün kılar.
Şekil 11-2

Eğer fork başaramazsa -1 döndürür. Bu bir ebeveynin sahip olabileceği çocukların üst sınırı (COCUK_MAX (CHILD_MAX)) nedeni ile bu durum yaygındır, bu durumda errno EAGAIN değerine eşitlenir. Eğer süreç tablosunda bir hane için yeterli yer yoksa, veya yeterli sanal bellek yoksa, errno değişkeni ENOMEM ile eşitlenir.

Tipik bir fork kod parçası:

pid_t new_pid;
new_pid = fork();
switch(new_pid) {
case -1 : /* Error */
break;
case 0 : /* We are child */
break;
default : /* We are parent */
break;
}

Örnek: fork1.c
#include
#include
#include
int main()
{
pid_t pid;
char *message;
int n;
printf(“fork program starting\n”);
pid = fork();
switch(pid)
{
case -1:
perror(“fork failed”);
exit(1);
case 0:
message = “This is the child”;
n = 5;
break;
default:
message = “This is the parent”;
n = 3;
break;
}
for(; n > 0; n--) {
puts(message);
sleep(1);
}
exit(0);
}
Bu program iki süreç olarak çalışır. Bir çocuk doğar ve bir mesajı 5 defa basar. Asıl süreç(ebeveyn) mesajı yalnız üç defa basar. Ebeveyn süreç çocuk mesajlarının hepsini basmadan önce biter, dolayısıyla bir sonraki kabuk sorgucusu(prompt) çıkış ile karışarak belirir.

$ ./fork1
fork program starting
This is the parent
This is the child
This is the parent
This is the child
This is the parent
This is the child
$ This is the child
This is the child

Monday 24 December 2007

LINUX Programlamağa Giriş– Süreçler 3

Ali Rıza SARAL(1)


(1) Neil Matthew ve Richard Stones, Beginning Linux Programming ‘den faydalanarak derlenmiştir.

Süreç Planlayış
Daha ileri bir ps çıktısı ps komutunun kendisi için olan hanedir.

1357 pts/2 R 0:00 ps –ax

Bu, süreç 1357’nin koşuş halinde (run state (R)) olduğunu ve ps –ax komutunu çalıştırmakta olduğunu belirtir. Böylece bu süreç kendi çıktısında tarif edilmektedir! Durum (status) belirteci yalnızca programın koşmağa hazır olduğunu belirtir, gerçekten o anda koştuğunu değil.

Tek işlemcili bir bilgisayar üzerinde, bir anda tek bir süreç çalışabilir, diğerleri sıralarını beklerken. Zaman dilimleri (time slices) olarak bilinen bu sıralar, çok kısadır ve programlar aynı zamanda çalışıyor izlenimini verirler. R yalnızca programın başka süreçlerin bitmesini beklemediğini ya da giriş çıkışın tamamlanışını beklediğini belirtir. ps çıktısında bunun gibi iki süreçgörmenizin nedeni budur. (Bir başka genellikle koşuyor şeklinde işaretlenmiş süreç X görüntü sunucusudur (X display server).

Linux çekirdeği hangi sürecin bir sonraki zaman dilimini alacağına karar vermek için bir süreç planlayıcısı (process scheduler) kullanır. Bunu süreç önceliğine (process priority) göre yapar. Yüksek öncelikli süreçler daha sık koşmak durumuna ulaşırken, düşük öncelikli arkaplan görevleri gibi diğerleri daha seyrek çalışır.

Linux ile, süreçler kendilerine ayrılan zaman dilimini aşamazlar. Şüf’alı çok görevli (pre-emptively multitasked) (*) oldukları için işbirlikleri gerekmeden askıya alınırlar ve kaldıkları yerden devam ederler. Eski sistemler, başka süreçlerin kaldıkları yerden devam edebilişleri için genellikle süreçlerin doğrudan kontrolü bırakışını gerektirir.

(Şüf’a = pre-emptive kelimesi Hukuk’ta geçer, Sn. Nadir YÜCEL’den alınmıştır.)

Linux gibi çok sayıda programın büyük olasılıkla aynı imkanlar için yarıştığı çok görevli bir sistemde, kısa iş yapış patlayışları ve giriş için ara verişler yapan programlar daha iyi davranışlı olarak değerlendirililer, aralıksızca bir değer hesaplayarak veya sistemi yeni girişi var mı diye sürekli sorgulayarak işlemciyi esir alanlara göre… İyi davranışlı programlar hoş (nice) programlar olarak adlandırılırlar, ve bu “hoşluk” bir bakıma ölçülebilir.

İşletim sistemi bir sürecin önceliğini bir “hoş” (nice) değerine dayanarak belirler, kendiliğinden değeri 0’dır, ve programın davranışına göre değişir. Hiç durmadan, uzun süreler için çalışan programlar düşük öncelikler alırlar. Girdi beklerken vb. duraklayan programlar ödüllendirilirler.

Bu, kullanıcı ile etkileşen bir programı canlı tutmağa yardım eder; öte yandan kullanıcıdan bir girdi beklerken sistem önceliğini arttırır, öyle ki kaldığı yerden devam etmeğe hazır olduğunda yüksek öncelik sahibi olur. Süreç hoş değerini hoş (nice) ile belirleyebiliriz ve yeniden-hoş (renice) ile ayarlayabiliriz. Hoş (nice) komutu hoş değerini 10 arttırır, ona düşük öncelik vererek. Faal süreçlerin hoş değerlerine ps komutunun –l veya –f (uzun çıktı için) seçenekleri ile bakabiliriz. İlgilendiğimiz değer NI(nice) sütununda gösterilir.
$ ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
000 S 500 1259 1254 0 75 0 - 710 wait4 pts/2 00:00:00 bash
000 S 500 1262 1251 0 75 0 - 714 wait4 pts/1 00:00:00 bash
000 S 500 1313 1262 0 75 0 - 2762schedu pts/1 00:00:00 emacs
000 S 500 1362 1262 2 80 0 - 789 schedu pts/1 00:00:00 oclock
000 R 500 1363 1262 0 81 0 - 782 - pts/1 00:00:00 ps

Burada oclock programının(süreç 1362) kendiliğinden gelen hoş değeri ile çalıştığını görebiliriz. Eğer şu komut ile başlatılmış olsaydı

$ nice oclock &
+10 hoş değeri ayrılmış olacaktı ona. Eğer şu komut ile bu değeri ayarlarsak

$ renice 10 1362
1362: old priority 0, new priority 10

Saat (clock) programı daha seyrek koşacak. Değiştirilmiş hoş değerini yine ps ile görebiliriz:

F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
000 S 500 1259 1254 0 75 0 - 710 wait4 pts/2 00:00:00 bash
000 S 500 1262 1251 0 75 0 - 714 wait4 pts/1 00:00:00 bash
000 S 500 1313 1262 0 75 0 - 2762 schedu pts/1 00:00:00 emacs
000 S 500 1362 1262 0 90 10 - 789 schedu pts/1 00:00:00 oclock
000 R 500 1365 1262 0 81 0 - 782 - pts/1 00:00:00 ps

Durum sütunu hoş değerinin kendiliğinden gelenden daha farklı olduğunu belirtmek için şimdi N değerini belirtir. ps çıktısının PPID alanı ebeveyn süreç KİMLİKNO (ID)’sunu belirtir, yani ya bu sürecin başlayışına neden olan sürecin SüreçKimlikNo’su (PID), ya da eğer bu süreç artık koşmuyorsa, sıfırdan-başla(init) (SüreçKimlikNo 1). Linux planlayıcısı hangi sürecin çalışışına izin vereceğine öncelik temelinde karar verir. Tam gerçekleyişler değişebilirler, tabii, fakat yüksek öncelikli süreçler daha sık koşar. Bazı durumlarda, eğer yüksek öncelikli süreçler koşmağa hazır iseler, düşük öncelikli süreçler hiçbir zaman çalışmayabilir.

Sunday 23 December 2007

LİNUX Programlamağa Giriş - Süreçler 2

Ali Rıza SARAL(1)

(1) Neil Matthew ve Richard Stones, Beginning Linux Programming ‘den faydalanarak derlenmiştir.

Süreçleri İzleyiş
ps komutu çalıştırmakta olduğumuz süreçleri, bir başka kullanıcının çalıştırdığı süreci ya da sistem üzerindeki bütün süreçleri gösterir. İşte örnek bir çıktı:

$ ps -af
UID PID PPID C STIME TTY TIME CMD
root 433 425 0 18:12 tty1 00:00:00 [bash]
rick 445 426 0 18:12 tty2 00:00:00 -bash
rick 456 427 0 18:12 tty3 00:00:00 [bash]
root 467 433 0 18:12 tty1 00:00:00 sh /usr/X11R6/bin/startx
root 474 467 0 18:12 tty1 00:00:00 xinit /etc/X11/xinit/xinitrc --
root 478 474 0 18:12 tty1 00:00:00 /usr/bin/gnome-session
root 487 1 0 18:12 tty1 00:00:00 gnome-smproxy --sm-client-id def
root 493 1 0 18:12 tty1 00:00:01 [enlightenment]
root 506 1 0 18:12 tty1 00:00:03 panel --sm-client-id default8
root 508 1 0 18:12 tty1 00:00:00 xscreensaver -no-splash -timeout
root 510 1 0 18:12 tty1 00:00:01 gmc --sm-client-id default10
root 512 1 0 18:12 tty1 00:00:01 gnome-help-browser --sm-client-i
root 649 445 0 18:24 tty2 00:00:00 su
root 653 649 0 18:24 tty2 00:00:00 bash
neil 655 428 0 18:24 tty4 00:00:00 -bash
root 713 1 2 18:27 tty1 00:00:00 gnome-terminal
root 715 713 0 18:28 tty1 00:00:00 gnome-pty-helper
root 717 716 13 18:28 pts/0 00:00:01 emacs
root 718 653 0 18:28 tty2 00:00:00 ps –af

Bu çıktı, çok sayıda süreç hakkında bilgi göstermektedir, bir Linux sisteminde X altında çalışan bir Emacs editörü de dahil olmak üzere. Örneğin, TTY sütunu sürecin hangi terminalde başlatıldığını, TIME ise o ana kadar kullanılan MİB (CPU) zamanını, ve CMD sütunu süreci başlatmak için kullanılan komutu gösterir. Bunlardan bazılarına daha yakından bakalım:

neil 655 428 0 18:24 tty4 00:00:00 –bash
Başlangıçtaki login 4 nolu sanal konsolda icra edilmiştir. Bu makinadaki konsoldur bu. Çalışan kabuk (shell) programı Linux’un default’u, bash’tir.

root 467 433 0 18:12 tty1 00:00:00 sh /usr/X11R6/bin/startx
Xwindow sistemi xibaşlat (startx) komutu ile başlatılmıştır. Bu, X sunucusunu (server) başlatır ve bazı başlangıç X programlarını koşturur.

root 717 716 13 18:28 pts/0 00:00:01 emacs
Bu süreç X içinde Emacs’i çalıştıran bir pencereyi temsil eder. Yeni bir pencereden gelen bir istek doğrultusunda pencere yöneticisi tarafından başlatılmıştır. Ona yazmak ve ondan okumak için kabuğa yeni bir sözde terminal pts/0 atanmıştır.

root 512 1 0 18:12 tty1 00:00:01 gnome-help-browser --sm-client-i
Bu pencere yöneticisi tarafından başlatılmış GNOME yardım gözden-geçiricisidir(browser).

Yalnızca(by default), ps programı yalnız bir terminal, bir konsol, bir seri hat, veya sözde (pseudo) terminal ile bir bağlantıyı sürdüren süreçleri gösterir. Diğer süreçler bir kullanıcı ya da terminal ile bağlantıya ihtiyaç duymadan çalışabilirler. Bunlar ortak kullanılan kaynakları yönetmek için Linux’un kullandığı tipik süreçlerdir. Bütün bu tür süreçleri ps’in –a seçeneği ile ve “tam” (“full”) bilgi elde etmek için –f seçeneği ile görebiliriz.

Sistem Süreçleri
İşte Linux sisteminde çalışan başka bazı süreçler. Çıktı netlik açısından kısaltılmıştır.
Here are some of the other processes running on this Linux system. The output has been abbreviated for clarity.
$ ps -ax
PID TTY STAT TIME COMMAND
1 ? S 0:05 init
2 ? SW 0:00 [keventd]
3 ? SW 0:00 [kapmd]
4 ? SWN 0:00 [ksoftirqd_CPU0]
5 ? SW 0:00 [kswapd]
6 ? SW 0:00 [bdflush]
7 ? SW 0:00 [kupdated]
8 ? SW 0:00 [kinoded]
10 ? SW 0:00 [mdrecoveryd]
75 ? SW<>

Saturday 22 December 2007

LINUX Programlamağa Giriş– Süreçler 1

Ali Rıza SARAL(1)

(1) Neil Matthew ve Richard Stones, Beginning Linux Programming ‘den faydalanarak derlenmiştir.

Süreçler
Bir süreç “içinde bir veya daha çok bağın çalıştığı ve o bağlar için gerekli sistem kaynaklarının yer aldığı bir adres alanıdır.” Şimdilik, bir süreci çalışmakta olan bir program olarak değerlendireceğiz.

Linux gibi çok-görevli (multi-tasking) bir işletim sistemi çok sayıda programın aynı anda çalışışına izin verir. Çalışmakta olan bir programın her bir gerçekleşişi(instance) ayrı bir süreç ortaya koyar.

Çok-kullanıcılı bir sistem olarak, Linux çok sayıda kullanıcının sisteme aynı anda erişişine izin verir. Her kullanıcı çok sayıda programları, hatta aynı programın çok sayıda gerçekleşişini aynı anda çalıştırabilir. Sistem kaynaklarını yönetmek ve kullanıcı erişimini kontrol etmek için sistemin kendisi başka programlar çalıştırır aynı zamanda.

Çalışmakta olan bir program—veya süreç—program kodu, veri, değişkenler (sistem belleğini kaplayan), açık dosyalar(dosya tanımlayıcıları), ve ortam(environment) ‘dan oluşur. Genellikle, Linux sistemi kod ve sistem kütüphanelerini süreçler arasında ortak kullandırarak bir anda bellekte kodun yalnız bir kopyasının bulunmasını sağlar.

Süreç Yapısı Şekil 1-1
Eğer ps komutunu aşağıda belirtildiği gibi koşturursak, çıkış şunun gibi görünecektir:

$ ps -af
UID PID PPID C STIME TTY TIME CMD
rick 101 96 0 18:24 tty2 00:00:00 grep pid_t /usr/include/sys/*.h
neil 102 92 0 18:24 tty4 00:00:00 grep XOPEN /usr/include/features.h

Her sürece süreç tanımlayıcıya SüreçKimlikNosu - process identifier PID adı verilen eşsiz bir sayı ayrılır. 1 sayısı diğer süreçleri yöneten özel bir süreç olan sıfırdan-başlat (init) ‘a ayrılır.

Normalde, bir Linux süreci program kodunu tutan bellek alanına yazamaz, dolayısıyla kod belleğe yalnız-oku (read-only) olarak yüklenir. Bu alana yazılamasa bile, bu alan emin bir şekilde başkaları ile paylaşılabilir.

Sistem kütüphaneleri paylaşılabilir. Böylece, çok sayıda program çağırsa bile, bellekte, örneğin printf ‘in tek bir kopyası oluşu yeterlidir. Bu Pencereler (Windows)‘in devingen ilişki kütüphaneleri (dynamic link libraries (DLLs))’nin çalışış şekline benzer fakat daha işlenmiş bir modeldir.

Standart kütüphanelerin ortak alt-programlarını hariç tutmak bütün işletim sistemi üzerinde büyük bir bellek alanı kurtarılışını sağlar. Fakat bir programın çalışışı için ihtiyacı olan herşey, örneğin kullandığı değişkenler, dosyalar her süreç için ayrıdır. Ek olarak, fonksiyonlar içindeki yerel değişkenler (local variables) ve fonksiyonları kontrol ve onlardan geri döndürülen değerler için kullanılan, bir sürecin kendine ait yığın(stack) alanı vardır. Aynı zamanda, yalnız bu sürecin kullanması için kurulmuş, ortam değişkenlerini içeren kendi ortam alanı (environment space) vardır.

Bir süreç, aynı zamanda kendisi ile ilgili çalışan bağ içinde, çalışırken nereye geldiğinin kaydını yani program sayacını tutmak zorundadır.

Bir çok LİNUX sisteminde ve bazı UNİX sistemlerinde /proc adı verilen özel bir dosya kümesi vardır. Bunlar gerçek dosyalar olmaktan çok, süreçler çalışırken sanki kütüphanelerdeki dosyalar gibi içlerine bakmanıza izin verdikleri için özeldirler.

Son olarak, LİNUX, UNİX gibi kod ve veriyi sabit diskin bir alanına sayfalayan sanal bir bellek sistemine sahip olduğu için fiziksel belleğe sığabilecek olandan çok dafa fazla sayıda süreç yönetilebilir.

Süreç Tablosu
Linux süreç tablosu (process table) şu anda yüklü olan bütün süreçlerin, örneğin PID, durum(status), ve komut dizisi türünden ps çıktısında görebileceğimiz türden bilgilerini tanımlar.

İşletim sistemi süreçleri SüreçKimlikNo’ları (PID) ‘nı kullanarak yönetir, ve onlar süreç tablosuna index olarak kullanılırlar. Bu tablo sınırlı büyüklüktedir, bu yüzden bir sistemin destekleyebileceği süreç sayısı sınırlıdır.

Monday 17 December 2007

LİNUX Çekirdeğini Anlamak - Süreçler 6


3.4 Süreçleri Yok etmek (Destroying Processes)
Süreçlerin çoğu koşturmak zorunda oldukları kodun çalışışını bitirmek anlamında “ölürler.”
Bu olduğunda, çekirdek, bu sürecin sahibi olduğu kaynakları serbest bırakmak için haberdar edilmelidir; bu bellek, açık dosyalar, ve semaphor gibi başka ıvır zıvırı içerir. Bir sürecin alışılagelmiş bitiş şekli çık() (exit()) sistem çağrısını harekete geçirmektir. Bu sistem çağrısı programcı tarafından doğrudan konabilir. Ek olarak, çık() (exit()) sistem çağrısı kontrol akışı ana prosedürün son komut satırına geldiği her kez çalıştırılır (C programlarında ana() (main()) fonksiyonu). Ya da, çekirdek bir süreci ölmeye zorlayabilir.

Bu tipik olarak, süreç, muamele edemeyeceği veya yok sayamayacağı bir işaret (signal) aldığında veya çekirdek bir süreç yararına çalışırken, Çekirdek Çalışma Türünde kurtarılamayacak bir MİB istisnası (exception) ilan edilirse ortaya çıkar.

3.4.1 Süreç Sonu (Process Termination)
Bütün süreç sonları çekirdek veri alanlarının sona eren sürece değindiği noktaları(references) kaldıran çıkış_yap() (do_exit( )) fonksiyonu tarafından ele alınır. çıkış_yap() (do_exit( )) fonksiyonu aşağıdaki eylemleri yapar:

1. Süreç tanımlayıcının PF_ÇIKIYOR (PF_EXITING) bayrak alanını işaretler.
2. Eğer gerekiyorsa süreç tanımlayıcıyı bir IPC semaphore kuyruğundan sem_çık() (sem_exit( )) fonksiyon ya da devingen zamanlayıcı kuyruğundan yoket_zamanlayıcı (del_timer( )) fonksiyonu ile çıkar.

3. __exit_mm( ), __exit_files( ), __exit_fs( ), and _ _exit_sighand( ) fonksiyonları ile sayfalayış, dosyasistemi, ve işaret muamelesi ile ilgili süreç veri yapılarını sınar. Bu fonksiyonlar aynı zamanda eğer başka hiçbir süreç paylaşmıyorsa bu veri yapılarının herhangi birini yok ederler.

4. Süreç tanımlayıcının hal (state) alanını GÖREV_ZOMBİE (TASK_ZOMBIE) olarak işaretler.

5. Süreç tanımlayıcının çıkış_kodu (exit_code) alanını süreç sona eriş koduna eşitler. Bu değer ya çıkış() (exit()) sistem çağrısı parametresi(normal sonlanış), veya çekirdek tarafından sağlanan hata kodudur(anormal sonlanış).

6. Hem ebeveyn sürecinin hem de çocuk süreçlerin ebeveyn ilişkilerini güncelleştirmek için çıkış_bildirim() (exit_notify()) fonksiyonunu harekete geçirir. Sona eren süreç tarafından yaratılmış bütün çocuk süreçleri sıfırdan_başla (init) sürecinin çocukları haline gelirler.

7. Yeni bir süreci koşturmağa seçmek için zaman_ planla() (schedule()) fonksiyonunu harekete geçirir. GÖREV_ZOMBİE (TASK_ZOMBIE) halindeki bir süreç zaman_planlayıcı tarafından yok sayıldığı için, süreç zaman_planla() (schedule( )) içindeki ona_geç() (switch_to) makrosu harekete geçtikten hemen sonra çalışmağı durduracaktır.

3.4.2 Süreç Kaldırılışı (Process Removal)
Unix işletim sistemi bir sürecin ebeveyninin SüreçKimlikNosu (PID)’nu temin etmek için ya da çocuklarından herhangi birinin çalışım halini temin etmek için o sürecin çekirdeği sorgulayışına izin verir.

Bir süreç, örneğin belirli bir görev için bir çocuk süreç yaratır ve sonra bekle() (wait())- benzeri bir sistem çağrısı icra ederek çocuğun bitip bitmediğini kontrol eder. Eğer çocuk bitmiş ise, bitiş kodu ebeveyn sürece görevin başarı ile yapılıp yapılmadığını anlatır. Bu tasarım seçeneklerine uymak için, Unix çekirdeklerinin bir süreç bittikten hemen sonra süreç tanımlayıcı alanı içine dahil edilen verileri çöpe atmalarına izin verilmez. Böyle yapmalarına yalnız ebeveyn süreç bir bekle() (wait()) benzeri sistem çağrısını yaptıktan sonra izin verilir, sona erdirilmiş sürece ilişkin. GÖREV_ZOMBİE (TASK_ZOMBIE) hali işte bu yüzden kullanılmıştır: süreç teknik açıdan ölü olsa bile, tanımlayıcısı, ebeveyn süreci haberdar edilinceye kadar saklanmalıdır.

Eğer ebeveyn süreç çocuklarından önce sona ererse ne olur? Böyle bir durumda, sistem zombie süreçler ile dolup taşabilir, var olan görev hanelerini kullanıp tüketebilen. Daha önce bahsedildiği gibi, bu problem bütün öksüz süreçlerin zorla sıfırdan_başla (init) sürecinin çocukları haline getirilişi ile çözülür.

Bu şekilde, sıfırdan_başla süreci bir bekle()-benzeri sistem çağrısı ile kendi meşru çocuklarından birini kontrol ederken zombie’leri yok eder.

Saturday 15 December 2007

LINUX Çekirdeğini Anlamak - Süreçler 5

3.3 Süreçleri yaratış (Creating Processes)
Unix işletim sistemi kullanıcılarını memnun etmek için yüklü bir şekilde süreç yaratmağa başvurur. Örnek olarak, kabuk(shell) süreci kullanıcı ne zaman bir komut verirse kabuğun bir başka kopyasını çalıştıran yeni bir süreç yaratır. Geleneksel Unix sistemleri bütün süreçlere aynı şekilde muamele eder: ebeveyn süreç tarafından sahip olunan kaynaklar katlanır, ve bir kopyası çocuk sürece verilir.

Bu yaklaşım, süreç yaratışı çok yavaş ve verimsiz kılar, çünkü ebeveyn sürecinin bütün adres alanını kopyalamağı gerektirir. Çocuk süreç ebeveynine ait kaynakların tümünü okumak ya da değiştirmek ihtiyacını nadiren duyar; bir çok durumda, hemen bir çalıştırve() (execve( )) komutu verir ve o kadar dikkatli olarak yaratılmış adres alanını siliverir. Çağdaş Unix çekirdekleri bu sorunu üç ayrı mekanizma ile çözer:

•Yazarken Kopyala (Copy On Write) tekniği ebeveyn ve çocuğun aynı fiziki sayfaları okuyuşlarına olanak verir. Ne zaman biri fiziki sayfaya yazmağa kalkışırsa, çekirdek onun içeriğini yazan sürece atanmış olan yeni bir fiziksel sayfaya kopyalar.

• Hafifsıklet süreçler hem ebeveynin hem de çocuğun süreç başına yaratılan bir çok çekirdek veri yapısını paylaşmalarına olanak sağlar, sayfalayış (paging) tabloları (ve dolayısıyla bütün Kullanıcı Çalışış Türü adres alanı (User Mode address space) ) ve açık dosya tabloları gibi.

• vdallan() (vfork( )) sistem çağrısı ebeveyninin bellek adres alanını paylaşan bir süreç yaratır.
Çocuğun ihtiyaç duyduğu verinin ebeveyn tarafından üstüne yazılmaması için, ebeveynin çalıştırılışı çocuk bitinceye ya da çocuk yeni bir program çalıştırıncaya kadar engellenir.

3.3.1 Kopyala(), dallan(), ve vdallan() Sistem Çağrıları (The clone( ), fork( ), and vfork( ) System Calls)
Hafifsıklet süreçler Linux tarafından __kopyala() (__clone( )) adlı bir fonksiyon kullanarak yaratılırlar, dört tane parametre kullanan:

fn Yeni süreç tarafından çalıştırılan bir fonksiyon belirler; fonksiyondan geri dönüldüğünde, çocuk sona erer. Fonksiyon bir tam sayı (integer) döndürür, çocuk sürecinin bitiş kodunu temsil eden.

Arg fn( ) fonksiyonuna geçirilen veriye işaret eden bir işaret edici.

Bayraklar (Flags) Çeşitli bilgiler. Alçak byte çocuk bittiğinde ebeveyn sürece gönderilecek işaret (signal) sayısını belirler; İŞAÇOCUK (SIGCHLD) işareti genellikle seçilir. Kalan 3 byte bir grup kopyalayış bayrağını (clone flags) şifreler (encode), çocuk ile ebeveyn arasında paylaşılan kaynakları belirleyen. Bayraklar, kuruluklarında aşağıdaki anlamlara sahiptir:

KOPYA_SB (CLONE_VM) Sanal bellek tanımlayıcı ve bütün sayfa tabloları.
KOPYA_DS (CLONE_FS) :Kök kütüphaneyi ve şu anda çalışılan kütüphaneyi belirleyen tablo.
KOPYA_DOSYALAR (CLONE_FILES) : Açık dosyaları belirleyen tablo.
KOPYA_İŞAMUA (CLONE_SIGHAND) : İşaret muamele edicileri belirleyen tablo. KOPYA_SURECKIMLIKNO (CLONE_PID) : Süreç kimlik nosu.
KOPYA EİZLE (CLONE_PTRACE) : Eğer bir eizle() (ptrace()) sistem çağrısı ebeveyn sürecin izlenmesine(traced) neden oluyorsa, çocuğu da izlenecektir.
KOPYA_VDALLAN (CLONE_VFORK) : vdallan() (vfork( )) sistem çağrısı için kullanılır.
Çocuk_yığını (child_stack) çocuk sürecin esp kayıtına (esp register) atanacak olan Kullanıcı Çalışış Türü yığın işaret edicisini belirler.
3.3.2 Çekirdek Bağları (Kernel Threads)
Geleneksel Unix sistemleri bazı kritik görevleri arada çalışan süreçlere atar, disk tamponlarını yazmak, kullanılmayan sayfa çerçevelerini değiştirmek (swapping out unused page frames), ağ bağlantılarına hizmet vermek ve diğerleri gibi. Gerçekten, bu görevleri katı doğrusal üslupta yerine getirmek etkin olmaz; hem işlevleri hem de eğer arkaplanda çalışırlarsa son kullanıcı süreçleri daha iyi yanıt alır.

Bazı sistem süreçleri yalnız Çekirdek Çalışım Türünde koştukları için, çağdaş işletim sistemleri işlevlerini çekirdek bağlarına (kernel threads) atarlar, gereksiz Kullanıcı Çalışım Türü ile ağırlaşmayan. Linux’ta çekirdek bağları alışımış süreçlerden şu açılarda farklıdır:

• Her çekirdek bağı tek bir özel çekirdek fonksiyonunu çaıştırır, alışılmış süreçler ise çekirdek fonksiyonlarını yalnız sistem çağrıları içinden çalıştırır.

• Çekirdek bağları yalnız Kernel Çelrşrm Türünde koşarlar, alışılmış süreçler ise bazan Çekirdek bazan Kullanıcı Çalışım Türünde koşarlar.

• Çekirdek bağları yalnız Çekirdek Çalışım Türünde çalıştıkları için, yalnız SAYFA_ÖTELEYİŞİ (PAGE_OFFSET) ’ten büyük doğrusal adresleri kullanırlar. Alışılmış süreçler, öte yandan, yalnız 4 gigbyte doğrusal adres kullanırlar, Kullanıcı ay da Çekirdek Çalışım Türlerinden her birinde.

3.3.2.1 Bir çekirdek bağı yaratmak (Creating a kernel thread)
çekirdek_bağı() (kernel_thread()) fonksiyonu yeni bir çekirdek bağı yaratır ve yalnız bir başka çekirdek bağı tarafından çalıştırılabilir. …
3.3.2.2 Süreç 0 (Process 0)
Bütün süreçlerin atası veya tarihsel nedenlerle değiştirici süreç (swapper process) adı verilen
Süreç0 (process0), Linux’un başlatılış aşamasında çekirdeği_başlat() (start_kernel()) fonksiyonunca sıfırdan yaratılan bir çekirdek bağıdır.

3.3.2.3 Süreç 1 (Process 1)
Süreç tarafından yaratılan çekirdek bağı sıfırdan-başlat() (init()) fonksiyonunu çalıştırır, o da çekirdek_bağı() (kernel_thread( )) fonksiyonunu dört defa çalıştırarak sıradan çekirdek görevleri için gerekli dört çekirdek bağını başlatır.

kernel_thread(bdflush, NULL, CLONE_FS CLONE_FILES CLONE_SIGHAND);
kernel_thread(kupdate, NULL, CLONE_FS CLONE_FILES CLONE_SIGHAND);
kernel_thread(kpiod, NULL, CLONE_FS CLONE_FILES CLONE_SIGHAND);
kernel_thread(kswapd, NULL, CLONE_FS CLONE_FILES CLONE_SIGHAND);














Thursday 13 December 2007

Linux Çekirdeğini Anlamak - Süreçler 4

3.2 Süreç Açıp Kapayış (Process Switching)
Süreçlerin çalıştırılışını kontrol etmek için, çekirdek MerkeziİşlemBirimi üzerinde çalışmakta olan sürecin çalıştırılışını askıya alabilmek ve daha önce askıya alınmış başka bir sürecin çalıştırılışını kaldığı yerden devam ettirebilmek zorundadır. Bu faaliyet süreç açıp-kapayış ( process switching) , görev açıp-kapayış (task switching), ya da bağlam açıp-kapayış (context switching) olarak adlandırılır.

Aşağıdaki kısımlar Linux’ta süreç açıp-kapayış’ın unsurlarını tarif etmektedir:
• Donanım bağlamı Hardware context
• Donanım desteği Hardware support
• Linux kodu Linux code
• Yüzen nokta kayıtlarının saklanışı Saving the floating point registers

3.2.1 Donanım Bağlamı (Hardware Context)
Her süreç kendine ait bir adress alanına sahip olabildiği gibi, bütün süreçler MerkeziİşlemBirimi kayıtlarını (register) paylaşmak zorundadırlar. Dolayısıyla bir sürecin çalıştırılışına kaldığı yerden devam etmeden önce, çekirdek bunun gibi her kayıtın(register), süreç askıya alınmadan önce sahip olduğu değerle yeniden yüklendiğini
Garantiye almalıdır.

Süreç MerİşBrmi üzerinde çalışışına kaldığı yerden devam etmeden önce kayıtlara(registers) yüklenmek zorunda olan veri grubuna donanım bağlamı (hardware context) adı verilir. Donanım bağlamı süreç çalıştırılışı için ihtiyaç duyulan bütün bilgileri içeren, süreç çalıştırış bağlamının bir altkümesidir. Linux’ta, bir sürecin donanım bağlamının bir kısmı TSS bölmesine(segment), kalan kısmı da Çekirdek Çalışma Türü Yığınına (Kernel Mode stack) saklanır. TSS bölmesi süreç tanımlayıcının tss alanına den düşer.


önceki (prev) yerel değişkeninin (local variable) kapatılan sürecin süreç tarif edicisine ve sonraki (next)’in açılmakta olup onun yerine geçeni belirttiğini kabul edeceğiz. Böylece süreç açıp-kapayışı öncekinin (prev) donanım bağlamını saklamak ve onun yerine sonrakinin (next) donanım bağlamını yerleştirmek faaliyeti olarak tanımlayabiliriz. Süreç açıp kapayışları sık sık olduğundan, donanım bağlamlarını saklamak ve yeniden yüklemek için harcanan zamanı en aza indirmek önemlidir.

3.2.2 Görev Hali Bölmesi (Task State Segment)
Intel 80x86 mimarisi, donanım bağlamlarını saklamak için, Görev Hali Bölmesi (Task State Segment (TSS)) adı verilen özel bir bölme türü içerir. Her süreç en az 104 byte uzunluğunda kendi TSS bölmesini içerir. Donanım tarafından otomatik olarak kaydedilmemiş kayıtları (registers) ve G/Ç İzin haritasını (I/O Permission bitmap) depolamak için işletim sistemi ek byte’lara ihtiyaç duyar. Bu harita ioperm( ) ve iopl( ) sistem çağrıları Kullanıcı Çalışma Türündeki (User Mode) bir sürece belirli G/Ç kapılarına doğrudan erişim izni verebildiği için gereklidir.

3.2.3 Değiştir Makrosu (The switch_to Macro)
değiştir() (switch_to) makrosu bir süreç açış işlemini icra eder. Önceki (prev) ve sonraki (next) ile belirtilen iki parametre kullanır: ilki askıya alınacak sürecin süreç tanımlayıcı işaretedicisi, ikincisi MİB üzerinde çalıştırılacak sürecin süreç tanımlayıcısı işaret edicisidir. Bu makro planla() (schedule( )) fonksiyonunca MİB üzerinde yeni bir süreç çalıştırmak için plan yapılmak amacı ile harekete geçirilir. değiştir (switch_to) makrosu çekirdeğin en çok donanım-bağımlı alt-programlarından biridir.

3.2.4 Kayan Nokta Kayıtlarının Saklanışı (Saving the Floating Point Registers)
80486 ile başlayarak, AKU Aritmetik Kayan Nokta Birimi (arithmetic floating point unit (FPU)) MİB içine birleşik inşa edildi. Matematik ortakişlemcisi (mathematical coprocessor) kayan nokta hesaplarının pahalı özel amaçlı yongalarla uygulandığı günlerin anısı için hala kullanılmaktadır. Eski modellerle uyumluluğu sürdürmek için, yine de, kayan nokta aritmetiği fonksiyonları KAÇ (ESCAPE) komutlarını kullanmaya başvurarak icra edilmektedir. KAÇ komutları öncül (prefix) byteları 0xd8 ve 0xdf arasında değişen komutlardır. Bu komutlar MİB’ne dahil edilen bir küme kayan nokta kayıtı üzerinde etkili olurlar. Açıkçası, eğer bir süreç KAÇ (ESCAPE) komutları kullanıyorsa, kayan nokta kayıtlarının içerikleri onun donanım bağlamına aittir. Intel 80x86 mikroişlemcileri kayan nokta kayıtlarını TSS içine otomatik olarak saklamazlar. Yine de, çekirdeklerin bu kayıtları yalnız gerekli olduğunda saklamalarına olanak sağlayan bazı donanım destekleyicilerini içerirler.

Wednesday 12 December 2007

LINUX Çekirdeğini Anlamak - Süreçler 3

3.1.3 Süreçler Arasında Ebeveynlik İlişkileri (Parenthood Relationships Among Processes)
Bir program tarafından yaratılan süreçler ebeveyn/çocuk ilişkisine sahiptirler.
Bir süreç birden çok sayıda çocuk yaratabileceklerinden, bunlar kardeşlik ilişkisine sahip olurlar. Bu ilişkileri temsil etmek için bir süreç tanmlayıcıda çok sayıda alan kullanılması gerekir. Süreç 0 ve 1 çekirdek tarafından yaratılır; süreç 1 (sıfırla (init)) diğer bütün süreçlerin atasıdır.

Bir P sürecinin tanımlayıcısı aşağıdaki alanlara içerir:
p_opptr (doğal ebeveyn) (original parent)
p_pptr (ebeveyn) (parent)
p_cptr (çocuk) (child)
p_ysptr (daha genç kardeş) (younger sibling)
p_osptr (daha yaşlı kardeş) (older sibling)


Şekil 3-6. Beş süreç arasında ebeveynlik ilişkileri
Parenthood relationships among five processes

3.1.4 Bekleyiş Kuyrukları (Wait Queues)
Çalıştırışkuyruğu (runqueue) listesi bir GÖREV_ÇALIŞIYOR (TASK_RUNNING) halindeki bütün süreçleri biraraya getirir. Başka hallerdeki süreçleri gruplayışa gelince, çeşitli haller farklı tür ele alışı gerektirir, Linux’un aşağıdaki seçeneklerden birini tercih edişine yol açarak:

• GÖREV-DURDU (TASK_STOPPED) veya GÖREV_ZOMBİ (TASK_ZOMBIE) halindeki süreçler belirli listelere bağlanmaz. Bunları gruplamağa gerek yoktur, çünkü çocuk sürece erişmek için ebeveyn süreç tarafından ya süreç SüreçKimlikNo’su (PID) ya da süreç ebeveynlik ilişkileri kullanılabilir.

• GÖREV_KESİLEBİLİR (TASK_INTERRUPTIBLE) veya GÖREV_KESİLEMEZ (TASK_UNINTERRUPTIBLE) halindeki süreçler her biri belirli bir olaya (event) denk düşen çok sayıda alt sınıfa ayrılabilir. Bu durumda, süreç hali sürece çabukça erişmek (retrieve) için yeterli bilgi sağlamaz, bu yüzden ek süreç listeleri kullanmak gerekir. Bu ek listelere bekleyiş listeleri (wait queues) adı verilir.

Bekleyiş listeleri çekirdek içinde bir çok kullanımlara sahiptir, özellikle kesiş muamelesi (interrupt handling), süreç eşzamanlayışı (process synchronization), ve zamanlayış (timing) için. Bir süreç genellikle bir olayın gerçekleşişini beklemek zorundadır; bir disk işleminin bitişi, bir sistem kaynağının serbest bırakılışı, veya sabit aralık zamanın doluşu gibi… Bekleyiş kuyrukları olaylara bağlı koşullu bekleyişleri gerçekleştirir: belirli bir olayı beklemek isteyen bir süreç kendini bir bekleyiş kuyruğuna yerleştirir ve kontrolü serbest bırakır. Bunun yüzünden, bir bekleyiş kuyruğu bir koşul sağlandığında çekirdek tarafından uyandırılan bir grup uyuyan süreci temsil eder.

Bekleyiş kuyrukları elemanları süreç tanımlayıcılara işaret ediciler içeren çembersel listeler olarak gerçekleştirilir. Bir bekleyiş kuyruğunun her elemanı bekleyiş_kuyruğu (wait_queue) türündendir.

yapı görev_kuyruğu { struct wait_queue {
yapı görev_yapısı * görev; struct task_struct * task;
yapı bekleyiş_kuyruğu * sonraki; struct wait_queue * next;
}; };

Her bekleyiş kuyruğu bir bekleyiş kuyruğu işaret edicisi(wait queue pointer) tarafından belirlenir, ilk elemanın adresini taşıyan ya da eğer liste boş ise, boş işaret ediciyi taşıyan…
bekleyiş_kuyruğu (wait_queue) veri yapısı listedeki sonra gelen ilk elemana işaret eder, sonraki (next) alanı anlamsız (dummy) bir liste elemanına işaret eden son eleman hariç.
Anlamsız(dummy)’ın sonraki (next) alanı bekleyiş kuyruğundan bir işaret edici genişliği azını işaret eden bir değişken ya da alanın adresini taşır (İntel platformlarında bir işaret edici 4 byte uzunluğundadır). Böylece, bekleyiş kuyruğu listesi çekirdek fonksiyonları tarafından gerçek bir çembersel liste şeklinde değerlendirilebilir, son elemanın sonraki(next) alanı, bekleyiş kuyruğu işaretedicisi ile çakışan anlamsız(dummy) bekleyiş kuyruğu yapısına işaret ettiği için… (Şekil 3-7 ’ye bakınız).


Şekil 3-7. Bekleyiş kuyruğu veri yapısı
The wait queue data structure

bekleyişkuyruğunu_sıfırla (init_waitqueue()) fonksiyonu boş bir bekleyiş kuyruğunu sıfırlar; bekleyişkuyruğuna_ekle(kyrk, hane) (add_wait_queue(q,entry)) fonksiyonu hane(entry) adresli yeni haneyi bekleyiş kuyruğu işaretedicisi kyrk (q) ile belirlenen bekleyiş kuyruğuna ekler.


Bekleyiş kuyrukları önemli çekirdek fonksiyonları kadar kesiş muamele edicileri (interrupt handlers) tarafından da değiştirildikleri için, bu fonksiyon aşağıdaki işlemleri engellenmiş kesişlerle(disabled interrupts) çalıştırır:

3.1.5 Süreç Kullanım Sınırları (Process Usage Limits)
Süreçler kullanım sınırları (usage limits ) grupları ile ilişkilendirilirler, kullandıkları sistem kaynakları miktarını belirleyen.

Özellikle, Linux aşağıdaki sınırları tanır:
RLIMIT_CPU Süreç için Merkezi işlem Birimi zaman üst sınırı
RLIMIT_FSIZE İzin verilen dosya uzunluğu üst sınırı
RLIMIT_DATA Düz bellek (heap) büyüklüğü üst sınırı
RLIMIT_STACK Yığın(stack) büyüklüğü üst sınırı
RLIMIT_CORE Nüve dökümü (core dump) dosya büyüklüğü üst sınırı
RLIMIT_RSS Süreç tarafından sahip olunan sayfa çerçeveleri sayısı üst sınırı
RLIMIT_NPROC Bir kullanıcının sahip olabileceği en çok süreç sayısı
RLIMIT_NOFILE Açık dosya sayısı üst sınırı
RLIMIT_MEMLOCK Maximum size of nonswappable memory
RLIMIT_AS Süreç adres alanı büyüklüğü üst sınır





Saturday 8 December 2007

LINUX Çekirdeğini Anlamak – Süreçler 2

Ali Rıza SARAL(1)

(1) Daniel P. Bovet, Marco Cesati, Understanding the Linux Kernel ‘den faydalanarak derlenmiştir.

3.1.2.4 Süreç Listesi (The process list)
Verilmiş bir çeşit süreçlerin arasında etkili bir arayışa imkan sağlamak için (örn., çalışabilir halde olan bütün süreçler) çekirdek, bu süreçlerin bir çok listesini yaratır. Her liste süreç tanımlayıcılara işaretedicilerden oluşur.

Bir liste işaretedicisi(yani, herbir sürecin bir sonraki sürece işaret etmek için kullandığı alan) doğrudan süreç tanımlayıcı veri yapısı içine gömülüdür. görev_yapısı (task_struct)’nın C-dili tanımlanışına baktığınızda, tanımlayıcılar kendi üstlerine karışık bir tekrarçağıran (recursive) tavırla döner gibi gözükürler.

Yine de, bu kavram kendisinin bir başka gerçeklenişine bir işaret edici taşıyan bir veri yapısı olan bir listeden daha karışık değildir. Çembersel çift bağlı liste (bkz. Şekil 3-3) var olan bütün süreç tanımlayıcıları birbirlerine bağlar; buna süreç listesi adını vereceğiz.

Listeyi gerçekleştirmek için her süreç tanımlayıcının önceki_görev ve sonraki_görev (prev_task and next_task) alanları kullanılır. görev dizisinin ilk elemanı tarafından belirtilen(referenced) görevi-sıfırla (init_task) tanımlayıcısı listenin başıdır: bütün süreçlerin atasıdır(anchestor), ve one süreç0(process 0) ya da swapper adı verilir. görev_sıfırla(init_task )’nın önceki_görev (prev_task) alanı listeye eklenen en son süreç tanımlayıcıya işaret eder.


Şekil 3-3. Süreç listesi

BAĞLARI_KUR (SET_LINKS) ve BAĞLARI_KALDIR (REMOVE_LINKS) makroları süreç listesine bir süreç eklemek veya kaldırmak için kullanılır. Bu makrolar aynı zmanda sürecin ebevyn ilişkisininde çaresine bakar. her_görev_için (for_each_task) adı verilen bir başka kullanışlı makro bütün süreç listesini tarar.

Bu makro çekirdek programcısı çevrimi sağladıktan sonra gelen çevrim kontrol ibaresi (statement). görev_sıfırla (init_task) süreç tanımlayıcısının basitçe liste başı rolünü nasıl oynadığına dikkat ediniz. Makro görev_sıfırla (init_task)’dan sonraki göreve geçerek harekete başlar ve tekrar görev_sıfırla ’ya ulaşıncaya kadar devam eder.

3.1.2.5 GÖREV_ÇALIŞIYOR süreçlerinin listesi (The list of TASK_RUNNING processes)
MerkeziİşlemBirimi (CPU) üzerinde çalıştırmak için yeni bir süreç ararken, çekirdeğin yalnız çalıştırılabilir (runnable) süreçleri dikkate almaklığı gerekir(yani GÖREV_ÇALIŞIYOR (TASK_RUNNING) durumundaki süreçler) . Bütün süreç listesi taramak çok etkisiz olacağından, çalıştırış kuyruğu (runqueue) adı verilen bir çift bağlı çembersel liste geliştirilmiştir. Süreç tanımlayıcılar çalıştırış kuyruğu (runqueue) ’yu gerçekleştirmek için sonraki_çalışan ve önceki_çalışan (next_run and prev_run) alanlarını içerir.

Bir önceki durumda olduğu gibi, görev_sıfırla (init_task) süreç tanımlayıcısı liste başı rolünü oynar. sy_çalıyor (nr_running) değişkeni çalıştırılabilir süreçlerin toplam sayısını saklar. çalıştırış_kuyruğuna_ekle (add_to_runqueue( )) fonksiyonu bir sürec tanımlayıcıyı listenin başlangıcına ekler, çalıştırış_kuyruğundan_sil (del_from_runqueue( )) ise bir süreç tanımlayıcıyı listeden çıkartır.

Çalıştırım planlamak amacı ile, süreç tanımlayıcıyı başa ya da sona almak için iki fonksiyon, çalıştırış_kuyruğunda_başa_al ve çalıştırış_kuyruğunda_sona_al (move_first_runqueue( ) ve move_last_runqueue( )) sağlanmıştır.

Son olarak, süreci_uyandır() (wake_up_process( )) fonksiyonu bir süreci çalştırılabilir yapmak için kullanılır. Süreç halini GÖREV_ÇALIŞIYOR (TASK_RUNNING)’a ayarlar, süreci çalıştırış kuyruğuna sokmak için çalıştırışkuyruğuna_ekle() ( add_to_runqueue( ))’yi harekete geçirir ve sy_çalıyor (nr_running) ’i arttırır.

Süreç gerçek-zaman (real-time) olduğu veya şu anki süreçten çok daha büyük bir devinimsel öncelik(dynamic priority) sahibi olduğunda çalıştırım planlayıcıyı zorla harekete geçirir.

3.1.2.6 süreçkimliknosueşleştiriş tablosu ve zincirlenmiş listeler (The pidhash table and chained lists)
Bir çok durumda, çekirdek SüreçKimlikNosu (PID)’ndan süreç tanımlayıcı işaretedicisini çıkartabilmek zorundadır. Bu örneğin, öldür() (kill( )) sistem çağrısına hizmet ederken olur: P1 süreci bir başka sürece, P2, işaret göndermek istediğinde, P2’nin SüreçKimlikNo’sunu parametre olarak belirleyerek öldür() sistem çağrısını harekete geçirir.

Çekirdek süreç tanımlayıcı işaretedicisini SüreçKimlikNo’sundan çıkarır ve daha sonra P2 süreç tanımlayıcısından gelip beklemekte olan (pending) işaretleri kayıt eden veri yapısına işaretediciyi elde eder. Süreç listesini sıradan taramak ve süreç tanımlayıcıların pid alanlarını kontrol etmek başvurulabilir fakat hiç te etkin olmaz.

Arayışı hızlandırmak için, SÜREÇKİMLİKNOSUEŞLEŞTİRİŞ_SY (PIDHASH_SZ) sayısında elemandan oluşan bir süreçkimliknosueşleştiriş tablosu (pidhash hash table) ’a başvurulmuştur (PIDHASH_SZ genellikle GÖREV_SY (NR_TASKS)/4 ‘tür). Bu tablo haneleri süreç tanımlayıcı işaretedicilerini taşır.

SüreçKimlikNo’su süreçkimliknosu_eşleştirişfonksiyonu (pid_hashfn) makrosunu kullanarak bir tablo indexine dönüştürülür.





Şekil 3-4. Süreçkimliknosueşleştiriş tablosu ve zincirlenmiş listeler Zincirleyerek eşleştiriş
SüreçKimlikNo’larından tablo indexlerine doğrusal bir dönüştürüşe terih edilir, çünkü SüreçKimlikNo’su 0 ile 32767 arasında herhangi bir değeri alabilir. GÖREV_SY(NR_TASKS) , yani süreçlerin alabileceği en büyük sayı değeri genellikle 512 olduğundan, 32768 haneden oluşan bir tablo tanımlamak bir depo ziyanı olurdu.
SüreçKimNo_eşleştir (hash_ pid( ) ve SüreçKimNo_karşılıkBul (unhash_ pid( )) fonksiyonları süreçKimlikNoEşleştiriş tablosu (pidhash table) ’na süreç eklemek veya çıkarmak için kullanılır.
SüreçKimNo_ile_gorev_bul(find_task_by_pid( )) fonksiyonu işleştiriş tablosunu arar ve belirli bir SüreçKimlikNosu ile verilen bir sürecin süreç tanımlayıcı işaretedicisini geri verir (ya da eğer süreci bulmazsa boş bir işaret edici
3.1.2.7 Serbest görev hanelerinin listesi (The list of task free entries)
Her süreç yaratılışında ya da yok edilişnde gorev (task) dizisi güncelleştirilmek zorundadır. Diziye yeni bir hane eklenişi etkin bir şekilde yapılır:diziyi doğrusal olarak arayıp ilk serbest haneyi bulmaktansa, çekirdek ayrı bir çift bağlı, çembersel olmayan bir serbest haneler listesi tutar. görevdizisi_serbestlistesi (tarray_freelist) o listenin ilk elemanını içerir; listedeki her serbest hane bir başka serbest haneye işaret eder, listenin en son elemanı boş bir işaretedici içerir.
Bir süreç yok edildiğinde, gorev (task) dizisi içindeki karşı düşen eleman liste başına eklenir. Şekil 3-5’ te, eğer ilk eleman 0 olarak sayılırsa, görevdizisi_serbestlistesi (tarray_freelist) değişkeni 4. elemana işaret eder çünkü o son serbest bırakılan elemandır. Önceden, 2. ve 1. elemanlara karşı düşen süreçler o sıra ile yok edilmişler. 2. eleman şekilde gösterilmeyen bir başka serbest gorevler (tasks) elemanına işaret eder. Şekil 3-5. Bir serbest haneli görev dizisi örneği

Diziden bir hane yok edilişi de etkin bir şekilde yapılır. Her süreç tanımlayıcı p , p’ye işaret ediciyi taşıyan görev (task) hanesine işaret eden bir görevdizisi_işaretedicisi (tarray_ ptr) alanı içerir. serbest_gorevhanesi_al (get_free_taskslot( ) ve serbest_gorev_hanesi_ekle (add_free_taskslot( )) fonsiyonları serbest bir hane almak ya da serbest bırakmak için kullanılır.

Thursday 29 November 2007

LINUX Çekirdeğini Anlamak – Süreçler 1 (Teknik İngilizce Çeviri Örneği)

LINUX Çekirdeğini Anlamak – Süreçler 1
Ali Rıza SARAL(1)

(1) Daniel P. Bovet, Marco Cesati, Understanding the Linux Kernel ‘den faydalanarak derlenmiştir.
(2) Bu yazı biraz da okuyucunun teknik İngilizce’sini geliştirmek amacı ile yazılmıştır.
(3) Bir diğer amaç ta teknik metinlerin Türkçe’ye tercüme edilişi hakkında tartışmağa imkan sağlamaktır.

Bölüm 3. LINUX’ta Süreçler
Chapter 3. Processes

Süreç kavramı her bir çoğul-programlayıcı (multi-programming) işletim sistemi için temel bir kavramdır. The concept of a process is fundamental to any multiprogramming operating system.

Bir süreç genellikle bir programın çalıştırılmakta olan bir örneğidir (instance);
A process is usually defined as an instance of a program in execution;

böylece, eğer 16 kullanıcı aynı anda vi programını çalıştırıyorsa, 16 tane ayrı süreç vardır
(aynı çalıştırılabilir kodu paylaşabildiklerine karşın).
thus, if 16 users are running vi at once, there are 16 separate processes (although they can share the same executable code).

Süreçlere Linux kaynak kodunda “görev” (task) adı verilir. Processes are often called "tasks" in Linux source code.

3.1 Süreç Tanımlayıcı (Process Descriptor)
Süreçleri yönetmek için, çekirdek, her sürecin ne yaptığına ilişkin net bir resme sahip olmalıdır. In order to manage processes, the kernel must have a clear picture of what each process is doing.

Örneğin, süreç önceliğini, CPU üzerinde çalışır durumda ya da bir olay nedeni ile tıkanmış olduğunu, hangi adres alanının ona atanmış olduğunu, hangi dosyalara erişişine izin verildiğini, ve benzerşeyleri bilmek zorundadır. It must know, for instance, the process's priority, whether it is running on the CPU or blocked on some event, what address space has been assigned to it, which files it is allowed to address, and so on.

Bu süreç tanımlayıcı’nın, yani tek bir sürece ilişkin bütün bilgileri içeren görev_yapısı (task_struct) tipinde bir yapının rolüdür. This is the role of the process descriptor , that is, of a task_struct type structure whose fields contain all the information related to a single process.

Şekil 3-1. Linux süreç tanımlayıcısı


3.1.1 Süreç Hali (Process State)
İsminden anlaşıldığı gibi, süreç tanımlayıcının hal (state) alanı süreç içinde o anda ne olduğunu tarif eder. As its name implies, the state field of the process descriptor describes what is currently happening to the process.

Her biri süreç durumunu tarif eden bir dizi bayraktan oluşur.
It consists of an array of flags, each of which describes a possible process state.

Güncel Linux sürümünde bu haller karşılıklı olarak birbirlerini hariç tutar, ve böylece kümenin yalnız bir bayrağı kaldırılımış diğerleri indirilmiş olur.
In the current Linux version these states are mutually exclusive, and hence exactly one flag of state is set; the remaining flags are cleared.

Aşağıdakiler mümkün süreç halleridir: The following are the possible process states:

GÖREV_ÇALIŞIYOR (TASK_RUNNING) Süreç ya CPU üstünde çalışıyor ya da çalıştırılmak için bekliyor. TASK_RUNNING The process is either executing on the CPU or waiting to be executed.

GÖREV_KESİNTİYE_UĞRAYABİLİR (TASK_INTERRUPTIBLE) Bir koşul gerçekleninceye kadar süreç askıda (uyuyor). TASK_INTERRUPTIBLE The process is suspended (sleeping) until some condition becomes true.

Bir donanım kesintisini başlatmak, sürecin beklediği bir sistem kaynağını serbest bırakmak, veya bir işaret teslim etmek bir süreci uyandıran koşullara örnektir, yani sürecin halini geriye
GÖREV_ÇALIŞIYOR (TASK_RUNNING) ’a getirirler. Raising a hardware interrupt, releasing a system resource the process is waiting for, or delivering a signal are examples of conditions that might wake up the process, that is, put its state back to TASK_RUNNING .

GÖREV_KESİNTİYE_UĞRATILAMAZ (TASK_UNINTERRUPTIBLE) bir önceki hal gibidir, yalnız uyuyan sürece bir işaret teslim edilişi hali değiştirmez. TASK_UNINTERRUPTIBLE Like the previous state, except that delivering a signal to the sleeping process leaves its state unchanged.

Bu süreç nadir olark kullanılır. Yine de, bir sürecin öngörülmüş bir olay oluncaya kadar kesintiye uğramadan beklemesi gerektiği belirli özel koşullar altında değerlidir.
This process state is seldom used. It is valuable, however, under certain specific conditions in which a process must wait until a given event occurs without being interrupted.

Örneğin, bir süreç bir cihaz dosyasını açtığında ve denk düşen cihaz sürücüsü denk düşen donanım cihazını yoklamağa başladığında bu hal kullanılabilir. For instance, this state may be used when a process opens a device file and the corresponding device driver starts probing for a corresponding hardware device.

Cihaz sürücüsü yoklayış tamamlanıncaya kadar kesintiye uğramamak zorundadır, ya da donanım cihazı belirsiz bir halde kalabilir.
The device driver must not be interrupted until the probing is complete, or the hardware device could be left in an unpredictable state.

GÖREV_DURDU (TASK_STOPPED) Süreç çalıştırılışı durduruldu: süreç bu duruma bir SIGSTOP, SIGTSTP, SIGTTIN, veya SIGTTOU işareti aldıktan sonra girer. TASK_STOPPED Process execution has been stopped: the process enters this state after receiving a SIGSTOP, SIGTSTP, SIGTTIN, or SIGTTOU signal.

Bir süreç bir diğeri tarafından izlendiğinde (bir hata bulucu bir test programını denemek için bir ptrace( ) sistem çağrısı çalıştırdığında ki gibi), herhangi bir işaret süreci GÖREV_DURDU (TASK_STOPPED) haline koyar. When a process is being monitored by another (such as when a debugger executes a ptrace( ) system call to monitor a test program), any signal may put the process in the TASK_STOPPED state.

GÖREV_ZOMBİ (TASK_ZOMBIE) Süreç çalıştırışı bitirildi, fakat anne süreç ölü süreç hakkında bilgi geriye vermek için henüz wait( )- benzeri sistem çağrısı (wait( ), wait3( ), wait4( ), veya waitpid( )) çıkarmadı. TASK_ZOMBIE Process execution is terminated, but the parent process has not yet issued a wait( )- like system call (wait( ), wait3( ), wait4( ), or waitpid( )) to return information about the dead process.

wait( )- benzeri çağrı yapılmadan önce, çekirdek ölü süreç tanımlayıcının içerdiği veriyi çöpe atamaz, çünkü ebeveyn ona ihtiyaç duyabilir. Before the wait( )-like call is issued, the kernel cannot discard the data contained in the dead process descriptor because the parent could need it.


3.1.2 Süreci Belirleyiş (Identifying a Process)
Lunix süreçleri kendilerine ait çekirdek veri yapılarınının önemli bir kısmını paylaşabildikleri halde—hafif-sıklet süreçler(lightweight processes)—her süreç kendine ait süreç tanımlayıcısına sahiptir. Although Linux processes can share a large portion of their kernel data structures—an efficiency measure known as lightweight processes—each process has its own process descriptor.

Bağımsız olarak çalıştırım programına alınabilen her çalıştırış bağlamı kendine ait süreç tanımlayıcısı sahibi olmalıdır. Each execution context that can be independently scheduled must have its own process descriptor.

Hafif-sıklet süreçler bir kullanıcı-seviyesinde kütüphane tarafından ele alınıp farklı bir çalıştırış akışı olan, kullanıcı-üslubu bağlar(user-mode threads)’la karıştırılmamalıdır.
Lightweight processes should not be confused with user-mode threads, which are different execution flows handled by a user-level library.

Süreç ile süreç tanımlayıcı arasında çok katı bire-bir denk düşüş 32-bit süreç tanımlayıcı adresini [1] süreci belirlemek için kullanışlı bir araç yapar. The very strict one-to-one correspondence between the process and process descriptor makes the 32-bit process descriptor address[1] a convenient tool to identify processes.

Bu adreslere süreç tanımlayıcı adresleri (process descriptor pointers) olarak değinilir. Çekirdeğin yarattığı süreçlere yaptığı değinişlerin çoğu süreç tanımlayıcı işaret-vericileri(pointer) aracılığı iledir. These addresses are referred to as process descriptor pointers. Most of the references to processes that the kernel makes are through process descriptor pointers.

[1] Teknik açıdan 32 bit mantıksal adresin(logical) yalnız öteleyiş(offset) bileşenidir.
Technically speaking, these 32 bits are only the offset component of a logical address.

Yine de, Linux tek bir çekirdek veri kesimi(segment) kullandığı için, öteleyişin tam bir mantıksal adrese(whole logical address) eşdeğer kabul edebiliriz. However, since Linux makes use of a single kernel data segment, we can consider the offset to be equivalent to a whole logical address.

Dahası, kodun ve veri kesimleri(data segments)’nin taban adres(base address)’i 0 yapıldığı için, öteleyişi(offset) doğrusal bir addres gibi kullanabiliriz. Furthermore, since the base addresses of the code and data segmentsare set to 0, we can treat the offset as a linear address.

Öte yandan, herhangi bir UNIX-benzeri sistem, kullanıcılarının süreçleri Süreç Kimlik Nosu(Process ID (or PID)) adı verilen bir sayı aracılığıyla belirleyişine müsaade eder.
Any Unix-like operating system, on the other hand, allows users to identify processes by means of a number called the Process ID (or PID).

Süreç Kimlik NO’su (PID) süreç tanımlayıcının pid alanında tutulan 32-bit işaretsiz bir sayıdır. The PID is a 32-bit unsigned integer stored in the pid field of the process descriptor.

PID’ler sıra ile sayılandırılmıştır:yeni yaratılan bir sürecin PID’i normalde bir öncekinin PID’inin bir ile arttırılmışıdır. PIDs are numbered sequentially: the PID of a newly created process is normally the PID of the previously created process incremented by one.

Yine de, 16-bit donanım platformları için üretilmiş geleneksel UIX sistemleri ile uyumluluk için, LINUX üzerinde izin verilen en büyük PID sayısı 32767’dir. However, for compatibility with traditional Unix systems developed for 16-bit hardware platforms, the maximum PID number allowed on Linux is 32767.

Çekirdek sistemdeki 32768’inci süreci yarattığı zaman, aşağıdaki kullanılmamış PID’ları yeniden kullanmaya başlamalıdır. When the kernel creates the 32768th process in the system, it must start recycling the lower unused PIDs.

Etkinlik önemlidir çünkü öldür() ( kill( )) gibi bir çok sistem çağrısı etkilenen süreci belirtmek için PID’ı kullanır. Efficiency is important because many system calls like kill( ) use the PID to denote the affected process.

3.1.2.1 Görev dizisi (The task array)
Süreçler sistem içinde yaşam süreleri birkaç milisaniyeden aylara kadar uzanan devinimsel (dynamic) varlıklardır (entities). Processes are dynamic entities whose lifetimes in the system range from a few milliseconds to months. Thus, the kernel must be able to handle many processes at the same time.

Linux SY_GÖREV (NR_TASKS) kadar sürece muamele edebilir.
Linux is able to handle up to NR_TASKS processes.

Çekirdek kendi adress alanında görev (task) adı verilen SY_GÖREV (NR_TASKS) büyüklüğünde küresel sabit bi dizi (global static array) ayırır. Bu dizinin elemanları süreç tanımlayıcıya işaret edicilerdir (process descriptor pointers); boşluğa(null) işaret edici bir süreç tanımlayıcının o dizi elemanı ile ilişkilendirilmediğini belirtir. The kernel reserves a global static array of size NR_TASKS called task in its own address space. The elements in the array are process descriptor pointers; a null pointer indicates that a process descriptor hasn't been associated with the array entry.

3.1.2.2 Bir Süreç Tanımlayıcının Saklanışı (Storing a process descriptor)
görev (task) dizisi yalnız süreç tanımlayıcıların işaret edicilerini içine alır, çok yer kaplayan tanımlayıcıların kendilerini değil. The task array contains only pointers to process descriptors, not the sizable descriptors themselves.

Süreçler devinimsel varlıklar (dynamic entities) olduğu için, süreç tanımlayıcılar çekirdeğe kalıcı olarak atanan bellek alanına değil devinimsel belleğe depolanırlar. Since processes are dynamic entities, process descriptors are stored in dynamic memory rather than in the memory area permanently assigned to the kernel.

Linux her süreç için, 8KB bellek alanına, iki farklı veri yapısı saklar: süreç tanımlayıcı ve Çekirdek-üslubu (Kernel Mode) süreç yığını. Linux stores two different data structures for each process in a single 8 KB memory area: the process descriptor and the Kernel Mode process stack.


Şekil 3-2. Süreç tanımlayıcı ve süreç çekirdek yığınının saklanışı
Figure 3-2. Storing the process descriptor and the process kernel stack

esp kayıt-tutucusu(register) yığının en üst konum adresini tutan yığın eşaret-edicisidir.
The esp register is the CPU stack pointer, which is used to address the stack's top location.

Intel sistemlerinde, yığın sonda başlar ve bellek alanının başlangıcına doğru genişler.
On Intel systems, the stack starts at the end and grows toward the beginning of the memory area.

Kullanıcı-Üslubundan Çekirdek-Üslubuna geçişten hemen sonra, bir sürecin çekirdek yığını her zaman boştur, ve bu yüzden esp kayıt-tutucusu bellek alanından hemen sonra gelen byte’a işaret eder. Right after switching from User Mode to Kernel Mode, the kernel stack of a process is always empty, and therefore the esp register points to the byte immediately following the memory area….

3.1.2.3 Güncel Makrosu (The current macro)
Çekirdek-üslubu yığını ile süreç tanımlayıcı eşleştirişi etkinlik açısından anahtar bir fayda sağlar: çekirdek esp kayıt-tutucusunun değerinden CPU üzerinde çalışan sürecin süreç tanımlayıcısına işaret-ediciyi kolaylıkla elde edebilir.
The pairing between the process descriptor and the Kernel Mode stack just described offers a key benefit in terms of efficiency: the kernel can easily obtain the process descriptor pointer of the process currently running on the CPU from the value of the esp register.

Aslında, bellek alanı 8 KB(213 byte) uzunluğunda olduğundan, süreç tanımlayıcının taban adresini elde etmek için, çekirdeğin bütün yapması gereken, esp ‘nin en az önemli 13 bitini maskeleyerek çıkarmaktır. Bu şuanki (current) makrosu tarafından yapılır.
In fact, since the memory area is 8 KB (213 bytes) long, all the kernel has to do is mask out the 13 least significant bits of esp to obtain the base address of the process descriptor. This is done by the current macro…

Görev_yapısı-dizisi (task_struct_stack) arabölge (cache) içindeki süreç tanımlayıcılarının işaret edicilerini içinde bulundurur. The task_struct_stack array contains the pointers to the process descriptors in the cache.

İsmi süreç tanımlayıcı serbest bırakılışı ve istekte bulunuluşunun dizi üzerinde “it”(push) ve “çek”(pop) işlemleri ile yapılışından gelir:
Its name comes from the fact that process descriptor releases and requests are implemented respectively as "push" and "pop" operations on the array:

Görev_yapısını_serbest_birak (free_task_struct( )) Bu fonksiyon 8 KB’lık görev_birimi (task_union) bellek alanlarını serbest bırakır ve onları eğer boş değilse arabölge(cache)’ye yerleştirir.
free_task_struct( ) This function releases the 8 KB task_union memory areas and places them in the cache unless it is full.

Görev_yapısına_yer_ata (alloc_task_struct( )) Bu fonksiyon görev_birimi (task_union) bellek alanları için 8 KB yer atar. alloc_task_struct( ) This function allocates 8 KB task_union memory areas.

Bu fonksiyon eğer yarı-dolu ise ya da art arda gelen bir çift sayfa çerçevesi(page frame) yok ise arabölge(cache)’den bellek alanları alır.
The function takes memory areas from the cache if it is at least half-full or if there isn't a free pair of consecutive page frames available.

LINUX Çekirdeğini Anlamak – Süreçler 1


Şekil 3-1. Linux süreç tanımlayıcısı Şekil 3-2. Süreç tanımlayıcı ve süreç çekirdek yığınının saklanışı

LINUX Çekirdeğini Anlamak – Süreçler 1

Ali Rıza SARAL(1)


(1) Daniel P. Bovet, Marco Cesati, Understanding the Linux Kernel ‘den faydalanarak derlenmiştir.
(2) Bu yazı biraz da okuyucunun teknik İngilizce’sini geliştirmek amacı ile yazılmıştır.
(3) Bir diğer amaç ta teknik metinlerin Türkçe’ye tercüme edilişi hakkında tartışmağa imkan sağlamaktır. Bu amaçla cümle cümle tercümeyi içeren bir sürümünü de bulmak mümkündür.


Bölüm 3. LINUX’ta Süreçler (Processes)
Süreç kavramı her bir çoğul-programlayıcı (multi-programming) işletim sistemi için temel bir kavramdır. Bir süreç genellikle bir programın çalıştırılmakta olan bir örneğidir (instance);
böylece, eğer 16 kullanıcı aynı anda vi programını çalıştırıyorsa, 16 tane ayrı süreç vardır
(aynı çalıştırılabilir kodu paylaşabildiklerine karşın). Süreçlere Linux kaynak kodunda “görev” (task) adı verilir.

3.1 Süreç Tanımlayıcı (Process Descriptor)
Süreçleri yönetmek için, çekirdek, her sürecin ne yaptığına ilişkin net bir resme sahip olmalıdır. Örneğin, süreç önceliğini, CPU üzerinde çalışır durumda ya da bir olay nedeni ile tıkanmış olduğunu, hangi adres alanının ona atanmış olduğunu, hangi dosyalara erişişine izin verildiğini, ve benzerşeyleri bilmek zorundadır. Bu süreç tanımlayıcı’nın, yani tek bir sürece ilişkin bütün bilgileri içeren görev_yapısı (task_struct) tipinde bir yapının rolüdür.
.
Şekil 3-1. Linux süreç tanımlayıcısı


3.1.1 Süreç Hali (Process State)
İsminden anlaşıldığı gibi, süreç tanımlayıcının hal (state) alanı süreç içinde o anda ne olduğunu tarif eder. Her biri süreç durumunu tarif eden bir dizi bayraktan oluşur.
Güncel Linux sürümünde bu haller karşılıklı olarak birbirlerini hariç tutar, ve böylece kümenin yalnız bir bayrağı kaldırılımış diğerleri indirilmiş olur. Aşağıdakiler mümkün süreç halleridir:

GÖREV_ÇALIŞIYOR (TASK_RUNNING) Süreç ya CPU üstünde çalışıyor ya da çalıştırılmak için bekliyor.

GÖREV_KESİNTİYE_UĞRAYABİLİR (TASK_INTERRUPTIBLE) Bir koşul gerçekleninceye kadar süreç askıda (uyuyor). Bir donanım kesintisini başlatmak, sürecin beklediği bir sistem kaynağını serbest bırakmak, veya bir işaret teslim etmek bir süreci uyandıran koşullara örnektir, yani sürecin halini geriye GÖREV_ÇALIŞIYOR (TASK_RUNNING) ’a getirirler.

GÖREV_KESİNTİYE_UĞRATILAMAZ (TASK_UNINTERRUPTIBLE) bir önceki hal gibidir, yalnız uyuyan sürece bir işaret teslim edilişi hali değiştirmez. Bu süreç nadir olark kullanılır. Yine de, bir sürecin öngörülmüş bir olay oluncaya kadar kesintiye uğramadan beklemesi gerektiği belirli özel koşullar altında değerlidir. Örneğin, bir süreç bir cihaz dosyasını açtığında ve denk düşen cihaz sürücüsü denk düşen donanım cihazını yoklamağa başladığında bu hal kullanılabilir. Cihaz sürücüsü yoklayış tamamlanıncaya kadar kesintiye uğramamak zorundadır, ya da donanım cihazı belirsiz bir halde kalabilir.

GÖREV_DURDU (TASK_STOPPED) Süreç çalıştırılışı durduruldu: süreç bu duruma bir SIGSTOP, SIGTSTP, SIGTTIN, veya SIGTTOU işareti aldıktan sonra girer. Bir süreç bir diğeri tarafından izlendiğinde (bir hata bulucu bir test programını denemek için bir ptrace( ) sistem çağrısı çalıştırdığında ki gibi), herhangi bir işaret süreci GÖREV_DURDU (TASK_STOPPED) haline koyar.

GÖREV_ZOMBİ (TASK_ZOMBIE) Süreç çalıştırışı bitirildi, fakat anne süreç ölü süreç hakkında bilgi geriye vermek için henüz wait( )- benzeri sistem çağrısı (wait( ), wait3( ), wait4( ), veya waitpid( )) çıkarmadı. wait( )- benzeri çağrı yapılmadan önce, çekirdek ölü süreç tanımlayıcının içerdiği veriyi çöpe atamaz, çünkü ebeveyn ona ihtiyaç duyabilir.


3.1.2 Süreci Belirleyiş (Identifying a Process)
Lunix süreçleri kendilerine ait çekirdek veri yapılarınının önemli bir kısmını paylaşabildikleri halde—hafif-sıklet süreçler(lightweight processes)—her süreç kendine ait süreç tanımlayıcısına sahiptir. Bağımsız olarak çalıştırım programına alınabilen her çalıştırış bağlamı kendine ait süreç tanımlayıcısı sahibi olmalıdır. Hafif-sıklet süreçler bir kullanıcı-seviyesinde kütüphane tarafından ele alınıp farklı bir çalıştırış akışı olan, kullanıcı-üslubu bağlar(user-mode threads)’la karıştırılmamalıdır. Süreç ile süreç tanımlayıcı arasında çok katı bire-bir denk düşüş 32-bit süreç tanımlayıcı adresini [1] süreci belirlemek için kullanışlı bir araç yapar. Bu adreslere süreç tanımlayıcı adresleri (process descriptor pointers) olarak değinilir. Çekirdeğin yarattığı süreçlere yaptığı değinişlerin çoğu süreç tanımlayıcı işaret-vericileri(pointer) aracılığı iledir.

[1] Teknik açıdan 32 bit mantıksal adresin(logical) yalnız öteleyiş(offset) bileşenidir. Yine de, Linux tek bir çekirdek veri kesimi(segment) kullandığı için, öteleyişin tam bir mantıksal adrese(whole logical address) eşdeğer kabul edebiliriz. Dahası, kodun ve veri kesimleri(data segments)’nin taban adres(base address)’i 0 yapıldığı için, öteleyişi(offset) doğrusal bir addres gibi kullanabiliriz.

Öte yandan, herhangi bir UNIX-benzeri sistem, kullanıcılarının süreçleri Süreç Kimlik Nosu(Process ID (or PID)) adı verilen bir sayı aracılığıyla belirleyişine müsaade eder.
Süreç Kimlik NO’su (PID) süreç tanımlayıcının pid alanında tutulan 32-bit işaretsiz bir sayıdır. PID’ler sıra ile sayılandırılmıştır:yeni yaratılan bir sürecin PID’i normalde bir öncekinin PID’inin bir ile arttırılmışıdır. Yine de, 16-bit donanım platformları için üretilmiş geleneksel UIX sistemleri ile uyumluluk için, LINUX üzerinde izin verilen en büyük PID sayısı 32767’dir. Çekirdek sistemdeki 32768’inci süreci yarattığı zaman, aşağıdaki kullanılmamış PID’ları yeniden kullanmaya başlamalıdır. Etkinlik önemlidir çünkü öldür() ( kill( )) gibi bir çok sistem çağrısı etkilenen süreci belirtmek için PID’ı kullanır.

3.1.2.1 Görev dizisi (The task array)
Süreçler sistem içinde yaşam süreleri birkaç milisaniyeden aylara kadar uzanan devinimsel (dynamic) varlıklardır (entities). Linux SY_GÖREV (NR_TASKS) kadar sürece muamele edebilir. Çekirdek kendi adress alanında görev (task) adı verilen SY_GÖREV (NR_TASKS) büyüklüğünde küresel sabit bi dizi (global static array) ayırır. Bu dizinin elemanları süreç tanımlayıcıya işaret edicilerdir (process descriptor pointers); boşluğa(null) işaret edici bir süreç tanımlayıcının o dizi elemanı ile ilişkilendirilmediğini belirtir.

3.1.2.2 Bir Süreç Tanımlayıcının Saklanışı (Storing a process descriptor)
görev (task) dizisi yalnız süreç tanımlayıcıların işaret edicilerini içine alır, çok yer kaplayan tanımlayıcıların kendilerini değil. Süreçler devinimsel varlıklar (dynamic entities) olduğu için, süreç tanımlayıcılar çekirdeğe kalıcı olarak atanan bellek alanına değil devinimsel belleğe depolanırlar. Linux her süreç için, 8KB bellek alanına, iki farklı veri yapısı saklar: süreç tanımlayıcı ve Çekirdek-üslubu (Kernel Mode) süreç yığını.


Şekil 3-2. Süreç tanımlayıcı ve süreç çekirdek yığınının saklanışı
Figure 3-2. Storing the process descriptor and the process kernel stack

esp kayıt-tutucusu(register) yığının en üst konum adresini tutan yığın eşaret-edicisidir. Intel sistemlerinde, yığın sonda başlar ve bellek alanının başlangıcına doğru genişler. Kullanıcı-Üslubundan Çekirdek-Üslubuna geçişten hemen sonra, bir sürecin çekirdek yığını her zaman boştur, ve bu yüzden esp kayıt-tutucusu bellek alanından hemen sonra gelen byte’a işaret eder.

3.1.2.3 Güncel Makrosu (The current macro)
Çekirdek-üslubu yığını ile süreç tanımlayıcı eşleştirişi etkinlik açısından anahtar bir fayda sağlar: çekirdek esp kayıt-tutucusunun değerinden CPU üzerinde çalışan sürecin süreç tanımlayıcısına işaret-ediciyi kolaylıkla elde edebilir. Aslında, bellek alanı 8 KB(213 byte) uzunluğunda olduğundan, süreç tanımlayıcının taban adresini elde etmek için, çekirdeğin bütün yapması gereken, esp ‘nin en az önemli 13 bitini maskeleyerek çıkarmaktır. Bu şuanki (current) makrosu tarafından yapılır.

Görev_yapısı-dizisi (task_struct_stack) arabölge (cache) içindeki süreç tanımlayıcılarının işaret edicilerini içinde bulundurur. İsmi süreç tanımlayıcı serbest bırakılışı ve istekte bulunuluşunun dizi üzerinde “it”(push) ve “çek”(pop) işlemleri ile yapılışından gelir:

Görev_yapısını_serbest_birak (free_task_struct( )) Bu fonksiyon 8 KB’lık görev_birimi (task_union) bellek alanlarını serbest bırakır ve onları eğer boş değilse arabölge(cache)’ye yerleştirir.
Görev_yapısına_yer_ata (alloc_task_struct( )) Bu fonksiyon görev_birimi (task_union) bellek alanları için 8 KB yer atar. alloc_task_struct( ). Bu fonksiyon eğer yarı-dolu ise ya da art arda gelen bir çift sayfa çerçevesi(page frame) yok ise arabölge(cache)’den bellek alanları alır.

Monday 2 July 2007

10 questions that you should ponder on before you enter a program conversion process

10 questions that you should ponder on before you enter a program conversion process
(for ex. from Natural/ADABAS to JAVA-J2EE ORACLE).

1- What is program conversion?
- Program conversion
- Platform conversion
- operating system change (IBM MVS to UNIX)
- hardware change (for ex. from IBM mainframe to distributed workstations)
-Data conversion (for ex. from ADABAS to ORACLE)

2- What are the problems that you may encounter?
- loss of some functions that you had at the previous system
- maintainability
- you have a converted system but the lines of code has increased
three times the original, low readibility, few documentation
- you do not have the ability to create new versions by changing the
converted programs
- you can not create new versions, you are totally dependent on the program converting company on this matter
- the converted system works but very slow

3- Is program conversion an easy process?
- The realization of the conversion process may be too cumbersome.
- The conversion process may be done at sites geographically far etc…
- Using Internet may not be an option by the customer even for testing or demo purposes.
- If the process is not managed properly, you may get tangled to millions of lines of code, something missing somewhere etc…

4- What is the cost?
- You should carefully estimate the cost of having your programs converted
in comparison to rewriting them with a labor based approach… If not you may come
to questioning this point down the road anyway…
- Payments should be clear… How much you will pay for what? It should be clear how the new versions after conversion, before conversion will be payed? Some times the customer can not finalize its bottom line for the software…
- Who is going to pay for the maintenance?
- WWWill you be able to change the converted programs?

5- References of the conversion company?
-The team size?
- The qualifications of the team members etc…

6- What does the contract include?
-How will the two sides decide that the job is done?
-How many versions will be produced? Which one will be considered the first?
-What will happen to the forgotten or missing programs?
-Will you have the program development environment and be able to do small corrections?
-What is the measuring unit? How will the work done be measured and payed for?
-How will subsequent program development be done after the conversion process is finished?

7- Do you have an upto date inventory of your programs?
-How many programs, screens, tables etc. exist in your system to be converted?
-Do you have an inventory system that lists the LOC for each of these easily?
-Which programs in your inventory are redundant or out of use?
-Do you have the source code for all the active programs that you have? Are they easily manageable or are they some where that you guess you know?

8- Does your system use special character sets, calendar etc?

9 – Are you ready for a labourous testing process?
- Do you have a functional inventory?
- Do you have a minimal test plan which includes only the ‘linearly independent’ test cases in your system?…
- Have you documented critical test paths, which are crucial for your system.
- How are you planning to catch side-effects caused by moving 2-3 million lines of code from one platform to an other?

10- You may enjoy the expertise of an outsource for conversion but are you ready for handling related problems such as communication, etc?..

Friday 29 June 2007

Notes on the use of REDUCTION in program conversion from Natural to JAVA

This is an explanation of reduction in complex statements.

There have to be two stacks. One holds the
instructions that are being processed. The other
holds the tokens in the statement.
The stack leaves are composed of two fields.

For ex:

MOVE A TO B

Requires

instruction stack:
MOVE, MOVE
Token stack:
B, MOVE-PARM2 -> TO, MOVE-TO -> A, MOVE-PARM1 -> MOVE, MOVE


This is simple as it is.
Let’s take a complex statement:

IF X = Y
MOVE A TO B
END-IF

instruction stack at first line:
IF, IF
Token stack:
X = Y, CALC -> IF, IF

instruction stack at second line:
MOVE, MOVE -> IF, IF
Token stack:
B, MOVE-PARM2 -> TO, MOVE-TO -> A, MOVE-PARM1 -> MOVE, MOVE
-> X = Y, CALC -> IF, IF

The problem here is, how are we going to convert this parsing information to
a logical meaning?

The solution is at the end of the move statement a REDUCTION should be
Made so that the token stack holds the pith of the MOVE statement, namely the
Natural statement converted to JAVA.

The solution follows:
instruction stack at the end of second line:
IF, IF
Token stack:
B = A; , MOVE-STATEMENT -> X = Y, CALC -> IF, IF

In this case, the stacks will be as below:
instruction stack at the end of third line:
nill
Token stack:
Nill

Output:
If (X==Y) {
B = A;
}


Things are not that neat though. There is a diference between a simple
MOVE statement

MOVE A TO B

And a MOVE statement in a complex statement

IF X = Y
MOVE A TO B
END-IF

The simple MOVE statement does not upush its reduction output
into the token stack but the the MOVE statement in the complex statement
does. So, just after the reduction the program checks if there are any
leaves in the instruction stack. If yes then it pushes its reduced and converted
output into the token stack.

One more note, as you may have noticed there is a need to write a pretty
Printer for the outputs.

PROBLEM: While this redduction approach feels very sensible,
it is difficult to print the source Natural lines and output JAVA lines
in sequence with this approach. The output lists the input
complex Natural statement with all its lines and then the output
complex JAVA statement as bunch…