UNIX/Linux ve Windows Sistemlerinde Proseslerin Çalışma Dizinleri Kaan Aslan 12 Nisan 2008 UNIX/Linux ve Windows sistemlerinde her prosesin bir çalışma dizini (current working directory) vardır. Proseslerin çalışma dizinleri bu iki grup işletim sisteminde farklı biçimlerde belirlenmektedir. UNIX/Linux sistemlerinde bir prosesin çalışma dizini proses fork fonksiyonuyla yaratılırken üst prosesten alınır. exec işlemleri sırasında da yaratılmış olan prosesin çalışma dizini değişmez.yani bu sistemlerde bir proses bir alt proses yarattığında yaratılan alt prosesin çalışma dizini üst prosesin çalışma dizini ile aynı olacaktır. Windows sistemlerinde de alt prosesin çalışma dizini onu yaratan proses tarafından CreateProcess API fonksiyonu çağrılırken belirlenir. Her iki grup işletim sisteminde de prosesin çalışma dizini daha sonra belirli sistem fonksiyonlarıyla değiştirilebilmektedir. Yol ifadeleri (pathnames) bir dizin ya da dosyayı belirtmekte kullanılan karakter kümeleridir. Örneğin /a/b/c/d.dat bir yol ifadesidir. Dizin geçişlerinin UNIX türevi sistemlerde / karakterleriyle Windows sistemlerinde \ karakterleriyle ifade edildiğini biliyorsunuz. Yol ifadelerindeki / (Windows sistemlerinde \ ) karakterleriyle ayrılan her bir öğeye yol bileşeni (pathname component) deniyor. Örneğin "a/b/c/d.dat yol ifadesindeki "a", "b", "c" ve "d.dat" birer yol bileşenidir. Ayrıca UNIX/Linux sistemlerinde dizin ve dosyaların büyük harf - küçük harf duyarlılığına (case sensitivity) sahip olduğunu anımsatalım. Windows sistemleri dizin girişlerini büyük harf - küçük harf duyarlılığı ile oluşturuyor olsa da işleme sokarken büyük harflerle küçük harfleri aynı karakterlermiş gibi ele alıyor. Yol ifadeleri mutlak (absolute) ve göreli (relative) olmak üzere ikiye ayrılmaktadır. Prosesin çalışma dizini göreli yol ifadelerinin çözümlenmesinde kullanılan bir bilgidir. Eğer yol ifadesinin ilk karakteri / (Windows sistemlerinde \ ) ise söz konusu yol ifadesi kök dizinden başlayarak çözümlenir. Bu tür yol ifadelerine mutlak yol ifadeleri (absolute pathnames) denilmektedir. Mutlak yol ifadelerinin prosesin çalışma dizini ile bir ilgisi yoktur. Eğer yol ifadesinin ilk karakteri / (Windows sistemlerinde \ ) değilse böyle yol ifadelerine de göreli yol ifadeleri (relative pathnames) denir. Göreli yol ifadeleri prosesin çalışma dizininden itibaren yol belirtir. Örneğin, UNIX/Linux sistemlerinde unlink fonksiyonu ile bir dizin girişini silmek isteyelim: if (unlink("project/test") < 0) perror("unlink"); exit(exit_failure); Bu örnekte silinmek istenen dizin girişi project/test yol ifadesiyle belirtilmiştir. Yol ifadesinin ilk karakteri / olmadığı için bu yol ifadesi görelidir ve prosesin çalışma dizinine göre çözümlenecektir. Örneğin, prosesin çalışma dizininin o anda /home/kaan biçiminde olduğunu varsayalım. Bu durumda silinmek istenen giriş /home/kaan/project/test girişidir. Şimdi fonksiyonun şöyle çağrıldığını düşünelim: 1
if (unlink("/project/test") < 0) perror("unlink"); exit(exit_failure); Burada belirtilen yol ifadesi mutlaktır. Mutlak yol ifadeleri için prosesin çalışma dizinine başvurulmaz. Örneğimizde kökün altındaki /project dizininin altındaki "test" girişi silinmek istenmektedir. UNIX/Linux ve Window un tüm sistem fonksiyonlarındaki yol ifadeleri bu biçimde çözümlenmektedir. UNIX/Linux sistemleri için başka bir örnek daha verelim: int fd;... if ((fd = open("a.dat", O_RDONLY)) < 0) perror("open"); exit(exit_failure); open fonksiyonunda belirtilen a.dat yol ifadesi de görelidir. Dolayısıyla açılmak istenen dosya prosesin çalışma dizininde aranacaktır. POSIX standartlarına göre yol ifadelerindeki dizin geçişlerinde birden fazla / karakteri kullanılabilir. Örneğin, /a/b/c yol ifadesi ile /a//b////c yol ifadesi arasında bir fark yoktur. Fakat yol ifadesinin // biçiminde iki bölü karakteri ile başlatılması özel bir durum kabul edilmiş ve bu özel durumun yorumu işletim sistemini yazanlara bırakılmıştır (implementation dependent). Yol ifadesi ikiden fazla bölü karakteri ile başlarsa bu özel bir durum değildir ve bu durum yol ifadesinin tek bir bölü karakteri ile başlatılması ile aynıdır. Hiçbir karakter içermeyen (örneğin C'deki "" gibi) yol ifadeleri geçerli kabul edilmemektedir. Windows sistemlerinde de durum benzerdir. Dizin geçişlerinde birden fazla \ karakteri kullanılabilir. Örneğin C:\Windows\\\\\System yol ifadesi geçerlidir. Yol ifadelerinde katı ya da sembolik dizin bağlarından dolayı döngüsel bir durum oluşmamalıdır. Böylesi döngüsel durumlarda yol çözümlemesini yapan fonksiyonlar errno değişkenine ELOOP değerini yerleştirerek başarısızlıkla geri dönerler. Hem UNIX/Linux sistemlerinde hem de Windows sistemlerinde yol ifadelerindeki nokta bir önceki yol bileşeni ile belirtilen dizini temsil eder. Örneğin, UNIX/Linux sistemlerinde /home/./kaan/./test biçimindeki bir yol ifadesi geçerlidir. Bu yol ifadesindeki birinci nokta ondan bir önceki dizin olan /home dizinini temsil eder. Bu durumda yol ifadesindeki ikinci nokta da /home/kaan dizinini temsil edecektir. Yani bu yol ifadesi /home/kaan/test ile eşdeğerdir. Pekiyi, yol ifadesi olarak. ne anlama gelir? Bu göreli bir yol ifadesidir. Yol ifadelerindeki nokta önceki dizini belirttiğine göre ve önceki dizin de prosesin çalışma dizini olduğuna göre o halde. yol ifadesi de prosesin çalışma dizinini belirtecektir. Örneğin, prosesin çalışma dizini /home/kaan olsun. Bu durumda "." yol ifadesi de /home/kaan/. anlamına gelir. Nokta da bir önceki bileşeni temsil ettiğine göre belirtilmek istenen yol ifadesi prosesin çalışma dizini yani /home/kaan olacaktır. Ya da örneğin,./test ile test aynı yol ifadesini belirtmektedir. 2
UNIX/Linux ve Windows sistemlerinde yol ifadelerindeki.. önceki yol bileşeninden önceki yol bileşenini temsil eder. Örneğin, UNIX/Linux sistemlerinde /home/kaan/.. yol ifadesi /home anlamına gelmektedir. Ya da örneğin,../a.dat gibi bir yol ifadesi prosesin çalışma dizininin üst dizinindeki a.dat dosyasını belirtir. Özel bir durum olarak /.. gibi bir yol ifadesi de geçerlidir. Bu yol ifadesi -kök dizinin yukarısında bir dizin olmadığı gerekçesiyle- kök dizin anlamına gelir. Son olarak sonunda bir ya da birden fazla / karakteri bulunan yol ifadelerinin geçerli olduğunu belirtelim. (Örneğin /home/kaan/ gibi.) Böyle yol ifadeleri bir dizin belirtmelidir. Kabuk üzerinde çalışan kullanıcılar kabuğun imleci yüzünden sanki bir dizin üzerinde oturuyormuş hissine kapılabiliyorlar. Fakat aslında kabuk da sıradan bir prosestir ve kabuk üzerinde uygulanan cd komutu da aslında kabuk prosesinin çalışma dizinini değiştirmektedir. Makalenin giriş kısmında bir prosesin çalışma dizininin onu çalıştıran prosesten alındığını belirtmiştik. Eğer kabuk üzerinden dosyanın ismini yazarak bir programı çalıştırırsak aslında bu durumda o programı kabuk prosesi çalıştırmış olacaktır değil mi? Yani artık çalıştırılan proses kabuk prosesinin bir alt prosesidir. O halde çalıştırılan programın çalışma dizini de kabuk prosesinin o andaki çalışma dizini olacaktır. Örneğin kabuk üzerinde /home/kaan dizininde olalım. Burada test isimli bir program dosyası bulunuyor olsun. test isimli programı çalıştırdığımızda yaratılan prosesin çalışma dizini de /home/kaan olacaktır. Prosesin çalışma dizini alt prosese fork işlemi ile yaratım sırasında aktarıldığından yaratımdan sonra üst prosesin çalışma dizini değiştirildiğinde bunun alt prosese, alt prosesin çalışma dizini değiştirildiğinde de bunun üst prosese bir etkisi olmaz. UNIX/Linux sistemlerinde bir prosesin çalışma dizini chdir isimli POSIX fonksiyonuyla değiştirilebilir: #include <unistd.h> int chdir(const char *path); Fonksiyonun parametresi prosesin yeni çalışma dizinini belirten yol ifadesidir. Bu yol ifadesinin bir dizin belirtmesi gerekir. Fonksiyon başarı durumunda sıfır değerine, başarısızlık durumunda -1 değerine geri döner. Başarısızlık durumunda errno değişkenine yerleştirilebilecek bazı değerler şunlardır: EACCESS ENOENT ENOTDIR ELOOP : Yol bileşenlerine ilişkin dizinlerden en az birine x hakkı yoktur. : Yol ifadesine ilişkin dizin girişlerinden herhangi birisi yoktur ya da boş stringten oluşmaktadır. : Yol bileşenlerinden biri dizin olması gerekirken dizin değildir. : Sembolik bağlantılar yüzünden döngüsel bir durum oluşmuştur. : 3
Windows sistemlerinde dizin değiştirmek için SetCurrentDirectory isimli API fonksiyonu kullanılıyor. Fonksiyonun parametrik yapısını inceleyiniz: #include <windows.h> BOOL WINAPI SetCurrentDirectory(LPCTSTR lppathname); Fonksiyonun parametresi değiştirilecek dizinin yol ifadesini belirtiyor. Bu yol ifadesi mutlak ya da göreli olabilir. Fonksiyon başarılıysa sıfır dışı bir değere, başarısızsa sıfır değerine geri dönmektedir. Windows sistemlerinde yol ifadeleri sürücü (drive) de içerebilmektedir (full pathname). Bu sistemlerde prosesin çalışma dizini başka bir sürücünün bir dizini olacak biçimde değiştirilebilir. Örneğin: if (!SetCurrentDirectory("d:\\Study\\c")) fprintf(stderr, "Cannot change working directory!..\n"); exit(exit_failure); Windows sistemleri çevre değişkenleri yoluyla her bir sürücü için prosese özgü bir varsayılan dizin tutmaktadır. Prosesin çevre değişken listesinde =C:, =D:, =E:, biçiminde isimlendirilmiş çevre değişkenleri prosesin ilgili sürücülere ilişkin varsayılan dizinlerini belirtir. Örneğin: =C:=C:\study =D:=D:\test Burada C sürücüsü için varsayılan dizin C:\study, D sürücüsü için ise D:\test dizinidir. Eğer bir sürücü için bu biçimde bir çevre değişkeni tanımlanmamışsa, prosesin bu sürücü için varsayılan dizini, ilgili sürücünün kök dizini olur. Sürücüler için varsayılan dizinler SetCurrentDirectory fonksiyonu tarafından dikkate alınmaktadır. SetCurrentDirectory fonksiyonunun argümanı 4 biçimde girilebilir: 1. Yol ifadesi sürücü ismi içeren göreli bir yol ifadesidir ve yol ifadesindeki sürücü ismi prosesin çalışma dizininin ilişkin olduğu sürücüden farklıdır. Bu biçimdeki yol ifadesi ilgili çevre değişkeninde belirtilen dizin temel alınarak çözülür. Örneğin: DeleteFile("D:x.dat"); çağırmasıyla "D:\test\x.dat" dosyası silinmeye çalışılmaktadır. Çağırmanın şöyle yapıldığını düşünelim: SetCurrentDirectory("E:x.dat"); bu durumda "E:\x.dat" dosyası silinmeye çalışılır. (E sürücüsü için varsayılan dizin belirten bir çevre değişkeninin bulunmadığına dikkat ediniz. ) 4
2. Yol ifadesi sürücü ismi içeren mutlak bir yol ifadesidir. Bu durumda prosesin çevre değişkenlerine bakılmaz. Örneğin: DeleteFile("D:\x.dat"); burada "D:\x.dat" dosyası silinmek istenmektedir. 3. Yol ifadesi prosesin çalışma dizininin ilişkin olduğu sürücü ile aynı sürücüye ilişkin göreli bir yol ifadesidir. Bu durumda yol ifadesi prosesin çalışma dizininden itibaren çözülür. Örneğin: SetCurrentDirectory("C:\\windows"); DeleteFile("C:x.dat); bu çağırmalarla "C:\windows\x.dat" dosyası silinmek istenmektedir. 4. Yol ifadesinde sürücü ismi belirtilmemiştir. Bu durumda prosesin çalışma dizini hangi sürücüye ilişkinse yol ifadesi de o sürücüde mutlak ya da göreli bir yol belirtir. Örneğin: SetCurrentDirectory("C:\\windows"); DeleteFile("\\study"); bu çağırmalar sonucunda C:\x.dat" dosyası silinmek istenmektedir. Prosesin çalışma dizinini değiştirmek için Microsoft derleyicilerinde _chdir isimli bir kütüphane fonksiyonu da (POSIX sistemlerindeki chdir fonksiyonuna benzetilmiştir) bulunmaktadır. Şüphesiz bu fonksiyon arka planda arka planda SetCurrentDirectory API fonksiyonunu çağıracak biçimde yazılmıştır. Şimdi de proseslerin çalışma dizinlerinin nasıl elde edileceği üzerinde duralım. UNIX/Linux sistemlerinde proseslerin çalışma dizinleri getcwd isimli POSIX fonksiyonuyla elde edilebilir: #include <unistd.h> char *getcwd(char *buf, size_t size); Fonksiyonun birinci parametresi çalışma dizin inin yerleştirileceği char türden dizi nin adresini, ikinci parametresi ise birinci parametresiyle belirtilen dizi nin uzunluğunu belirtmektedir. Birinci parametreye NULL adres geçilmesi durumunda POSIX standartlarına göre fonksiyonun davranışı belirsizdir. [1] getcwd fonksiyonu başarı durumunda birinci parametreyle belirtilen adresin kendisiyle, başarısızlık durumunda NULL adres değeriyle geri döner. İkinci parametreyle belirtilen uzunluk eğer prosesin çalışma dizininin karakter uzunluğunun bir fazlasından (null karakterden dolayı) büyük ya da ona eşit değilse fonksiyon başarısız olmaktadır. Başarısızlığın önemli nedenleri şu errno değerleriyle temsil edilmektedir: 5
EINVAL : Fonksiyonun ikinci parametresi sıfırdır. ERANGE : Fonksiyonun ikinci parametresi sıfırdan büyüktüt fakat çalışma dizin ine ilişkin mutlak yol ifadesinin karakter uzunluğunun bir fazlasından küçüktür. EACCESS : Yol bileşenlerine ilişkin bir dizin e x hakkı ya da r hakkı yoktur. : getcwd fonksiyonu her zaman prosesin çalışma dizinini mutlak yol ifadesi olarak vermektedir. Bu mutlak yol ifadesi sembolik bağlantı içermez. Windows sistemlerinde prosesin çalışma dizini GetCurrentDirectory isimli API fonksiyonuyla elde edilmektedir: #include <windows.h> DWORD WINAPI GetCurrentDirectory( DWORD nbufferlength, LPTSTR lpbuffer ); Fonksiyonun ikinci parametresi çalışma dizin inin yerleştirileceği TCHAR türünden dizi nin adresini alır. Birinci parametre ise ikinci parametreyle belirtilen dizi nin null karakter dahil olmak üzere karakter uzunluğunu belirtmektedir. Birkaç olası durum söz konusudur: 1. Fonksiyon başarısız olursa sıfır değerine geri döner. Şüphesiz bu durum normal değildir. 2. Eğer argüman olarak verilen dizi, prosesin çalışma dizinine ilişkin yol ifadesinin null karakter dahil olmak üzere karakter uzunluğundan küçükse fonksiyon çalışma dizinini ikinci parametreyle belirtilen diziye yerleştirmez. Bu durumda fonksiyon null karakter dahil olmak üzere yol ifadesinin yerleştirileceği dizi nin uzunluğuyla geri döner. 3. Eğer argüman olarak verilen dizi, prosesin çalışma dizinine ilişkin yol ifadesinin null karakter dahil olmak üzere karakter uzunluğundan büyük ya da ona eşitse fonksiyon prosesin çalışma dizinini ikinci parametreyle belirtilen diziye yerleştirir ve yerleştirdiği karakter sayısı ile (null karakter dahil değil) geri döner. Programcı gereksinim duyulan dizi uzunluğunu elde etmek için birinci argümana sıfır ve ikinci argümana NULL değeri girebilir. Null karakterin dahil olup olmamasının kafa karşıklığına neden olabileceğini biliyoruz. O halde biraz daha açık örnek verelim. Prosesin çalışma dizini C:\study olsun (null karakter dahil 9 karakter). Fonksiyonu aşağıdaki gibi çağırmış olalım: 6
TCHAR buf[size]; DWORD dw; dw = GetCurrentDirectory(SIZE, buf); 1. Eğer SIZE değeri 9 dan küçükse fonksiyon yerleştirme yapmaz ve 9 değerine geri döner. 2. Eğer SIZE değeri 9 a eşit ya da 9 dan büyükse fonksiyon yerleştirme yapar ve 8 değerine geri döner. GetCurrentDirectory fonksiyonunun ASCII ve UNICODE versiyonlarının bulunduğuna dikkat ediniz. Fonksiyonun birinci parametresiyle ve geri dönüş değeriyle belirtilen uzunluk byte uzunluğu değil karakter uzunluğudur. Microsoft derleyicilerinde _getcwd isimli bir kütüphane fonksiyonu da vardır. Fonksiyonun parametrik yapısı POSIX karşılığına benzemektedir. Programlarınızda doğrudan bu fonksiyonu kullanabilirsiniz. Şüphesiz bu fonksiyon da arka planda GetCurrentDirectory fonksiyonunu çağırmaktadır. _getcwd fonksiyonu hakkında daha ayrıntılı açıklamaları MSDN yardım sisteminden elde edebilirsiniz. Aşağıda UNIX/Linux sistemleri için bir kabuk programını temsil eden örnek bir kod veriyoruz. Bu program, kabuk programlarının kendi çalışma dizinlerini imleç üzerinde nasıl görüntüledikleri hakkında bilgi verebilir: /* cmdline.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <dirent.h> #include <errno.h> /* Symbolic Constants */ #define PROMPT "CSD" #define MAX_CMD_LEN 256 #define MAX_CMD_PARAM 20 #define MAX_PATH 4096 /* Type Declarations */ typedef struct tagcmd char *pcmdtext; void (*proc) (void); CMD; 7
/* Function Prototypes */ void err_sys(const char *msg); void put_prompt(void); void parse_cmd_line(void); void change_dir(void); void list_dir(void); /* Global Data Definitions */ char g_cmdtext[max_cmd_len]; CMD g_cmd[] = "cd", change_dir, "dir", list_dir, NULL, NULL ; char *g_params[max_cmd_param]; int g_nparams; char g_cwd[max_path]; /* Function Definitions */ int main(void) char *pstr; int i; for (;;) put_prompt(); fgets(g_cmdtext, MAX_CMD_PARAM, stdin); if ((pstr = strchr(g_cmdtext, '\n'))!= NULL) *pstr = '\0'; parse_cmd_line(); if (!g_nparams) continue; if (!strcmp(g_params[0], "exit")) break; for (i = 0; g_cmd[i].pcmdtext!= NULL; ++i) if (!strcmp(g_params[0], g_cmd[i].pcmdtext)) g_cmd[i].proc(); break; if (g_cmd[i].pcmdtext == NULL) fprintf(stderr, "invalid command: %s\n", g_params[0]); return 0; void err_sys(const char *msg) perror(msg); exit(exit_failure); 8
void put_prompt(void) if (!getcwd(g_cwd, MAX_PATH)) err_sys("getcwd"); printf("%s:%s>", PROMPT, g_cwd); void parse_cmd_line(void) char *pstr; int i = 0; for (pstr = strtok(g_cmdtext, " \t"); pstr!= NULL; pstr = strtok(null, " \t")) g_params[i++] = pstr; g_nparams = i; void change_dir(void) if (g_nparams!= 2) fprintf(stderr, "wrong number of arguments!..\n"); return; if (chdir(g_params[1]) < 0) perror("chdir"); void list_dir(void) DIR *dir; struct dirent *ent; struct stat finfo; char pathfile[max_path], *pathdir; unsigned long tsize = 0; int nfiles = 0; if (g_nparams > 2) fprintf(stderr, "wrong number of arguments!..\n"); return; pathdir = g_nparams == 1? g_cwd : g_params[1]; if ((dir = opendir(pathdir)) == NULL) perror("opendir"); return; 9
printf("\n"); while (errno = 0, (ent = readdir(dir))!= NULL) sprintf(pathfile, "%s/%s", pathdir, ent->d_name); if (lstat(pathfile, &finfo) < 0) err_sys("stat"); printf("%-30s", ent->d_name); if (S_ISDIR(finfo.st_mode)) printf("<dir>\n"); else printf("%lu\n", finfo.st_size); ++nfiles; tsize += finfo.st_size; if (errno) err_sys("readdir"); printf("\t%d file(s)\t\t%lu\n\n", nfiles, tsize); closedir(dir); Derleme işelmini şöyle yapabilirsiniz: gcc o cmdline cmdline.c Örneğimizde bir kabuk programının iskeletinin nasıl olabileceğini görüyorsunuz. Örnek kabuğumuza yalnızca cd ve dir komutlarını yerleştirdik. Fakat isterseniz bu komutlara yenisini ekleyebilirsiniz. main fonksiyonunun oldukça basit olduğunu görüyorsunuz. Bütün komutlar ve bu komutlar girildiğinde çağrılacak fonksiyonlar CMD türünden g_cmd isimli bir yapı dizisine yerleştirilmiştir. Kullanıcının girdiği komutlar parse_cmd_line isimli fonksiyonla argümanlarına ayrıştırılmış ve komut ile argümanların adresleri bir gösterici dizisinde toplanmıştır. Böylece her komut için çağrılan fonksiyonda argüman kontrolü kolayca yapılabilmektedir. Aynı programın Windows uyarlaması da şöyle yazılabilir: /* cmdline.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <windows.h> #include <tchar.h> /* Symbolic Constants */ #define MAX_CMD_LEN 256 #define MAX_CMD_PARAM 20 #define EXIT 1 #define NOEXIT 0 10
/* Type Declarations */ typedef struct tagcmd TCHAR *pcmdtext; void (*proc) (void); CMD; /* Function Prototypes */ void PutErrMsg(LPCTSTR pmessage, BOOL bexit); void PutPrompt(void); void ParseCmdLine(void); void ChangeDir(void); void DispFileInfo(const WIN32_FIND_DATA *pfd); void ListDir(void); /* Global Data Definitions */ TCHAR g_cmdtext[max_cmd_len]; CMD g_cmd[] = _T("cd"), ChangeDir, _T("dir"), ListDir, NULL, NULL ; TCHAR *g_params[max_cmd_param]; int g_nparams; TCHAR g_cwd[max_path]; /* Function Definitions */ int _tmain(void) TCHAR *pstr; int i; for (;;) PutPrompt(); _fgetts(g_cmdtext, MAX_CMD_PARAM, stdin); if ((pstr = _tcschr(g_cmdtext, '\n'))!= NULL) *pstr = '\0'; ParseCmdLine(); if (!g_nparams) continue; if (!_tcscmp(g_params[0], _T("exit"))) break; for (i = 0; g_cmd[i].pcmdtext!= NULL; ++i) if (!_tcscmp(g_params[0], g_cmd[i].pcmdtext)) g_cmd[i].proc(); break; if (g_cmd[i].pcmdtext == NULL) _ftprintf(stderr, _T("invalid command: %s\n"), g_params[0]); return 0; 11
void PutErrMsg(LPCTSTR pmsg, BOOL bexit) LPTSTR psysmsg; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, GetLastError(), 0, (LPTSTR) &psysmsg, 0, NULL); if (pmsg) _ftprintf(stderr, _T("%s:%s\n"), pmsg, psysmsg); else _ftprintf(stderr, _T("%s\n"), psysmsg); LocalFree(pSysMsg); if (bexit) exit(exit_failure); void PutPrompt(void) if (!GetCurrentDirectory(MAX_PATH, g_cwd)) PutErrMsg(_T("GetCurrentDirectory"), EXIT); _tprintf(_t("%s>"), g_cwd); void ParseCmdLine(void) TCHAR *pstr; int i = 0; for (pstr = _tcstok(g_cmdtext, _T(" \t")); pstr!= NULL; pstr = _tcstok(null, _T(" \t"))) g_params[i++] = pstr; g_nparams = i; void ChangeDir(void) if (g_nparams!= 2) _ftprintf(stderr, _T("wrong number of arguments!..\n")); return; if (!SetCurrentDirectory(g_params[1])) PutErrMsg(NULL, NOEXIT); 12
void DispFileInfo(const WIN32_FIND_DATA *pfd) LARGE_INTEGER li; int64 fs; SYSTEMTIME st; li.u.lowpart = pfd->nfilesizelow; li.u.highpart = pfd->nfilesizehigh; fs = li.quadpart; FileTimeToSystemTime(&pfd->ftLastWriteTime, &st); _tprintf(_t("%02d/%02d/%04d %02d:%02d "), st.wday, st.wmonth, st.wyear, st.whour, st.wminute); if (pfd->dwfileattributes & FILE_ATTRIBUTE_DIRECTORY) _tprintf(_t("%-18s"), _T("<DIR>")); else _tprintf(_t("%18i64u"), fs); _tprintf(_t(" %s\n"), pfd->cfilename); void ListDir(void) TCHAR *ppathdir; HANDLE hfind; WIN32_FIND_DATA fd; LARGE_INTEGER li; int64 tsize = 0; int nfiles = 0; TCHAR listpath[max_path]; if (g_nparams > 2) _ftprintf(stderr, _T("wrong number of arguments!..\n")); return; _tprintf(_t("\n")), ppathdir = g_nparams == 1? g_cwd : g_params[1]; _stprintf(listpath, _T("%s\\%s"), ppathdir, _T("*.*")); if ((hfind = FindFirstFile(listPath, &fd)) == INVALID_HANDLE_VALUE) PutErrMsg(_T("FindFirstFile"), EXIT); do DispFileInfo(&fd); if (!(fd.dwfileattributes & FILE_ATTRIBUTE_DIRECTORY)) li.u.lowpart = fd.nfilesizelow; li.u.highpart = fd.nfilesizehigh; tsize += li.quadpart; ++nfiles; while (FindNextFile(hFind, &fd)); 13
if (GetLastError()!= ERROR_NO_MORE_FILES) PutErrMsg(_T("FindFirstFile"), NOEXIT); _tprintf(_t("\t%d file(s)\t\t%lu\n\n"), nfiles, tsize); FindClose(hFind); Derleme işlemini şöyle yapabilirsiniz: cl cmdline.c UNICODE derleme yaparken UNICODE ve _UNICODE makrolarını tanımlamayı unutmayınız: cl -D UNICODE -D _UNICODE cmdline.c CLI ortamında prosesin çalışma dizini doğrudan System.Environment sınıfının CurrentDirectory isimli read/write property elemanı ile elde edilebilir ve değiştirilebilir. Aynı işlem eşdeğer olarak System.IO isim alanındaki Directory sınıfının GetCurrentDirectory ve SetCurrentDirectory isimli statik metotlarıyla da yapılabilmektedir. Şüphesiz bu property ler ve metotlar.net ortamında GetCurrentDirectory ve SetCurrentDirectory API fonksiyonlarını, Mono ortamında da getcwd ve chdir POSIX fonksiyonlarını kullanırlar. Benzer biçimde Java ortamında da prosesin çalışma dizini java.lang paketindeki System sınıfının getproperty isimli statik fonksiyonuyla alınıp setproperty isimli statik metoduyla değiştirilebilmektedir. Şimdi de proseslerin çalışma dizinlerinin çekirdek tarafından nasıl ele alındığı üzerinde duralım. İşletim sistemlerinde proseslerin çalışma dizinleri doğrudan ya da dolaylı olarak proses kontrol bloğunda tutulmaktadır. Örneğin Linux 2.6 çekirdeğinde prosesin çalışma dizininin proses kontrol bloğunda tutulması aşağıdaki veri yapısıyla sağlanır: struct task_struct struct fs_struct *fs; ; struct fs_struct struct path root, pwd; ; struct path struct vfsmount *mnt; struct dentry *dentry; ; 14
Gördüğünüz gibi Linux sistemlerinde her prosesin kök dizini ve çalışma dizini proses kontrol bloğunda ayrı ayrı tutuluyor. vfsmount ilgili dizinin içinde bulunduğu dosya sisteminin mount bilgilerini içermektedir. dentry ise Linux dosya sistemlerinde dizin girişleri için bellekte oluşturulan veri yapısıdır. Bu sistemlerde prosesin kök dizini de değiştirilebilmektedir. Windows sistemlerinde de prosesin çalışma dizini proses kontrol bloğunda (process database) yine benzer biçimde tutulur. Windows sistemlerinin kaynak kodları açık olmadığı için burada kesin bir açıklama yapmamız mümkün değil. [2] Fakat açık kaynak kodlu bir Windows sistemi olarak ReactOS tan örnek verebiliriz. ReactOS sistemlerinde proses kontrol bloğu EPROCESS (Executive Process) yapısıyla temsil edilmektedir. EPROCESS yapısının içerisinde bir gösterici PEB isimli yapıyı gösterir. Bu yapıya prosesin çevre bloğu (environment block) denilmektedir. Çevre bloğunun içerisinde RTL_USER_PROCESS_PARAMETERS türünden bir yapının adresi tutulur. Bu yapıda da prosese ilişkin çeşitli parametrik bilgiler vardır: typedef struct _EPROCESS struct _PEB* Peb; EPROCESS; typedef struct _PEB RTL_USER_PROCESS_PARAMETERS *ProcessParameters; PEB; typedef struct _RTL_USER_PROCESS_PARAMETERS CURDIR CurrentDirectory; RTL_USER_PROCESS_PARAMETERS; 15
Prosesin kök dizini ve çalışma dizini yol ifadelerinin çözülmesinde kullanılmaktadır. Bir sistem fonksiyonu bir yol ifadesini aldığında öncelikle o yol ifadesi ile belirtilen dizin girişine ilişkin bilgilere erişmeye çalışır. Yol ifadesinden hareketle ilgili dizin girişinin bilgilerine erişilmesi sürecine yol ifadesinin çözümlenmesi (pathname resolution) denilmektedir. Yol ifadelerinin çözümlenmesi pek çok işletim sisteminde benzer tekniklerle yapılmaktadır. İşletim sistemi bir dizinden başlar. Dizinlerden dizinlere geçerek hedeflenen girişe ilişkin bilgileri bulmaya çalışır. Örneğin Linux sistemlerinde bir dosya ya da dizine ilişkin bütün bilgiler bellekte inode denilen bir yapı içerisinde tutulmaktadır. Yol ifadelelerinin çözümlenmesi süreci de aslında hedeflenen dizin girişine ilişkin inode yapısının elde edilmesi sürecidir. Şüphesiz işletim sistemi disk üzerindeki tüm dosyaların bilgilerini inode yapısı olarak belleğe çekemez. Bu işlem ilgili dizin girişine gereksinim duyulduğunda yapılmaktadır. Linux sistemlerinde dizin girişlerine ilişkin inode yapıları ismine inode cache denilen bir cache sisteminde biriktirilmektedir. [1] Bazı UNIX türevi sistemlerde getcwd fonksiyonunun birinci parametresi NULL geçildiğinde fonksiyon kendi içerisinde malloc ile tahsisat yapıp prosesin çalışma dizinini buraya yerleştirerek tahsis edilen adrese geri dönmektedir. Fakat bu davranış POSIX sistemlerinde garanti altına alınmamıştır. [2] Windows 95'in proses kontrol bloğu "Matt Pietrek'in Windows 95 Programming SECRETS" isimli kitabında kısmen açıklanmaktadır. Kaynaklar Aslan, K. (2001). Win32 Sistem Programlama Kurs Notları. Istanbul: C ve Sistem Programcıları Derneği. Aslan, K. (2002). UNIX/Linux Sistem Programlama Kurs Notları. Istanbul: C ve Sistem Programcıları Derneği. Bovet, D. and Cesati, M. (2005). Understanding the Linux Kernel. Oreilly & Associates Inc. ISO/IEC 9945-2:2003(E), Information techonolgy Portable Operating System Interface (POSIX) Part 2: System Interfaces. ISO/IEC 9945-2:2003(E), Information techonolgy Portable Operating System Interface (POSIX) Part 1: Base Definitions. 16
Pietrek, M. (1995). Windows 95 System Programming SECRETS. California: IDG Books Worldwide, Inc. MSDN Library Reference (http://msdn.microsoft.com/en-us/library/default.aspx). Richter, J. M. (1999). Programming Applications for Microsoft Windows with Cdrom. 4th. Edition. Redmond, Washinton: Microsoft Press. Stevens, R., Rago, S. A. (2005). Advanced Programming in the UNIX(R) Environment (2nd Edition). Addison-Wesley Professional. 17