> Sistemle İlgili Parametrik Değerlerin Elde Edilmesi, Kullanılması: UNIX türevi sistemlerde, sistemden sisteme değişebilecek, birden çok parametrik değer bulunmaktadır. Örneğin, yol ifadelerinin uzunluğu, oluşturulabilecek alt proseslerin toplam adedi, "exec" fonksiyonlarına geçilebilecek komut satırı argümanlarının sayısı, ek grupların ("supplementary groups") adedi ki bu konuya ileride değineceğiz, bir prosesin açık tutabileceği maksimum dosya sayısı gibi pek çok parametre sistemden sisteme değişmektedir. İşte taşınabilir bir program yapabilmek için iş bu parametrik değerlerin o sistemdeki gerçek değerlerinin biliniyor olması gerekmektedir. Genel olarak sistemdeki parametrik değerlerin tanımlandığı temel dosya "limits.h" isimli dosyadır. Bu dosyayı incelediğimizde değerlerin şu gruplara ayrıldığı görülür: "Minimum Values", "Runtime Invariant Values (Possibly Indeterminate)", "Pathname Variable Values", "Runtime Increasable Values", "Numerical Limits", ... Bu gruplardan, >> "Minimum Values" : Bir parametrik değerin, o sistemde olabilecek en küçük değerinin ne olacağı, bu gruptaki sembolik sabitlerle belirlenmiştir. Bu sembolik sabitlerin isimleri genellikle "_MAX" son ekine, "_POSIX_" ön ekine sahiptir. Her ne kadar isim içerisinde "MAX" kullanılmışsa da esasında o sistemde karşılık geldiği değerin, standartta belirtilen değerden fazla olması gerekmektedir. Örneğin, -> Bir prosesin açık tutabileceği MAKSİMUM dosya sayısı "_POSIX_OPEN_MAX" sembolik sabitiyle belirtilmiştir ve değeri "20" dir. İşte o sistemde "_POSIX_OPEN_MAX" sembolik sabitinin karşılığı olan sayı, MİNİMUM "20" olmalıdır. -> Bir kullanıcının oluşturabileceği MAKSİMUM alt proses sayısı ise "_POSIX_CHILD_MAX" sembolik sabitiyle belirtilmiştir ve değeri "25" tir. İşte o sistemde "_POSIX_CHILD_MAX" sembolik sabitinin karşılığı olan sayı, MİNİMUM "25" olmalıdır. -> "_POSIX_ARG_MAX" sembolik sabiti ise "exec" fonksiyonlarında kullanılabilecek komut satırı argümanı ve çevre değişkenlerinin MAKSİMUM "byte" uzunluğuna ilişkindir ve karşılığı olan sayı MİNİMUM "4096" olmalıdır. -> ... Örneklerde de görüldüğü üzere isminde "MAX" olması, sadece okunabilirliği arttırmaya yöneliktir. "Minimum Values" kategorisinde olması, hedef sistemde karşılık geldiği değerin POSIX standardında belirtilen taban değerden fazla olması gerektiği içindir. Yani bir işletim sistemi yazmaya kalkarsak "_POSIX_OPEN_MAX" sembolik sabitinin değerini EN AZ "20", "_POSIX_CHILD_MAX" için EN AZ "25" ve "_POSIX_ARG_MAX" için EN AZ "4096" olacak şekilde tanımlamalıyız. Diğer yandan bu gruptaki sembolik sabitlerin karşılığı olan sayılar esasında çok küçük sayılardır. Ancak modern sistemler dikkate alındığında, karşılık geldiği değerler daha büyüktür. Dolayısıyla bu sembolik sabitlerin önemi de DÜŞÜKTÜR (Yani pek bir işe yaramamaktadır). O halde programcının bu sembolik sabitlere değil, o sistemdeki değerine ihtiyacı vardır. >> "Runtime Invariant Values (Possibly Indeterminate)" : İşte bu kategori, "Minimum Values" kategorisindeki "_POSIX_" ön eki ve "_MAX" son ekine sahip değerlerin o sistemdeki gerçek değerlerine ilişkindir. Bu kategorideki sembolik sabitler "_POSIX_" ön ekine sahip DEĞİLDİR. Örneğin, -> "CHILD_MAX" sembolik sabiti, "_POSIX_CHILD_MAX" sembolik sabitinin o sistemdeki gerçek değeridir. -> "OPEN_MAX" sembolik sabiti, "_POSIX_OPEN_MAX" sembolik sabitinin o sistemdeki gerçek değeridir. -> ... Ancak bu gruptaki bazı sembolik sabitler, yukarıdakiler gibi değildir. Yani bazı parametrik değişkenlerin değerleri sistemin çalışması esnasında değiştirilebilir, bazı parametrelerin sınır değeri de olmayabilir veya sistemin o anki kaynaklarına bağlı olabilir. Dolayısıyla böylesi parametrelerin değerlerini baştan belirlemek mümkün olmayabilir. İşte böylesi parametreler için POSIX standartları, böylesi sembolik sabitler için, o sistemde bir sembolik sabitin "limits.h" içerisinde TANIMLANMIŞ OLMASINI GEREKTİRMEMEKTEDİR. Dolayısıyla bu gruptaki sembolik sabitler ancak tanımlı ise kullanabiliriz. Pekiyi bu durumda ne yapmalıyız? Burada programcı "#ifdef" ile ilgili sembolik sabitin o sistemde tanımlı olup olmadığını sorgulamalıdır. Eğer tanımlıysa kullanmalı, tanımlı değilse "sysconf", "pathconf" ve "fpathconf" fonksiyonlarına başvurmalıdır. Tabii "#ifdef" yapmadan da iş bu fonksiyonlar çağrılabilir. Burada "#ifdef" yapma amacımız gereksiz yere fonksiyon çağrılmamasını sağlamaktır. >> "Pathname Variable Values" : Bu kategoride belirtilen sembolik sabitlerin değerleri o anda kullanılan dosya sistemine bağlı olarak değişebilmektedir. Çünkü UNIX ve türevi sistemlerde farklı dosya sistemleri farklı noktalara "mount" edilebilmekte, dosya sistemlerinin özellikleri de birbirinden farklılık olabilmektedir. Dolayısıyla bu tür özellikler bu kategori altında toplanmıştır. Örneğin, -> "NAME_MAX" : Bir dizin girişinin isminin maksimum karakter sayıdır. "null" karakter dahil değildir. -> "PATH_MAX" : Mutlak bir yol ifadesinin alabileceği maksimum karakter sayısıdır. "null" karakter dahildir. -> ... Diğer yandan bu kategorideki sembolik sabitler, POSIX standartları gereği tüm dizin ağacı içerisinde sabitse, tanımlı olması ancak sabit değilse tanımlı olmaması gerekmektedir. Yani tanımlı olanları doğrudan kullanabiliriz. Aksi halde bu bilgileri "pathconf", "fpathconf" fonksiyonlarına başvurmalıyız. Dolayısıyla burada yine ilk önce "#ifdef" yapmalı, tanımlı değilse iş bu fonksiyonları çağırmalıyız. >> "Runtime Increasable Values" : Bu gruptaki sembolik sabitler sistemden sisteme değişebilir ve değerleri çalışma zamanında arttırılabilir. Ancak bu sembolik sabitlerin ilgili sistemdeki MİNİMUM değerleri tanımlı OLMALIDIR. Zaten bu MİNİMUM değerler "Minimal Values" kategorisi altında "_POSIX_" ile başlayan ve "_MAX" ile biten sembolik sabitlerdir. Gerçek değerleri programın çalışma zamanında "sysconf" fonksiyonu ile elde edilebilir. Örneğin, -> "NGROUPS_MAX" : Maksimum "supplementary group" adedi. -> ... >> "Numerical Limits" : Bu gruptaki sembolik sabitler "primitive" türlerin o sistemdeki MAKSİMUM ve MİNİMUM değerlerini belirtmektedir. -> "INT_MIN" : "int" türünün o sistemdeki en düşük değerini belirtir. -> "INT_MAX" : "int" türünün o sistemdeki en büyük değerini belirtir. Şimdi de yukarıda bahsedilen fonksiyonlara değinelim: >> "sysconf" : Yukarıda da belirtildiği üzere bu fonksiyon, ilgili sembolik sabitlerin çalışma zamanındaki değerlerini elde etmek için kullanılır. Fonksiyonun prototipi aşağıdaki gibidir. #include long sysconf(int name); Fonksiyon parametre olarak gerçek değerini öğrenmek istediğimiz sembolik sabiti alır. Ancak sembolik sabitleri kullanırken "_POSIX_" ön ekine sahip sabitler için, bu ön ek yerine, "_SC_" ön eki getirilir. Diğer sembolik sabitler için direkt "_SC_" ön eki getirilir. Örneğin, -> _POSIX_MESSAGE_PASSING => _SC_MESSAGE_PASSING -> LOGIN_NAME_MAX => _SC_LOGIN_NAME_MAX -> ... Fonksiyon başarı durumunda ilgili sembolik sabite, hata durumunda ise "-1" değerine geri döner ve "errno" değişkeni uygun değere çekilir. Ancak bu fonksiyon, eğer ilgili sembolik sabit için bir sınır belirlenmemişse(örneğin, sınırsız olması), yine başarısız olur ve "-1" ile geri döner fakat "errno" değişkenine dokunulmaz. Dolayısıyla "errno" değişkenini baştan sıfıra çekip, bu fonksiyon çağrısı sonrasında kontrol etmeliyiz. Eğer hala "0" ise ilgili sembolik sabit için bir sınır tayin edilmemiş demektir. * Örnek 1, Aşağıdaki örnekte "OPEN_MAX" sembolik sabitinin gerçek değeri elde edilmiştir. #include #include #include #include #include int main(void) { /* # OUTPUT # 175 */ long result; #ifdef OPEN_MAX result = OPEN_MAX; printf("It is defined: "); #else errno = 0; if ((result = sysconf(_SC_OPEN_MAX)) == -1) { if (errno == 0) fprintf(stderr, "Infinite Value...\n"); else perror("sysconf"); exit(EXIT_FAILURE); } #endif printf("%ld\n", result); return 0; } >> "pathconf" ve "fpathconf" : Fonksiyonların prototipleri aşağıdaki gibidir. #include long pathconf(const char *path, int name); long fpathconf(int fildes, int name); Fonksiyonların ikinci parametreleri gerçek değerini öğrenmek istediğimiz sembolik sabiti alır. "sysconf" fonksiyonu için "_SC_" ön ekini yazıyorken, bu fonksiyonlar için "_PC_" ön ekini yazıyoruz. Örneğin, -> _POSIX_NO_TRUNC => _PC_NO_TRUNC -> PIPE_BUF => _PC_PIPE_BUF -> ... Fonksiyonların birinci parametreleri ise sırasıyla yol ifadesi("pathconf") ve açık dosya betimleyicisi("fpathconf") biçimindedir. Fonksiyon başarı durumunda ilgili sembolik sabite, hata durumunda ise "-1" değerine geri döner ve "errno" değişkeni uygun değere çekilir. Ancak bu fonksiyon, eğer ilgili sembolik sabit için bir sınır belirlenmemişse(örneğin, sınırsız olması), yine başarısız olur ve "-1" ile geri döner fakat "errno" değişkenine dokunulmaz. Dolayısıyla "errno" değişkenini baştan sıfıra çekip, bu fonksiyon çağrısı sonrasında kontrol etmeliyiz. Eğer hala "0" ise ilgili sembolik sabit için bir sınır tayin edilmemiş demektir. * Örnek 1, Aşağıda kök dizinden itibaren, yani kök dizine göreli biçimde, yol ifadesinin alabileceği maksimum uzunluğu temin ettik. Dolayısıyla mutlak yol ifadesi için gereken karakter sayısı, burada elde edilenden bir fazla olmalıdır. Çünkü bu fonksiyonların verdikleri değer, bizim onlara geçtiğimiz dizine görelidir. Tabii Linux sistemlerinde bu sembolik sabit tanımlı olduğundan, bu fonksiyonların bu amaçla çağrılmasına gerek yoktur. #include #include #include #include #include int main(void) { /* # OUTPUT # 4096 */ long result; errno = 0; if ((result = pathconf("/", _PC_PATH_MAX)) == -1) { if (errno == 0) fprintf(stderr, "Infinite Value...\n"); else perror("sysconf"); exit(EXIT_FAILURE); } printf("%ld\n", result); return 0; } * Örnek 2, Aşağıda ise "#ifdef" çağrısı ile ilgili sembolik sabitin tanımlı olup olmadığı test edilmiştir. #include #include #include #include #include int main(void) { /* # OUTPUT # It is defined: 4096 */ long result; #ifdef PATH_MAX result = PATH_MAX; printf("It is defined: "); #else errno = 0; if ((result = pathconf("/", _PC_PATH_MAX)) == -1) { if (errno == 0) fprintf(stderr, "Infinite Value...\n"); else perror("sysconf"); exit(EXIT_FAILURE); } #endif printf("%ld\n", result); return 0; } İşte bu fonksiyonları da aşağıdaki gibi "#ifdef" kullanarak çağırabiliriz; * Örnek 1, #include #include #include #include #include #define MAX_PATH_TEMP 4096 #ifdef MAX_PATH static long g_max_path = MAX_PATH; #else static long g_max_path = 0; #endif int path_max(void) { if (!g_max_path) { long result; errno = 0; if ((result = pathconf("/", _PC_PATH_MAX)) == -1) { if (errno == 0) g_max_path = MAX_PATH_TEMP; else { perror("sysconf"); exit(EXIT_FAILURE); } } else g_max_path = result + 1; } return g_max_path; } int main(void) { /* # OUTPUT # Started Continues Ended */ puts("Started"); char* path; if ((path = (char*)malloc(path_max())) == NULL) { fprintf(stderr, "cannot allocate memory!..\n"); exit(EXIT_FAILURE); } puts("Continues"); free(path); puts("Ended"); return 0; } > Proses Limitlerinin Elde Edilmesi: İşletim sistemini bir kaynak yöneticisi olarak düşünebiliriz. Prosesler ise işletim sistemi tarafından sunulan çeşitli kaynakları kullanmaktadır. Yani işletim sistemi bir prosesin kullanabileceği kaynağı sınırlandırmaktadır. Aksi halde bir prosesin kullanacağı kaynak diğer proses(ler)in kaynaklara erişimini kısıtlayabilirdi. Bir prosesin kaynak limit bilgileri yine o prosesin kontrol bloğu içerisinde yer alır. UNIX türevi işletim sistemleri, her kaynak için bir "soft-limit" ve bir "hard-limit" değeri tutmaktadır. Kontroller sırasında "soft-limit" dikkate alınır. "soft-limit" değerine ulaşan proses başarısız olur. Diğer yandan herhangi bir proses "soft-limit" değerini yükseltebilir, ancak "hard-limit" değerine kadar. Hakeza "hard-limit" değerini de düşürebilirler ancak düşürdükten sonra tekrardan yükseltemezler, eski değerine bile. Yalnızca uygun önceliğe sahip prosesler iş bu "hard-limit" değerini yükseltebilir ve/veya düşürebilirler. Peki bir prosesin kaynakları nelerdir, bu kaynaklar nasıl "get" edilir ve "set" edilir? Kaynaklar "" başlık dosyası içerisinde, "RLIMIT" ön ekiyle birlite tanımlanmışlardır. Şöyleki; "RLIMIT_STACK", "RLIMIT_NOFILE", "RLIMIT_AS", "RLIMIT_CORE", "RLIMIT_CPU", "RLIMIT_DATA", "RLIMIT_FSIZE", ... Bu kaynaklardan, -> "RLIMIT_STACK" : Bir "thread" in "stack" uzunluğunu(büyüklüğünü) sınırlandırmak için kullanılır. -> "RLIMIT_NOFILE" : Prosesin dosya betimleyici tablosunun uzunluğunu belirtmektedir. Bu limitin "soft-limit" değerini değiştirdiğimizde, kendi prosesimizinkini büyütmüş olacağız. Artık "fork" sonucu hayata gelen proseslerinki de bu değişiklikten ETKİLENECEKTİR. -> "RLIMIT_AS" : Bir prosesin kullanabileceği maksimum sanal bellek miktarını belirler. -> "RLIMIT_CORE" : Bu limiti daha önce "core" dosyalarında da görmüştük, "core" dosyalarının maksimum uzunluğunu belirtir. -> "RLIMIT_CPU" : Prosesin harcayacağı CPU zamanını sınırlandırmak için bulundurulmuştur. Eğer burada belirtilen zaman aşılırsa, "SIGXCPU" sinyali gönderilir. Bu sinyalin varsayılan davranışı prosesin sonlandırılması yönündedir. -> "RLIMIT_DATA" : Prosesin "data segment" kısmını belirler. "malloc" gibi tahsilat fonksiyonları bu "data segment" kısmını büyüttüğü için, bu limit değerinden de etkilenmektedir. -> "RLIMIT_FSIZE" : Prosesin toplamda oluşturabileceği dosya uzunluğudur(büyüklüğüdür). Eğer limit 5 MB ise o proses toplamda ancak 5 MB büyüklüğünde dosya oluşturabilir. Bu kaynakları "get" etmek için "getrlimit", "set" etmek için "setrlimit" isimli POSIX fonksiyonları kullanılır. >> "getrlimit" ve "setrlimit" : Fonksiyonlardan "getrlimit" fonksiyonu "get", "setrlimit" ise "set" amacıyla kullanılır. Fonksiyonların prototipleri aşağıdaki gibidir. #include int getrlimit(int resource, struct rlimit *rlp); int setrlimit(int resource, const struct rlimit *rlp); Fonksiyonların birinci parametreleri kaynağın türünü, ikinci parametreleri ise kaynak bilgilerine ilişkin "rlimit" yapı türünden bir adrestir. Bu yapı aşağıdaki gibi tanımlanmıştır; struct rlimit { rlim_t rlim_cur; // The current (soft) limit. rlim_t rlim_max; // The hard limit. }; Bu yapının elemanlarının türü olan "rlim_t" işaretsiz bir tam sayı türüdür. Fonksiyonlar başarı durumunda "0", hata durumunda "-1" ile geri döner ve "errno" değişkenini uygun değere çekerler. Fonksiyonlardan "getrlimit" isimli olanın ikinci parametresinin "const" olmadığına, yani bu parametreye geçtiğimiz adrese bilgilerin yazılacağını; "setrlimit" isimli olanın ise "const" olduğuna, yani bu parametreye geçtiğimiz adresteki bilgilerle "set" işlemi yapacağına dikka ediniz. "getrlimit" fonksiyonu ile elde edilmek istenen limitler; -> Sınırsız olması halinde "rlimit" yapısının "rlim_cur" ve "rlim_max" elemanlarına "RLIM_INFINITY" özel değeri yerleştirilir. -> Sistemimizde tanımsız olabilir veya elde edilme imkanı olmayabilir. Bu durumda "rlimit" yapısının "rlim_cur" elemaına "RLIM_SAVED_CUR", "rlim_max" elemanına ise "RLIM_SAVED_MAX" değeri yerleştirilir. Aşağıda fonksiyonların kullanımına ilişkin örnekler verilmiştir. * Örnek 1, #include #include #include #include int main(void) { /* # OUTPUT # soft limit: [8388608] hard limit: infinite */ struct rlimit rl; if (getrlimit(RLIMIT_STACK, &rl) == -1) { perror("getrlimit"); exit(EXIT_FAILURE); } if (rl.rlim_cur == RLIM_INFINITY) printf("sotf limit: infinite\n"); else if (rl.rlim_cur == RLIM_SAVED_CUR) printf("sotf limit: unspecified\n"); else printf("soft limit: [%ju]\n", (uintmax_t)rl.rlim_cur); if (rl.rlim_max == RLIM_INFINITY) printf("hard limit: infinite\n"); else if (rl.rlim_max == RLIM_SAVED_MAX) printf("hard limit: unspecified\n"); else printf("hard limit: [%ju]\n", (uintmax_t)rl.rlim_max); return 0; } * Örnek 2, #include #include #include #include int main(void) { /* # OUTPUT # soft limit: [10000000] hard limit: infinite */ struct rlimit rl; rl.rlim_cur = 10000000; rl.rlim_max = RLIM_SAVED_MAX; if (setrlimit(RLIMIT_STACK, &rl) == -1) { perror("getrlimit"); exit(EXIT_FAILURE); } if (getrlimit(RLIMIT_STACK, &rl) == -1) { perror("getrlimit"); exit(EXIT_FAILURE); } if (rl.rlim_cur == RLIM_INFINITY) printf("sotf limit: infinite\n"); else if (rl.rlim_cur == RLIM_SAVED_CUR) printf("sotf limit: unspecified\n"); else printf("soft limit: [%ju]\n", (uintmax_t)rl.rlim_cur); if (rl.rlim_max == RLIM_INFINITY) printf("hard limit: infinite\n"); else if (rl.rlim_max == RLIM_SAVED_MAX) printf("hard limit: unspecified\n"); else printf("hard limit: [%ju]\n", (uintmax_t)rl.rlim_max); return 0; } * Örnek 3, #include #include #include #include int main(void) { /* # OUTPUT # soft limit: [175] hard limit: [175] */ struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) == -1) { perror("getrlimit"); exit(EXIT_FAILURE); } if (rl.rlim_cur == RLIM_INFINITY) printf("sotf limit: infinite\n"); else if (rl.rlim_cur == RLIM_SAVED_CUR) printf("sotf limit: unspecified\n"); else printf("soft limit: [%ju]\n", (uintmax_t)rl.rlim_cur); if (rl.rlim_max == RLIM_INFINITY) printf("hard limit: infinite\n"); else if (rl.rlim_max == RLIM_SAVED_MAX) printf("hard limit: unspecified\n"); else printf("hard limit: [%ju]\n", (uintmax_t)rl.rlim_max); return 0; } Son olarak bir prosesin kaynak kullanımını elde etmek için "getrusage" isimli POSIX fonksiyonu da bulundurulmuştur. >> "getrusage" : Fonksiyonun prototipi aşağıdaki gibidir. #include int getrusage(int who, struct rusage *r_usage); Fonksiyonun birinci parametresi kaynak bilgilerinin elde edileceği prosesi, ikinci parametresi ise bu kaynak bilgilerinin yerleştirileceği yapı nesnesinin adresini almaktadır. "rusage" yapısı aşağıdaki gibi tanımlanmıştır: struct rusage { struct timeval ru_utime; // Prosesin "user-mode" da çalışma zamanı. struct timeval ru_stime; // Prosesin "kernel-mode" da çalışma zamanı. }; Fonksiyon başarı durumunda "0", hata durumunda "-1" ile geri döner ve "errno" değişkenini uygun değere çeker. Diğer yandan bir özelliğin "hard-limit" değeri onun "soft-limit" değerinden de aşağısına çekilememektedir. Öte yandan burada şu noktaya dikkat etmeliyiz; prosesin limit bilgileri de üst prosesten alt prosese aktarılmaktadır. Örneğin, bir kullanıcının hayata getirebileceği maksimum proses adedi "RLIMIT_NPROC" sembolik sabitiyle ifade edilir. Bu değeri değiştirdiğimiz zaman artık prosesimiz ve bu prosesten türetilen diğer proseslerimiz bu değişiklikten etkilenecektir. Fakat bizim başka proseslerimiz bundan etkilenmeyecektir çünkü değişiklik onların kontrol bloğunda gerçekleşmemiştir. Yani sistem geneli değil, o proses ve alt proseslerinde değişiklik geçerlidir. Yani "shell" programının kaynak limitleri arttırılırsa, o "shell" programı ile çalıştırılan bütün programlara bu sirayet edecektir. Eğer ikinci bir "shell" programını çalıştırırsak ve bu ikinci "shell" ile başka program çalıştırırsak, varsayılan limit değerleri kullanılacaktır. Eğer "shell" programını "root" olarak çalıştırırsak, artık "hard-limit" değerini de arttırabiliriz.