İşletim Sistemlerinde Proseslerin Çevre Değişkenleri



Benzer belgeler
UNIX/Linux ve Windows Sistemlerinde stdin, stdout ve stderr Dosyaları

UNIX/Linux Sistemlerinde exec İşlemleri

Temel Bilgisayar Programlama Final Sınavı Çalışma Notları

Pointer Kavramı. Veri Yapıları

ELN1001 BİLGİSAYAR PROGRAMLAMA I

Bilgi ve İletişim Teknolojileri (JFM 102) Ders 7. LINUX OS (Sistem Yapısı) BİLGİ & İLETİŞİM TEKNOLOJİLERİ. LINUX Yapısı

NESNEYE YÖNELİK PROGRAMLAMA

Süreç 1 Kavramı ve Oluşturma Yöntemleri

C Konsol ve Komut Satırı

Yrd. Doç. Dr. Caner ÖZCAN

UNIX/Linux ve Windows Sistemlerinde Dosyaların ve Dizinlerin Silinmesi

Hafta 12 Karakter Tutan Diziler

Görev. Hafta-06 Processes. Proses. Program Proses. Process Yönetimi. Örnek: Yemek yapmayı seven bir bilgisayarcı bir tarife göre kek yapıyor.

API(Application Programming Interface) Fonksiyonları:

Bil101 Bilgisayar Yazılımı I. M. Erdem ÇORAPÇIOĞLU Bilgisayar Yüksek Mühendisi

C++ Giriş Ders 1 MSGSU Fizik Bölümü Ferhat ÖZOK Kullanılacak kaynak: Published by Juan Soulié

HSancak Nesne Tabanlı Programlama I Ders Notları

Özyineleme (Recursion)

Göstericiler (Pointers)

ELN1002 BİLGİSAYAR PROGRAMLAMA 2

man komut man ls (ls komutu hakkında bilgi verir.) man pwd (pwd komutu hakkında bilgi verir.)

HEAP SİSTEMİ. Oğuz Karan

Sınav tarihi : Süre : 60 dak.

BLM-112 PROGRAMLAMA DİLLERİ II. Ders-3 İşaretçiler (Pointer) (Kısım-2)

Uzaktan Eğitim Uygulama ve Araştırma Merkezi

BİL-142 Bilgisayar Programlama II

Yrd. Doç. Dr. Caner ÖZCAN

BLM 112- Programlama Dilleri II. Hafta 5 İşaretçiler (Pointers)

INPUTBOX KULLANIMI. Komut Düğmesine uygulanan algoritma örneği

NESNEYE YÖNELİK PROGRAMLAMA C++ a Giriş

Uzaktan Eğitim Uygulama ve Araştırma Merkezi

Proses. Prosesler 2. İşletim Sistemleri

Değişkenler. Geçerli değişken isimleri : baslamazamani, ad_soyad, x5 Geçersiz değişken isimleri : 3x, while

HSancak Nesne Tabanlı Programlama I Ders Notları

PROSESLER. Proses. Proses

BLM 112- Programlama Dilleri II. Hafta 2 C Programlarının Bellek Düzeni ve Rekürsif (Özyinelemeli) Fonksiyonlar

Pointers (İşaretçiler)

Linux ta komutlar hakkında yardım almak için aşağıdaki komutlar kullanılır : - man - info - whatis - apropos

8. İŞARETCİLER (POINTERS)

C#(Sharp) Programlama Dili

Örnek: İki fonksiyondan oluşan bir program. Fonksiyon Tanımı

AHMET YESEVİ ÜNİVERSİTESİ BİLİŞİM SİSTEMLERİ VE MÜHENDİSLİK FAKÜLTESİ BİLGİSAYAR MÜHENDİSLİĞİ LİSANS DÖNEM ÖDEVİ

C# Programlama Dili. İlk programımız Tür dönüşümü Yorum ekleme Operatörler

C Programlama Dilinde Değişkenler

Bilgisayar Teknolojileri Bölümü Bilgisayar Programcılığı Programı. Öğr. Gör. Cansu AYVAZ GÜVEN

BM102 BİLGİSAYAR PROGRAMLAMA II LABORATUVAR UYGULAMALARI. 3Hafta

Sınav tarihi : Süre : 60 dak. a) strstr b) strchr c) strcat d) strcpy e) strlen. a) b) d) e) 0

GENEL GĐRĐŞ-ÇIKIŞ FONKSĐYONLARI. ENF102 Jeoloji 1. #include <stdio.h> printf Fonksiyonu ÖRNEK. printf

Bölüm 10: PHP ile Veritabanı Uygulamaları

YAPILAR BİRLİKLER SAYMA SABİTLERİ/KÜMELERİ. 3. Hafta

İşletim Sistemleri. Dr. Binnur Kurt Omega Eğitim ve Danışmanlık İşletim Sistemleri

Dr. Fatih AY Tel: fatihay@fatihay.net

Fonksiyonlar (Altprogram)

WebInstaller. 1. Kurulum Đçin Gereksinimler

BM-209 Nesne Yönelimli Programlama. Yrd. Doç. Dr. İbrahim Alper Doğru Gazi Üniversitesi Teknoloji Fakültesi Bilgisayar Mühendisliği Bölümü

Linux'ta Kabuk ve Kabuk Programlama

BTEP243 Ders 3. class Yazım Kuralı:

10. DOSYA GİRİŞ ÇIKIŞ FONKSİYONLARI

Eln 1002 Bilgisayar Programlama II

Ders 2: Veri Tipleri, Değişkenler ve Sabitler

Java 2 Standart Edition SDK Kurulum ve Java ya Giriş

İŞLETİM SİSTEMİ KATMANLARI (Çekirdek, Kabuk ve diğer temel kavramlar) Öğr.Gör. Dr. Dr. Şirin KARADENİZ

Uzaktan Eğitim Uygulama ve Araştırma Merkezi

Windows'da çalışırken pek çok durumda bir işe başlamadan önce işletim sisteminin o işe ilişkin bilgileri depolayacağı bir alan yaratması gerekir.

Adım Adım C-II. Eksik kalmış konular

Karabük Üniversitesi, Mühendislik Fakültesi... WEB TEKNOLOJİLERİ

Öğr. Gör. Serkan AKSU 1

Multicore/Multithread Programlama

/ C Bilgisayar Programlama Final Sınavı Test Soruları. Adı soyadı :... Öğrenci no :... İmza :... Tarih, Süre : , 60 dak.

Örnek 4: Örnek Özyinelemeli fonksiyon örneği Bölüm 9. C++ programlama dilinde Nesne ve sınıf

Widows un çalışmasında birinci sırada önem taşıyan dosyalardan biriside Registry olarak bilinen kayıt veri tabanıdır.

BLM 112- Programlama Dilleri II. Hafta 4 İşaretçiler (Pointers)

Yrd. Doç. Dr. Caner ÖZCAN

Görsel Programlama DERS 03. Görsel Programlama - Ders03/ 1

1 PROGRAMLAMAYA GİRİŞ

Data Structures Lab Güz

Yrd. Doç. Dr. Caner ÖZCAN

PROGRAMLAMAYA GİRİŞ DERS 2

Mühendislik Fakültesi Elektrik-Elektronik Mühendisliği C Programlama 3. Bölüm Veri Tipleri ve Değişkenler

BİLG Dr. Mustafa T. Babagil 1

BĠLGĠSAYAR PROGRAMLAMA II C++ Programlamaya GiriĢ Published by Juan Soulié

İŞLETİM SİSTEMİ KATMANLARI (Çekirdek, kabuk ve diğer temel kavramlar) Bir işletim sisteminin yazılım tasarımında ele alınması gereken iki önemli konu

Sistem Programlama. Kesmeler(Interrupts): Kesme mikro işlemcinin üzerinde çalıştığı koda ara vererek başka bir kodu çalıştırması işlemidir.

C PROGRAMLAMA D İ L İ

sayi=3 harf=a reelsayi=8.72 Bellek durumu 5. İşaretç iler (pointers)

BLM-111 PROGRAMLAMA DİLLERİ I. Ders-11 Karakter Diziler. Yrd. Doç. Dr. Ümit ATİLA

Komut Penceresi ile Çalışmaya Başlamak

Regular Expressions Version 0.1

Fatura Dinamik Kodlama İyileştirmeleri

// hataları işaret eden referans

Programlama Dilleri III 1

Fonksiyonlar. C++ ve NESNEYE DAYALI PROGRAMLAMA 51. /* Fonksiyon: kup Bir tamsayının küpünü hesaplar */ long int kup(int x) {

Programlama Dilleri 1. Ders 12: Belirleyiciler ve Niteleyiciler

ALGORİTMA VE PROGRAMLAMA II

Programlama Dillerinde Kullanılan Veri Tipleri

Linux Dosya Yapısı. Eren BAŞTÜRK.

YZM 2105 Nesneye Yönelik Programlama

Temel Bilgisayar Bilimleri Ders Notu #4-2. kısım

1. Aşağıdaki program parçacığını çalıştırdığınızda result ve param değişkenlerinin aldığı en son değerleri ve programın çıktısını yazınız.

ALGORİTMA VE PROGRAMLAMA II

Transkript:

İşletim Sistemlerinde Proseslerin Çevre Değişkenleri Kaan Aslan 21 Temmuz 2008 Modern işletim sistemlerinde her prosesin bir çevre değişken bloğu vardır. Prosesin çevre değişken bloğu çevre değişkenlerinden ve onların değerlerinden oluşmaktadır. Örneğin, MESAJ bir çevre değişkeninin ismi olabilir, Merhaba Dunya ise onun değeri olabilir. Çevre değişkenleri pek çok işletim sisteminde proses yaratılırken belirlenebilmekte ya da üst prosesten (parent process) aktarılabilmektedir. Çevre değişkenlerinin üst prosesten aktarılması en çok karşılaşılan tipik durumdur. Windows sistemlerinde komut satırı üzerinden çevre değişkenleri set komutuyla oluşturulabilir. Örneğin: Burada MESAJ çevre değişkeninin ismi Merhaba Dunya ise onun değeridir. Windows sistemlerinde komut satırında, X bir çevre değişkeni olmak üzere, X'in değeri %X% ifadesiyle elde edilir. (Ayrıca, Windows sistemlerinde çevre değişkenlerinin büyük harf küçük harf duyarlılığının olmadığını da belirtelim.) Örneğin: Yaratılmış olan bir çevre değişkenine yeni bir değer atayabiliriz: UNIX/Linux sistemlerinde ise çevre değişkenleri kabuk üzerinden iki aşamada oluşturulur. Örneğin X çevre değişkenine Y değerini atamak isteyelim. Birinci aşamada X= Y komutu ile X kabuk değişkeni yaratılır; ikinci aşamada export X komutu ile kabuk değişkeni çevre değişkeni haline dönüştürülür. Örneğin: Çevre değişkenine ilişkin değerin iki tırnak içinde girildiğine dikkat ediniz. Bu işlemler tek aşamada aşağıdaki gibi de yapılabilirdi: UNIX/Linux sistemlerindeki kabuk programlarında X bir çevre değişkeni olmak üzere, X'in değeri $X ifadesiyle elde edilmektedir. Örneğin: 1

Her iki sistemde de komut satırı üzerinde oluşturulan çevre değişkenleri, komut satırına ilişkin prosesin çevre değişken bloğuna eklenmektedir. Örneğin Windows sistemlerindeki komut satırı cmd.exe isimli programdır. UNIX/Linux sistemlerinde ise komut satırı, kullanıcının tercih etmiş olduğu kabuk programdır (tipik olarak bash). Kabuk programa çevre değişkenleri eklendiğinde kabuk üzerinden çalıştırılan programlara da bu değişkenler aktarılacaktır. Çünkü kullanıcı kabuk üzerinden bir programı çalıştırdığında, aslında o program dolaylı olarak kabuk prosesi tarafından çalıştırılmaktadır. Windows sistemlerinde çevre değişkenleri ayrıca "Control Panel/System/Advanced System Settings/Environment Variables" diyalog penceresinden de oluşturulabilir: Bu diyalog penceresi aynı zamanda çevre değişkenlerinin değerlerinin değiştirilmesine de olanak sağlamaktadır. Çevre Değişkenlerinin Oluşturulması ve Alt Proseslere Aktarılması Giriş bölümünde de kısaca belirttiğimiz gibi, UNIX/Linux ve Windows sistemlerinde çevre değişkenleri proses yaratılırken belirlenebilmekte ya da üst prosesten aktarılabilmektedir. Fakat çevre değişkenlerinin üst prosesten aktarılması tercih edilen tipik durumdur. Bilindiği gibi bu sistemlerde her proses başka bir proses tarafından yaratılır. Kabuk programlarının da bir proses olduğunu ve kabuk üzerinden bir programı çalıştırdığımızda yeni çalıştırılan programa ilişkin prosesin kabuk prosesi tarafından yaratıldığına dikkat ediniz. Böylece biz kabuk üzerinde çeşitli çevre 2

değişkenlerini oluşturduğumuzda, bu değişkenler kabuktan çalıştırğımız programlara da aktarılmış olacaktır. Bu konunun ayrıntılarına daha ileride değineceğiz. Bilindiği gibi UNIX/Linux sistemlerinde prosesler fork fonksiyonuyla yaratılmaktadır. [1] fork fonksiyonuyla proses yaratıldığında üst prosese ilişkin pek çok özellikle birlikte çevre değişkenleri de alt prosese aktarılırlar. Yani fork işleminden sonra alt proses ile üst proses aynı çevre değişken bloğunun farklı kopyalarını kullanıyor durumda olur. UNIX/Linux sistemlerinde proses üzerinde bir programı çalıştırabilmek için 6 farklı exec fonksiyonu bulunmaktadır: #include<unistd.h> int execl(constchar *path, const char *arg0,... /*, (char*)0 */); int execv(constchar *path, char *const argv[]); int execle(const char *path, const char *arg0,... /*,(char*) 0, char *const envp[] */); int execve(const char*path, char *const argv[], char*const envp[]); int execlp(const char *file, const char *arg0,... /*,(char *) 0 */); int execvp(const char *file, char *const argv[]); exec fonksiyonlarının e siz biçimleri (execl, execv, execlp, execvp) ile prosesin çevre değişkenlerini değiştirmenin bir yolu yoktur. Fakat e li biçimleri olan execle ve execve ile bir programı çalıştırırken mevcut prosesin çevre değişkenlerini de tamamen değiştirebiliriz. Dikkat ediniz, execve fonksiyonunun birinci parametresi çalıştırılabilen (executable) dosyanın yol ifadesini, ikinci parametresi komut satırı argümanlarını ve üçüncü parametresi de çevre değişken listesini belirtmektedir. Bu fonksiyon kullanılmadan önce programın komut satırı argümanları ve çevre değişkenleri char türden bir gösterici dizisine yerleştirilir. Çevre değişkenleri için gösterici dizisinin her elemanı, X çevre değişkeni ve Y de bunun değeri olmak üzere X=Y biçiminde olmalıdır. Ayrıca hem komut satırı argümanlarına ilişkin hem de çevre değişkenlerine ilişkin gösterici dizilerinin NULL adresle sonlandırılması gerekmektedir. Örneğin: 3

/* sample.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> void err_sys(const char *msg); int main(void) { char *env[] = {"name=kaan Aslan", "no=100", NULL; char *args[] = {"dispenv", NULL; pid_t pid; if ((pid = fork()) < 0) err_sys("fork"); if (pid == 0) if (execve("dispenv", args, env) < 0) err_sys("execve"); if (waitpid(pid, NULL, 0) < 0) err_sys("waitpid"); return 0; void err_sys(const char *msg) { perror(msg); exit(exit_failure); Örneğimizdeki dispenv programı prosesin tüm çevre değişkenlerini listelemektedir: /* dispenv.c */ #include <stdio.h> extern char **environ; int main(void) { int i; for (i = 0; environ[i]!= NULL; ++i) puts(environ[i]); return 0; Windows sistemlerinde de -tıpkı UNIX/Linux sistemlerinde olduğu gibi- çevre değişkenleri üst prosesten alt prosese aktarılabilir ya da alt proses için yeni bir çevre değişken kümesi belirlenebilir. Fakat yukarıda da belirttiğimiz gibi, tipik durum çevre 4

değişkenlerinin üst prosesten aktarılmasıdır. Bilindiği gibi Windows sistemlerinde proseslerin yaratılması ve programların çalıştırılması iki ayrı fonksiyonla değil, CreateProcess isimli API fonksiyonu ile tek hamlede yapılmaktadır. Öncelikle CreateProcess fonksiyonunun parametrik yapısını anımsatmak istiyoruz: #include <windows.h> BOOL WINAPI CreateProcess( LPCTSTR lpapplicationname, LPTSTR lpcommandline, LPSECURITY_ATTRIBUTES lpprocessattributes, LPSECURITY_ATTRIBUTES lpthreadattributes, BOOL binherithandles, DWORD dwcreationflags, LPVOID lpenvironment, LPCTSTR lpcurrentdirectory, LPSTARTUPINFO lpstartupinfo, LPPROCESS_INFORMATION lpprocessinformation ); Fonksiyonun lpenvironment isimli LPVOID türünden 7 nci parametresine dikkat ediniz. Eğer bu parametreye karşı gelen argüman NULL adres olarak girilirse alt prosesin çevre değişkenleri üst prosesten aktarılır. Eğer bu parametreye karşı gelen argüman A=B\0C=D\0E=F\0\0 biçiminde çevre değişken listelerinden oluşan bir yazının adresi olarak girilirse bu durumda yaratılan prosese burada belirtilen çevre değişkenleri aktarılır. Çevre değişken bloğunun her bir elemanının tek bir null karakterle, tümünün ise iki null karakterle sonlandırıldığına dikkat ediniz. Aşağıda Windows sistemleri için bir örnek görüyorsunuz: /* sample.c */ #include <stdio.h> #include <stdlib.h> #include <tchar.h> #include <windows.h> int _tmain(void) { STARTUPINFO si = {sizeof(startupinfo); PROCESS_INFORMATION pi; char *penv = "Name=Kaan Aslan\0No=100\0"; TCHAR szchildpath[] = _T("dispenv.exe"); if (!CreateProcess(NULL, szchildpath, NULL, NULL, FALSE, 0, penv, NULL, &si, &pi)) { _ftprintf(stderr, _T("CreateProcess error: %lu\n"), GetLastError()); exit(exit_failure); WaitForSingleObject(pi.hProcess, INFINITE); return 0; 5

/* dispenv.c */ #include <stdio.h> #include <stdlib.h> #include <tchar.h> #include <windows.h> int _tmain(void) { LPTCH penv; if ((penv = GetEnvironmentStrings()) == NULL) { _ftprintf(stderr, _T("GetEnvironmentStrings error: %lu\n"), GetLastError()); exit(exit_failure); while (*penv!= '\0') { _putts(penv); penv += _tcslen(penv) + 1; FreeEnvironmentStrings(pEnv); return 0; UNIX/Linux sistemlerinde sistemin başlatılmasından kabuk prosesine kadar çeşitli proseslerin yaratıldığını biliyorsunuz. Pek çok UNIX türevi sistemde, sistemin başlatılmasından kabuk prosesine kadar olan proses yaratılma öyküsü şöyledir: Boot işleminden gelen kod işletim sisteminin çekirdeğini yükledikten sonra kendini bir proses haline dönüştürür. Bu prosese geleneksel olarak swapper ya da pager denilmektedir. init prosesi bu proses tarafından yaratılmaktadır. init prosesi de daha 6

sonra fork ve exec fonksiyonlarını uygulayarak her terminal için getty isimli (mingetty ya da benzer bir isimde de olabilir) bir programı çalıştırır. init prosesinin etkin kullanıcı id si 0 olduğu için (root hakkı) getty proseslerinin de etkin kullanıcı id leri 0 olacaktır. Daha sonra getty prosesleri exec işlemleri ile login programını çalıştırırlar. login, parola doğrulamasını yaparak bizi sisteme sokan programdır. Nihayet login de yine exec işlemi uygulayarak kabuk programını (tipik olarak bash) çalıştırır. İşte çevre değişkenlerinin büyük bölümü login ve kabuk programları tarafından oluşturulmaktadır. Tabi bu noktada şunu vurgulamak istiyoruz: UNIX türevi bir sistemin açılış süreci sistemden sisteme, hatta aynı sistemde versiyondan versiyona ya da dağıtımdan dağıtıma değişebilmektedir. Örneğin tipik olarak getty, login ve bash gibi prosesler çeşitli betik (script) dosyalarını çalıştırırlar. Hangi betik dosyalarının hangi sırada çalıştırıldıkları o sisteme özgü bir biçimde öğrenilmelidir. login prosesinin oluşturduğu tipik çevre değişkenleri HOME, PATH, SHELL, TERM, MAIL, LOGNAME isimli değişkenlerdir. Kabuk programları da (örneğin bash) yine tipik olarak LANG, PWD gibi çevre değişkenlerini oluşturmaktadır. Biz programımızı kabuk üzerinden çalıştırdığımızda tüm bu çevre değişkenleri kabuk prosesinden prosesimize aktarılacaktır. Windows sistemlerinde de durum benzerdir. Bu sistemlerdeki grafik arayüz explorer isimli bir proses tarafından oluşturulur. explorer prosesi her masaüstü (desktop) için ayrı olacak biçimde (tıpkı UNIX türevi sistemlerdeki getty prosesinde olduğu gibi) birden fazla kez yaratılmaktadır. Önceki bölümde de söz ettiğimiz Control Panel/System/Advanced System Settings/Environment Variables diyalog penceresini anımsayınız. Burada User Variables ve System Variables isimli iki bölüm vardır. User Variables yalnızca o anda çalışılan masaüstü prosesinin çevre değişkenlerini, System Variables ise tüm masaüstü proseslerinin çevre değişkenlerini değiştirmek için kullanılmaktadır. Pek çok IDE de programın çalıştırılması sırasında, çalıştırılan programa hangi çevre değişkenlerinin aktarılacağı belirlenebilmektedir. IDE bu belirlemelere göre projeye ilişkin programı çalıştırırken söz konusu çevre değişkenlerini de oluşturur. Örneğin Visual Studio IDE lerinde bu belirleme işlemi Project/Properties/Debugging/Environment menüsüyle yapılmaktadır: 7

UNIX/Linux ve Windows Sistemlerinde Çevre Değişkenlerine Erişimde Kullanılan Fonksiyonlar Prosesin çevre değişkenlerine ilişkin değerlerin alınması ve onlara değer atanması için UNIX/Linux sistemlerinde getenv, setenv ve putenv isimli POSIX fonksiyonları kullanılmaktadır. getenv aynı zamanda standart bir C fonksiyonudur. Dolayısıyla tüm sistemlerde bulunmak zorundadır. Önce getenv fonksiyonunu açıklayalım: #include <stdlib.h> char *getenv(const char *name); Fonksiyon parametre olarak çevre değişkeninin ismini alır. Eğer verilen isimde bir çevre değişkeni varsa onun değerine, yoksa NULL adres değerine geri döner. Örneğin: #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { char *str; if (argc!= 2) { fprintf(stderr, "Wrong number of arguments!..\n"); exit(exit_failure); 8

if ((str = getenv(argv[1])) == NULL) { fprintf(stderr, "Cannot get environment variable:%s\n", argv[1]); exit(exit_failure); puts(str); return 0; Program komut satırı argümanıyla alınan çevre değişkeninin değerini ekrana yazdırıyor. Windows sistemlerinde çevre değişkenlerinin değerlerini elde etmek için GetEnvironmentVariable isimli API fonksiyonu da kullanılmaktadır: #include <windows.h> DWORD WINAPI GetEnvironmentVariable( LPCTSTR lpname, LPTSTR lpbuffer, DWORD nsize ); Fonksiyonun birinci parametresi, değeri elde edilecek çevre değişkeninin ismini, ikinci parametresi çevre değişkeninin değerinin yerleştirileceği dizinin adresini, üçüncü parametresi de bu dizinin uzunluğunu (null karakter dahil olmak üzere) belirtir. Fonksiyon başarı durumunda diziye yerleştirilen karakter sayısı ile (dikkat ediniz, byte sayısı ile değil), başarısızlık durumunda sıfır değeri ile geri dönmektedir. Geri dönüş değeri ile belirtilen uzunluğa null karakter dahil değildir. Eğer çevre değişkeninin -null karakter dahil olmak üzere- yazısal karşılığının karakter uzunluğu, fonksiyonun üçüncü parametresiyle belirtilen uzunluktan fazlaysa, bu durumda fonksiyon ikinci parametresiyle belirtilen diziye yerleştirme yapmaz; null karakter dahil olmak dizinin olması gereken karakter uzunluğuna geri döner. Örneğin: #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <tchar.h> #define BUFSIZE 1024 int _tmain(int argc, TCHAR *argv[]) { TCHAR env[bufsize]; DWORD dwresult; if (argc!= 2) { fprintf(stderr, "Wrong number of arguments!..\n"); exit(exit_failure); 9

dwresult = GetEnvironmentVariable(argv[1], env, BUFSIZE); if (!dwresult) { _ftprintf(stderr, "GetEnvironmentVariable error: %u\n", GetLastError()); exit(exit_failure); if (dwresult > BUFSIZE) { _ftprintf(stderr, "Buffer too small: %u\n", GetLastError()); exit(exit_failure); _putts(env); return 0; Bu program komut satırı argümanıyla alınan çevre değişkeninin değerini yazdırıyor. Yeni bir çevre değişkenini programlama yoluyla oluşturabilmek ya da zaten var olan bir çevre değişkeninin değerini değiştirebilmek için UNIX/Linux sistemlerinde setenv ve putenv isimli POSIX fonksiyonları kullanılmaktadır. setenv ve putenv fonksiyonlarının prototipleri şöyledir: #include <stdlib.h> int setenv(const char *name, const char *value, int overwrite); int putenv(char *string); setenv fonksiyonunun birinci parametresi eklenecek ya da değiştirilecek çevre değişkeninin ismini, ikinci parametresi de bunun değerini belirtir. Eğer birinci parametreyle belirtilen çevre değişkeni zaten varsa bunun değerinin değiştirilip değiştirilmeyeceği üçüncü parametredeki bayrağa bağlıdır. Fonksiyonun üçüncü parametresi sıfır dışı bir değer olarak girilirse değiştirme yapılır; sıfır girilirse değiştirme yapılmaz. Fonksiyon başarı durumunda sıfır değerine, başarısızlık durumunda -1 değerine geri döner. putenv fonksiyonu setenv fonksiyonunun daha dar kapsamlı bir biçimidir. Fonksiyonun parametresi değişken=değer biçiminde girilmelidir. Fonksiyon belirtilen çevre değişkeni yoksa onu ekler, varsa onun değerini değiştirir. putenv başarı durumunda sıfır değerine, başarısızlık durumunda -1 değerine geri dönmektedir. Burada önemli bir nokta şudur: putenv fonksiyonu ile verilen yazıda değişiklik yapıldığında çevre değişkeni üzerinde de değişiklik gerçekleşir. Yani işletim sistemi putenv fonksiyonuyla verdiğimiz yazının kopyasını değil, doğrudan adresini saklamaktadır. (putenv fonksiyonunun parametresinin const olmayan bir gösterici olduğuna dikkat ediniz. Ayrıca Microsoft derleyicilerinde putenv fonksiyonunun _putenv isimli yaklaşık bir eşdeğerinin bulunduğunu da belirtelim.) Windows sistemlerinde prosesin çevre değişkenlerine ekleme yapmak ya da zaten var olan değişkenin değerlerini değiştirmek için SetEnvironmentVariable isimli API fonksiyonu kullanılmaktadır: 10

#include <windows.h> BOOL WINAPI SetEnvironmentVariable( LPCTSTR lpname, LPCTSTR lpvalue ); Fonksiyonun birinci parametresi çevre değişkeninin ismini, ikinci parametresi değerini belirtir. Fonksiyon eğer çevre değişkeni yoksa onu yaratır, varsa değerini değiştirir. SetEnvironmentVariable başarı durumunda sıfır dışı bir değere, başarısızlık durumunda sıfır değerine geri dönmektedir. Eğer ikinci parametre NULL girilirse bu durum ilgili çevre değişkeninin silineceği anlamına gelmektedir. Bazen prosese ilişkin tüm çevre değişkenlerinin listesini almanız gerekebilir. Bu işlem UNIX/Linux sistemlerinde environ isimli değişken kullanılarak yapılmaktadır. environ, değişkeni kütüphane içerisinde global olarak tanımlanmıştır. Dolayısıyla bu değişkenin kullanılması için extern bildiriminin programcı tarafından yapılması gerekir. [2] environ, char türden bir göstericiyi gösteren göstericidir: extern char **environ; environ sonu NULL adresle biten bir gösterici dizisinin adresini tutmaktadır. Bu dizinin her bir elemanı değişken=değer biçiminde bir çevre değişkenini gösterir: UNIX/Linux sistemlerinde prosesin tüm çevre değişkenleri şöyle elde edilebilir (bu programı önceki bölümde de vermiştik): #include <stdio.h> extern char **environ; int main(void) { int i; for (i = 0; environ[i]!= NULL; ++i) puts(environ[i]); return 0; 11

Windows sistemlerinde prosese ilişkin çevre değişkenlerinin listesi GetEnvironmentStrings ve FreeEnvironmentStrings isimli API fonksiyonlarıyla elde edilmektedir: #include <windows.h> LPTCH WINAPI GetEnvironmentStrings(void); BOOL WINAPI FreeEnvironmentStrings(LPTCH pszenvironmentblock); GetEnvironmentStrings fonksiyonunun parametresinin olmadığını görüyorsunuz. Fonksiyonun geri dönüş değeri A=B\0C=D\0E=F\0\0 biçimindeki çevre değişken bloğunu gösteren bir adrestir. (Her değişken=değer çiftinin null karakter ile sonlandırıldığına ve tüm bloğun da iki null karakter ile sonlandırıldığına dikkat ediniz.) GetEnvironmentStrings fonksiyonuyla elde edilen dinamik alan FreeEnvironmentStrings fonksiyonuyla boşaltılmalıdır. Burada bir nokta üzerinde durmak istiyoruz. GetEnvironmentStrings fonksiyonu bize doğrudan prosesin çevre değişken bloğunu vermez; prosesin çevre değişken bloğunu tahsis edilmiş bir alana kopyalayarak o bloğun adresini verir. Dolayısıyla FreeEnvironmentStrings fonksiyonu da prosesin çevre değişken bloğunu değil tahsis edilmiş olan bu bloğu serbest bırakmaktadır. Aşağıda bir örnek görüyorsunuz: #include <stdio.h> #include <stdlib.h> #include <tchar.h> #include <windows.h> int _tmain(void) { LPTCH penv; if ((penv = GetEnvironmentStrings()) == NULL) { _ftprintf(stderr, _T("GetEnvironmentStrings error: %lu\n"), GetLastError()); exit(exit_failure); while (*penv!= '\0') { _putts(penv); penv += _tcslen(penv) + 1; FreeEnvironmentStrings(pEnv); return 0;.NET ve Mono gibi CLI ortamlarında proseslerin çevre değişkenleri System.Environment sınıfının GetEnvironmentVariable ve SetEnvironmentVariable metotlarıyla elde edilebilir; eklenebilir ve değiştirilebilir. Yine bu sistemlerde Environment sınıfının GetEnvironmentVariables isimli static metodu prosesin tüm çevre değişkenlerini bize vermektedir. Benzer biçimde Java ortamında da java.lang paketindeki System sınıfının getenv isimli static metotları ile çevre değişkenleri elde edilebilir. Maalesef Java ortamında belirli bir çevre değişkeninin eklenmesi ve 12

değiştirilmesi pratik bir biçimde yapılamamaktadır. Bu işlemler için JNI (Java Native Interface) ile işletim sisteminin yukarıda belirttiğimiz sistem fonksiyonları çağrılabilir. Çevre Değişkenleri Çekirdek Tarafından Nasıl Organize Ediliyor? İşletim sistemlerinde çevre değişkenleri genellikle prosesin kullanıcı modundaki (user mode) bellek alanı içerisinde saklanmaktadır. Böylelikle prosesin hiç çekirdek moduna (kernel mode) geçmeden çevre değişkenlerine erişebilmesi mümkün hale getirilmektedir. Tabi çevre değişkenlerinin prosesin bellek alanında nasıl saklandığına ilişkin ayrıntılar sistemden sisteme değişebildiği için programcının aşağı seviyeli bu organizasyonu çalıştığı sisteme özgü bir biçimde incelemesi gerekir. Biz bu bölümde yalnızca Linux ve Windows sistemlerindeki durumu ele alacağız. Linux sistemlerinde çevre değişkenleri prosesin kullanıcı modundaki bellek alanının sonunda saklanmaktadır. Linux ta normal proseslerin sanal bellek alanı şöyledir (adres değerleri 32 bit sistemler için verilmiştir): 13

Burada start_code ve end_code programın kod bölümünün, start_data ve end_data programın data bölümünün, start_brk ve brk ise heap bölümünün başını ve sonunu belirtiyor. Heap alanı duruma göre brk ile belirtilen adresten itibaren aşağıya doğru genişletilmektedir. start_stack programın stack alanının başlangıcını gösteriyor. Stack alanının yukarıya doğru (düşük adrese doğru) büyütüldüğüne dikkat ediniz. Programın komut satırı argümanları ve çevre değişkenleri stack alanının hemen altında bulunuyor. Ayrıca prosese ilişkin yukarıdaki şekilde belirtilen tüm değerlerin proses kontrol bloğu içerisinde dolaylı olarak tutulduğunu belirtelim: exec işlemleri sonucunda programın kod ve data bölümleri sanal belleğe yüklendikten sonra akış çalıştırılabilir formatta belirtilen noktadan başlatılır. Linux sistemlerinde program başlatıldığında stack göstericisi (Intel işlemcilerinde ESP yazmacı) komut satırı argümanlarının ve çevre değişkenlerinin hemen yukarısındadır. Başka bir deyişle, stack göstericisinin gösterdiği yerin hemen altında programın komut satırı argümanları ve onun da hemen altında çevre değişkenleri bulunur. Dolayısıyla program çalışmaya başladığında komut satırı argümanlarının ve çevre değişkenlerinin yeri bellidir. Bildiğiniz gibi, Linux sistemlerinde program çalıştırmak için yalnızca execve fonksiyonu bir sistem fonksiyonu olarak yazılmıştır. Diğer exec fonksiyonları (execl, execlp, execle, execv ve execvp) normal kütüphane fonksiyonları biçiminde gerçekleştirilmiştir. Kütüphanedeki bu fonksiyonlar birtakım düzenleme işlemlerinden sonra execve fonksiyonunu çağırırlar. Proses çekirdek moduna execve çağırmasıyla geçer. İşte komut satırı argümanları ve çevre değişkenleri execve fonksiyonu tarafından sanal bellek alanına kopyalanmaktadır. Linux kaynak kodlarında execve fonksiyonunun ilgili kısmı şöyledir (fs/exec.c): 14

int do_execve(char * filename, char user * user *argv, char user * user *envp, struct pt_regs * regs) {... bprm->exec = bprm->p; retval = copy_strings(bprm->envc, envp, bprm); if (retval < 0) goto out; retval = copy_strings(bprm->argc, argv, bprm); if (retval < 0) goto out;... Buradaki kodun ayrıntıları için makalenin sonunda belirtilen kaynaklara başvurabilirsiniz. Bildiğiniz gibi bir C programında akış hemen main fonksiyonundan başlamaz. Programın gerçek başlangıç noktası, derleyiciler tarafından çalıştırılabilen dosyaya eklenmiş olan başlangıç kodundadır. Aslında main fonksiyonu derleyicilerin başlangıç kodları (startup code) tarafından çağrılmaktadır. Derleyicilerin başlangıç kodları tipik olarak standart kütüphane için gerekli birtakım ilk işlemleri yaparak komut satırı argümanlarını (ve belki de çevre değişkenlerini) main fonksiyonuna argüman olarak geçirirler. Örneğin, Linux sistemlerindeki GNU C (glibc) kütüphanesine ilişkin başlangıç kodlarının main fonksiyonunu çağırması sırasıyla şu aşamalardan geçilerek yapılmaktadır: [5] _start [sysdeps/i386/elf/start.s] libc_start_main [csu/libc-start.c] main Akışın başladığı nokta sembolik makine dilinde yazılmıştır (start.s). Bu noktada stack göstericisinin (32 bit Intel işlemcilerinde ESP yazmacının) durumu şöyledir: 15

Bu şeklin 32 işlemciler dikkate alınarak oluşturulduğunu belirtelim. Global environ değişkenine atama işlemi derleyicilerin başlangıç kodları tarafından yapılmaktadır. (Örneğin glibc kütüphanesinde bu işlem libc-start.c dosyası içerisindeki libc_start_main fonksiyonunda yapılıyor.) Pekiyi environ global değişkenine atama yapıldıkdıktan sonra setenv fonksiyonu ile çevre değişken listesine ekleme yapılmak istendiğinde ne olacaktır? environ global değişkeninin gösterdiği yer prosesin heap alanı içerisinde değildir. Bu nedenle ilk setenv çağrısı ile ekleme yapılmak istendiğinde environ global değişkeninin gösterdiği yerdeki gösterici dizisi prosesin normal heap alanına kopyalanır ve artık environ gösterici dizisi realloc çağırmalarıyla büyütülür. Özetle Linux sistemlerinde çıkaracağımız sonuç şudur: 1. Komut satırı argümanları ve çevre değişkenleri execve tarafından prosesin adres alanına kopyalanır. 2. Derleyicilerin başlangıç kodları environ global değişkenini, çevre değişkenlerinin kopyalandığı bu bölgeyi gösterecek hale getirirler. 3. Daha sonra setenv fonksiyonuyla çevre değişkenlerine ekleme yapıldığında environ dizisi prosesin heap alanına taşınır ve işlemler buradan devam ettirilir. Windows sistemlerinde de tıpkı Linux sistemlerinde olduğu gibi prosesin çevre değişkenleri prosesin kullanıcı modundaki bellek alanında (user space) saklanmaktadır. Bilindiği gibi bu sistemlerde her proses için çekirdek tarafından proses kontrol bloğunun (EPROCESS) yanı sıra bir de çevre bloğu (PEB) tutulmaktadır. Proses kontrol bloğu çekirdek bellek alanı içerisindeyken çevre bloğu prosesin kullanıcı modundaki bellek alanı içerisindedir. Böylece proses kendi çevre 16

bloğuna hiç çekirdek moduna geçmeden doğrudan erişebilmektedir. Windows sistemlerinde -her ne kadar prosesin çevre bloğunun adresi ayrıca proses kontrol bloğunda tutuluyorsa da- her proses kendi çevre bloğunun yerini biliyor durumdadır. Örneğin Intel işlemcilerinde proses kendi çevre bloğuna FS segment yazmacı (selector) yoluyla erişebilmektedir. Burada şunu belirtelim: Prosesin çevre bloğu yalnızca çevre değişkenlerini tutan bir veri yapısı değildir. Prosesin çevre bloğunda çevre değişkenlerinin yanı sıra prosese ilişkin pek çok bilgi de tutulmaktadır. Ancak biz makalemizde bunun ayrıntılarına girmeyeceğiz. Windows sistemlerinde prosesin çevre değişkenlerinin nasıl organize edildiğini aşağıdaki şekille özetleyebiliriz: Proses kontrol bloğunda (EPROCESS) bulunan bir gösterici prosesin çevre bloğunu (PEB) göstermektedir. Prosesin çevre bloğundaki bir gösterici de prosesin çeşitli parametrelerinin bulunduğu bir yapıyı (RTL_USER_PROCESS_PARAMETERS) gösterir. Prosesin çevre değişkenleri buradaki bir gösterici tarafından tutulmaktadır. Windows sistemlerinde çevre değişkenlerinin sistemin kendisi tarafından da tek bir dizi biçiminde tutulduğuna dikkat ediniz. Çevre Değişkenlerine Neden Gereksinim Duyuluyor? Çevre değişkenlerini işletim sistemi genelindeki global değişkenler olarak değerlendirebiliriz. Çevre değişkenlerinin oluşturulması ve programlardan kullanılması çok küçük bir maliyetle yapılabilmektedir. Çevre değişkenlerinin temel kullanım alanları şunlardır: 1. Çevre değişkenleri üst ve alt prosesler arasında haberleşme için kullanılabilirler. Örneğin üst proses bir çevre değişkenini oluşturduktan sonra alt prosesi yaratır ve çevre değişkeninin alt prosese aktarılmasını sağlar; alt proses de bu çevre değişkenini kullanır. 2. Çevre değişkenleri yer belirlemeleri için kullanılabilmektedir. Örneğin bir program bir dosyayı kullanıyor olsun. Program bu dosyayı hangi dizinde arayacaktır? İşte program bir çevre değişkenine bakabilir ve dosyayı o dizinde arayabilir. Örneğin: 17

#define FILE_NAME... char path[max_path]; char *penv; FILE *f; "test.dat" if ((penv = getenv("data_location"))!= NULL) sprintf(path, "%s/%s", penv, FILE_NAME); else strcpy(path, FILE_NAME); if ((f = fopen(path, "w+b")) == NULL) { fprintf(stderr, "Cannot open file: %s\n", path); exit(exit_failure); Burada program gereksinim duyduğu dosyanın yerini DATA_LOCATION isimli bir çevre değişkenine bakarak belirlemektedir. Böylece söz konusu dosya, kullanıcı tarafından herhangi bir dizine yerleştirilip bu çevre değişkeni de ayarlandığında sorun çıkmayacaktır. Kullandığımız pek çok yaygın program da yukarıdaki örneğe benzer bir biçimde çeşitli çevre değişkenlerine başvurmaktadır. Örneğin, gcc derleyicileri C_INCLUDE_PATH isimli çevre değişkenini, Microsoft derleyicileri INCLUDE isimli çevre değişkenini, include edilmiş dosyaların aranacağı dizinleri belirlemek için kullanmaktadır. Derleyiciler ve bağlayıcılar (linkers) gibi temel programların hangi çevre değişkenlerini ne amaçla kullandıklarını bilmeniz gerekebilir. 3. Bazı ortamlarda çeşitli çevre değişkenleri programcıya birtakım bilgiler verebilmektedir. Örneğin UNIX/Linux sistemlerindeki bash kabuk programında USER isimli çevre değişkeni login olan kullanıcının ismini, HOME isimli çevre değişkeni bu kullanıcının varsayılan çalışma dizinini belirtmektedir. Biz bu tür bilgileri hiç işletim sisteminin sistem fonksiyonlarını çağırmadan doğrudan elde edebiliriz. 4. Bazı çevre değişkenlerini işletim sisteminin kendisi ya da bazı kütüphane fonksiyonları da kullanmaktadır. Örneğin UNIX/Linux sistemlerinde exec fonksiyonlarının p li biçimleri (execlp ve execvp), PATH çevre değişkenine başvururlar. Bu fonksiyonlar çalıştırılacak programı PATH çevre değişkeni ile belirtilen dizinlerde sırasıyla ararlar. PATH çevre değişkeni UNIX/Linux sistemlerinde : ile ayrılmış dizinlerden oluşmaktadı. [3] Örneğin: Windows sistemlerinde de program çalıştırmakta kullanılan CreateProcess isimli API fonksiyonu PATH çevre değişkenine başvurmaktadır. Bu fonksiyon çalıştırılacak program dosyasını PATH çevre değişkeni ile belirtilen dizinlerde de arar. Windows sistemlerinde PATH çevre değişkeninde belirtilen dizinler ; karakteri ile birbirinden ayrılmaktadır. Benzer biçimde UNIX/Linux sistemlerinde ve Windows sistemlerinde dinamik kütüphane dosyaları da çevre değişkenleri ile belirtilen dizinlerde aranır. Örneğin Linux sistemlerinde programın kullandığı dinamik kütüphaneler (shared objects) önce çalıştırılacak program dosyasının bulunduğu dizinde, sonra prosesin 18

çalışma dizininde ve en sonunda da LD_LIBRARY_PATH isimli çevre değişkeninin belirtildiği dizinlerde aranmaktadır. Windows sistemlerinde ise dinamik kütüphaneler (dynamic link libraries) işletim sistemi tarafından önce.exe dosyasının bulunduğu dizinde, sonra prosesin çalışma dizininde ve daha sonra da PATH çevre değişkeninin belirttiği dizinlerde aranmaktadır. [4] [1] vfork fonksiyonunu da bir çeşit fork fonksiyonu olarak ele alabiliriz. [2] POSIX standartlarında environ değişkenine ilişkin extern bildiriminin herhangi bir başlık dosyasında yapılması öngörülmemiştir. [3] Kabuk programının da programları exec fonksiyonlarının p li biçimleriyle çalıştırdığını belirtelim. Böylece biz kabuk üzerinden bir programı yol ifadesi belirtmeden çalıştırmak istersek program dosyası PATH çevre değişkeni ile belirtilen dizinlerde aranacaktır. Eğer program ismi bir yol ifadesi içeriyorsa execlp ve execvp fonksiyonları PATH çevre değişkenlerine hiç başvurmamaktadır. UNIX/Linux sistemlerinde execlp ve execvp fonskiyonları dosyayı prosesin çalışma dizininde aramaz. Zaten bu nedenle kabuk üzerinden bulunduğumuz dizindeki programları./sample biçiminde yol ifadesi belirterek çalıştırmaktayız. Tabi bu sistemlerde prosesin çalışma dizinini de PATH çevre değişkenine ekleyebilirsiniz. [4] Dinamik kütüphanelerin aranması işleminin bu sistemlerde çeşitli ayrıntıları vardır. Bu konuda işletim sisteminin kendi dökümanlarına başvurabilirsiniz. [5] Buradaki kaynak kodlar glibc-2.7 kütüphanesinden alınmıştır. GNU C kütüphanesinin kaynak kodlarını http://ftp.gnu.org/gnu/glibc/ adresinden indirebilirsiniz. Kaynaklar Aslan, K. (2001). Sistem Programlama ve İleri C Uygulamaları Kurs Notları. İstanbul: C ve Sistem Programcıları Derneği. Aslan, K. (2001). Win32 Sistem Programlama Kurs Notları. İstanbul: C ve Sistem Programcıları Derneği. Aslan, K. (2002). UNIX/Linux Sistem Programlama Kurs Notları. İstanbul: C ve Sistem Programcıları Derneği. Bovet, D. and Cesati, M. (2006). Understanding the Linux Kerne, Third Edition. O Reilly Media, Inc. Mauerer W. (2008). Professional Linux Kernel Architecture. Indianapolis: Wiley Publishing, Inc. Richter, J. M. (1999). Programming Applications for Microsoft Windows with Cdrom. 4th. Edition. Redmond, Washinton: Microsoft Press. 19

Russinovich M. E., Solomon D. A. (2005). Windows Internals. 4th Edition. Redmond, Washington: Microsoft Press. Stevens, R., Rago, S. A. (2005). Advanced Programming in the UNIX(R) Environment (2nd Edition). Addison-Wesley Professional. 20