Bu serinin devamı niteliğinde ki ana terimleri incelememiz devam ediyor. İlk bölümü okumadıysanız lütfen aşağıda ki linkten okumayı ihmal etmeyin.
Birinci Ders – Teknolojiye Giriş
Önceden Tanımlanmış Değişkenler
Kabuk tarafından dahili olarak ayarlanan ve kullanıcıya sunulan bazı değişkenler vardır:
1$ – $9: Konumsal parametreler
$0: Komut adı
$#: Konumsal argümanların sayısı
$? : Yürütülen son komutun çıkış durumu ondalık dizgesi (0,1,2 ..) olarak verir.
$$: Eşsiz(unique) dosya isimleri oluşturmak için yararlı olabilen, çalışılan kabuğun süreç(process) numarası.
$! : Arka planda çalışan son komutun işlem kimliği (Son arka plan işleminin PID’sini tutar).
$- : Kabuğun bu çağrısına uygulanan tedarik edilmiş mevcut seçenekler.
$* : $1 ‘dan başlayan, kabuğun tüm argümanlarını içeren bir dize.
$@: Alıntılananlar hariç, yukarıdakiyle aynı.
Not: ∗ve @ alıntı yapıldığında aynıdır ve değişkenlere genişler.
“$*”, boşlukla birleştirilmiş, kabuğun tüm argümanlarını içeren tek bir kelimedir. Örneğin, ‘1 2’ 3 , “1 2 3” olur.
“$@”, kabuk tarafından alınan argümanlarla aynıdır, sonuçta ortaya çıkan sözcük listesi, kabuğa verilenlerle tamamen eşleşir. Örneğin, ‘1 2’ 3 , “1 2” “3” olur.
Argümanları komut dosyalarına geçirme
Standart UNIX komutları gibi, kabuk komut dosyaları(shell scripts)’da komut satırından değişken alabilir.
Bağımsız değişkenler, komut satırından $1 ile $9 arasındaki konumsal parametreler kullanılarak bir kabuk programın içine geçirilir.
$0 konum parametresi, komut adını veya kabuk komut dosyasını içeren yürütülebilir dosyanın adını belirtir.
Tüm konumsal parametreler $* özel parametresi kullanılarak ifade edilebilir.
Examples
$ cat pass_arg. # 5 sayı kabul eden ve toplamını gösteren bir script.
aktarılan parametrelerin gösterimleri(echo ile) : $1, $2, $3, $4, $5
betiğin adının gösterimi (echo ile) : $0
aktarılan parametrelerin sayısı(echo ile) : $#
sum=`expr $1 + $2 + $3 + $4 + $5`
toplamları : $sum
shift komutu
Bir komut dosyasına 9’dan fazla parametre iletilirse, parametrelere erişmek için iki alternatif vardır:
Notasyonu ${n}
shift komutu
Shift komutu parametreleri bir konum sola kaydırır.
Shift komutunun yürütülmesinde, ilk parametrenin üzerine ikincisi yazılır, ikinci’nin üzerine üçüncü yazılır ve bunun gibi devam eder.
Örnek;
Farklı sayıları kabul edecek ve toplamlarını bulacak bir komut dosyası yazalım. Parametrelerin sayısı değişebilir.
$ cat sum_arg
sum=0
while [ $# -gt 0 ]
do
sum=`expr $sum + $1`
shift
done
echo sum is $sum
Örnek; Bu örneği birtane script dosyasının içine yazıp daha sonra sh script.sh 1 2 3 4… şeklinde parametreler vererek terminalden çağırıp deneyebilirsiniz.
#!/bin/bash
echo “arg1=$1 arg2=$2 arg3=$3”
shift
echo “arg1=$1 arg2=$2 arg3=$3”
shift
echo “arg1=$1 arg2=$2 arg3=$3”
shift
echo “arg1=$1 arg2=$2 arg3=$3”
Null komutu
Kabuk yerleşik bir null komutuna sahiptir
formatı basit;
- :
Amaç hiçbir şey yapmamak
Genellikle bir komutun, özellikle de komutlarda görünmesi gerekliliğini yerine getirmek için kullanılır.
if grep “^$system” ~/mail/systems > /dev/null
then
:
else
echo “$system is not a valid system”
exit 1
fi
Kabuk, bundan sonra bir komut yazmanızı gerektirir. Sistem geçerliyse, hiçbir şey yapılmaz
&& ve || operatörleri
Kabuk, bir önceki komutun başarılı veya başarısız olmasına bağlı olarak bir komutu çalıştırmanıza olanak tanıyan iki özel yapıya sahiptir.
&& operatörü eğer önceki komut başarılı bir şekilde derlenirse sonraki komutu uygular.
- komut1 && komut2
komut2 yalnızca komut1 sıfır çıkış durumunu döndürürse çalıştırılır.
örnek;
[ -z $EDITOR ] && EDITOR=/bin/ed
|| operatörü eğer önceki komut başarısız bir şekilde derlenirse sonraki komutu uygular.
- komut1 || komut2
komut2 yalnızca komut1 sıfır olmayan bir çıkış durumu döndürdüğünde çalıştırılır
Örnekler;
[ -z $PATH ] || echo $PATH
grep “$name” phonebook || echo \
“Not found $name“
who | grep “^$name ” > /dev/null || echo \
“$name’s not logged on“
(Satırın sonunda \ kullanıldığında, kabuğun satıra devam ettiğini bildirir.)
&& ve || Aynı komut satırında da birleştirilebilir:
who | grep “^$name ” > /dev/null && \
echo “$name is logged on” || echo “$name’s \
not logged on”
Grep başarılı olursa ilk echo gerçekleşir;başarısız olursa ikinci echo.
Bu operatörler if komutları ile temsil edilebilir.
if grep “$name” phonebook
then
:
else
echo “Couldn’t find $name“
fi
Koşullu İfadeler
Her Unix komutu, çıkışta kabuğun sorgulayabileceği bir değer döndürür. Bu değer salt okunur kabuk değişkeni $? İçinde tutulur.
0 (sıfır) değeri başarıyı gösterir; 0 (sıfır) dışında herhangi bir şey başarısızlık anlamına gelir.
Tamsayı kullanıyorsanız: ((koşul))
Dizeler kullanılıyorsa: [[koşul]]
Çıkış durumları duruma bağlı olarak sıfır veya sıfır değildir
Örnekler:
(( a == 10 ))
(( b >= 3 ))
[[ $1 = -n ]]
[[ ($v != fun) && ( $v != games) ]]
(( Z > 23 )) && echo Yes
Dosya varlığı, dosya izinleri, sahiplik, dosya türü vb. İçin özel koşullar.
- [[ -e $file ]] –File exists? (dosya mevcut mu?)
- [[ -f $file ]] –Regular file? (dosya normal mi?)
- [[ -d $file ]] –Directory? (dizin mi?)
- [[ -L $file ]] –Symbolic link? (sembolik link?)
- [[ -r $file ]] –File has read permission? (dosya okuma izni varmı)
- [[ -w $file ]] –File has write permission? (dosya okuma yazma varmı)
- [[ -x $file ]] –File has execute permission? (dosya çalıştırma izni varmı)
- [[ -p $file]] –File is a pipe?
İf deyimi
İf ifadesi verilen komutun çıkış durumunu kullanır ve şartlı olarak aşağıdaki ifadeleri çalıştırır.
Genel sözdizimi:
if koşul
then
komutlar (koşul doğruysa)
else
komutlar (koşul yanlışsa)
fi
İç içe if ifadesi:
if (—–)
then …
else if …
…
fi
fi
elif ifadesi, else if ifadesi için kısa yol olarak kullanılabilir.
örnek:
if [[ -r $fname ]]
then
echo “$fname is readable”
elif [[ -w $fname && -x $fname ]]
then
echo “$fname is writeable and executable”
fi
test komutu
Unix sistemi, önceki komutun çıkış durumunu araştıran ve sonucu başarı ya da başarısızlık biçiminde çeviren, yani sonucu 0 ya da 1 olan test komutu sağlar.
Test komutu herhangi bir çıktı üretmez, ancak testin başarısız olup olmadığını kontrol etmek için çıkış durumu if ifadesine geçirilebiliriz.
Herhangi bir komutun çıkış durumunu nasıl öğrenebilirim?
Tüm komutlar çıkış durumunu, echo komutu kullanılarak görüntülenebilen önceden tanımlanmış bir Shell Değişkenine ‘?’ Döndürür. Örneğin;
echo$?
Bunun çıktısı 0 (Sıfır) ise önceki komut başarılı olmuş demektir veya çıktı 1 (Bir) ise önceki komutun başarısız olduğu anlamına gelir.
Test komutunun aşağıda açıklanan dosyalar, sayısal değerler ve diziler üzerinde çalışması için belirli operatörleri vardır: Test komutuyla kullanılan Sayısal
Değişkenlerdeki İşleçler:
- -eq : equal to(eşittir)
- -ne : not equals to(eşit değildir)
- -gt : grater than (dan büyüktür)
- -lt : less than (dan küçüktür)
- -ge : greater than or equal to(büyük veya eşittir)
- -le : less than or equal to(küçük veya eşittir.)
Test komutuyla kullanılan String Değişkenleri işleçleri:
- = : equality of strings(dizgelerin eşitliği)
- != : not equal (eşit değil)
- -z : zero length string (sıfır karakter içeren dize yani null dize).
- -n : String length is non zero.(dize uzunluğu sıfır değil)
Örnekler:
$> a=12; b=23
$> test $a –eq $b
>echo? # 1verir
$> name=”Ahmet”
$> test –z $name #return 1
$> test –n $name #return 0
$> test –z “$address”
$> test $name = “Ali”
Test komutuyla kullanılan dosyalardaki operatörler:
- -f: dosya var.
- -s: dosya var ve dosya boyutu sıfır değil.
- -d: dizin var.
- -r: dosya var ve okuma iznine sahip.
- -w: dosya mevcut ve yazma iznine sahip.
- -x: dosya var ve yürütme iznine sahip.
Örnekler:
$> test –f “mydoc.doc”
# mydoc.doc dosyasını kontrol eder, varsa 0, yoksa 1 döndürür.
$> test –r “mydoc.doc”
# mydoc.doc için okuma izni olup olmadığını denetler
$> test –d “$HOME”
# kullanıcıların ana dizininin varlığını kontrol eder..
Test komutuyla kullanılan Mantıksal Operatörler:
Birden fazla koşulu birleştirmek mantıksal AND, OR ve NOT işleçleriyle yapılır.
- -a: mantıksal AND(VE)
- -o: mantıksal OR(VEYA)
- ! : mantıksal NOT(DEĞİL)
$> test –r “mydoc.doc” –a –w “mydoc.doc”
# mydoc.doc dosyasının hem okuma hem de yazma iznini kontrol eder ve sonucA bağlı olarak 0 veya 1 döndürür.
if who | grep -s hakan > /dev/null
then
echo hakan CE sunucusuna giris yapti
else
echo hakan CE sunucusunda mevcut degis
fi
Bu script, şu anda sisteme giriş yapmış olan kişileri listeler ve çıktıyı grep üzerinden pipe ile yönlendirir.
durum(case) açıklamaları
Sözdizimi:
case expression in
pattern1)
commands ;;
pattern2)
commands ;;
… *)
commands ;;
esac
örnekler:
örnek1:
case $1 in
-a)
a seçeneğiyle ilgili komutlar ;;
-b)
b seçeneğiyle ilgili komutlar ;;
*)
diğer tüm seçenekler ;;
esac
örnek2:
clear
echo “1. Date and time”
echo
echo “2. Directory listing”
echo
echo “3. Users information “
echo
echo “4. Current Directory”
echo
echo –n “Enter choice (1,2,3 or 4):”
örnek3:
read choice
case $choice in
1) date;;
2) s -l;;
3) who ;;
4) pwd ;;
*) echo wrong choice;;
esac
bu komutları birtane script dosyasının içine yazıp bu dosyayı terminalden çalıştırdığımız zaman bizden birtane argüman ister bu argümanı okuduktan sonra 1,2,3,4 veya diğer olacak şekilde ayırarak farklı komutları çalıştırır.
for döngüsü
Sözdizimi:
for var [in list ]
do
commands
done
Tek bir satırdaki komutlar noktalı virgülle (;) ayrılır.
Liste belirtilmezse, $@ kabul edilir.
Aksi takdirde ${list [*]} , Burada liste bir dizi değişkenidir.
örnekler:
for colors in Red Blue Green Yellow Orange Black Gray
do
echo $colors
done
echo
Bu örnek verilen tüm renkleri terminale yazdırır.
While döngüsü
Sözdizimi:
while command-list1
do
command-list2
done
command-list1’deki son komutun çıkış durumu 0 (sıfır) ise, command-list2’deki komutlar yürütülür.
Anahtar kelimeler break, contiune ve return , C / C ++ ile aynı özelliklere sahiptir.
break [num] veya contiune [num] # num döngü sayısıdır
Until döngüsü
Sözdizimi:
Until command-list1
do
command-list2
done
Döngü, command-list1’in çıkış durumu sıfır olmadıkça gerçekleştirilir.
While/until komutunun çıkış durumu, command-list2’de yürütülen son komutun çıkış durumudur. Eğer böyle bir komut listesi çalıştırılmazsa, while/until çıkış 0 durumundadır.
örnekler:
eval komutu
… (Bölüm 7 henüz bitmedi devam edicek :))
Bölüm-8 Gelişmiş Kabuk Betiği(Advanced Shell Scripting )
Fonksiyon oluşturma ve kullanma
Bir kabuk fonksiyonunun tanımı aşağıdaki gibidir:
isim(){list ;}
Geçerli ve geçersiz fonksiyon tanımları: lsl(){ ls -l ; } # geçerli
lsl { ls -l ; } # geçersiz
sh için takma ad tanımlama:
$> cat mycd
cd () { chdir 1:−$HOME;PS1=”‘pwd‘ ” ; export PS1 ; }
$> source mycd
Örnekler:
yazdir(){
for i in {1..13}
do
echo “hakan”
echo “$i”
done
}
Bu fonksiyon 13 kere hakan ve sırasıyla sayıları yazar ama bu fonksiyonu terminalde koşabilmek için dosyayı source etmemiz gerekir.Bunuda şu şekilde yaparız.
- source dosya_adı
Bu komuttan sonra dosyamızdaki fonksiyonu ismiyle çağırabiliriz(sadece mevcut terminalde):
yazdir
hakan
1
hakan
2 …
Her dizinin tek bir satırda listelenmesiyle, PATH’nin geçerli değerini listelemek:
lspath() {
OLDIFS=”$IFS”
IFS=:
for DIR in $PATH ; do echo $DIR ; done
IFS=”$OLDIFS”
}
$> lspath | grep “/usr/dt/bin”
Dizinini uygun hale getirmek:
setPath() {
PATH=${PATH:=”/sbin:/bin”};
for _DIR in “$@”
do
if [ -d “$_DIR” ] ; then
PATH=”$PATH”:”$_DIR” ; fi
done
export PATH
unset _DIR
}
Örnek bir çağrı:
$> setPath /sbin /usr/sbin /bin /usr/bin /usr/ccs/bin
Her argümanının bir dizin olup olmadığını kontrol eder ve bir dizin varsa PATH’ye eklenir.
Fonsiyona parametre verme
Bir parametre script’e iletildiği gibi bir fonsiyona da iletilebilir.
fonksyion tanımlamak için sözdizimi:
fonksyion fonksyion_adı() {
deyim1
deyim2
deyimN
}
Bu işlev komut satırından veya kabuk betiğinin içinde aşağıdaki şekilde çağrılır: fonsiyon-adı arg1 arg2 arg3 argN
örnek:
$ vi pass
function demo()
{
echo “All Arguments to function demo(): $*”
echo “First argument $1”
echo “Second argument $2”
echo “Third argument $3”
return
}
# fonksyionu cağrıyoruz.
demo -f foo bar
çıktısı:
All Arguments to function demo(): -f foo bar
First argument -f
Second argument foo
Third argument bar
Değer döndürme(return)
Örnek tanım:
function topla {
(( toplam=$1+$2 ))
return $toplam
}
fonksiyonu çağırmak:
topla 2 3
echo $?
$? son işlev çağrısından veya komut tarafından döndürülen değerdir.
Fonksiyonlar arasında veri paylaşımı
C kabuğu(shell),csh, UNIX dosya sisteminde hızlı hareket etmek için üç yardımcı komut sağlar:
- popd
- pushd
- dirs
Bu komutlar, dahili olarak bir dizin yığınını korur ve kullanıcının yığından dizinleri ekleyip, çıkarmasına olanak tanır ve yığının içeriğini listeler.
dirs uygulaması:
dirs() {
# IFS’yi kaydedin, ardından şuna ayarlayın:
# _DIR_STACK öğelerine ayrı ayrı erişmek için.
OLDIFS=”$IFS”
IFS=:
# her dizini ve ardından bir boşluk yazdır
for i in $_DIR_STACK
do
echo “$i \c“
done
# tüm girişlerden sonra yeni bir satır ekle
# _DIR_STACK yazdırıldı echo
# IFS’yi geri yükle IFS=”$OLDIFS”
}
pushd uygulaması:
…(gelecek)
popd uygulaması:
…(gelecek)
echo komutu
metin veya değişken değerlerini gösterir.
echo [options] [string, değişkenler …]
Seçenekler:
- -n Sondaki yeni satırı çıkışa vermez.
- -e Aşağıdaki kaçış karakterleri yorumlar.
- \c Sondaki yeni satırı bastırır.
- \a Bir uyarı (zil).
- \b geri al.
- \n yeni satır.
- \r satır başı.
- \t yatay sekme(tab).
- \ters eğik çizgi.
renkli metin gösterme
echo ile birlikte kullanılan bazı kontrol karakterleri vardır.
Bu kod, mesajı Mavi renkte yazdırır:
$> echo “\033[34m Hello Colorful World!”
Hello Colorful World!
Bu ANSI kaçış dizisini kullanır (\033[34m).
\033, kaçış karakteri, biraz harekete geçiyor
[34m kaçış kodu ön plan rengini Mavi olarak ayarlar
[ CSI’nin başlangıcıdır (Komut Dizisi Giriş).
34, parametredir.
m harfdir (eylemi belirtir).
Genel sözdizimi:
echo -e “\033[ escape-code your-message “
(Bu başlık vakit bulursam genişletilebilir…)
Komut dosyası yürütme(script execution)
Komut dosyasını kabuk programına bir argüman olarak verin (ör. Bash my_script).
Veya komut dosyasında hangi kabuğun kullanılacağını belirtin.
- script’in ilk satırı #!/bin/ bash
- Komut dosyasını chmod kullanarak yürütülebilir duruma getirin.
- PATH’in geçerli dizini içerdiğinden emin olun.
- Doğrudan komut satırından çalıştırın
Derleme gerekmez!
Bölüm-9 Yazılım Geliştirme: g++ ve make
Yazılım geliştirme süreçi
- Kaynak dosyalarının oluşturulması.(.c, .h, .cpp)
- Derleme (*.c , *.o) ve bağlama(linking)
- Programları çalıştırma ve test etme.
Geliştirme araçları:
Kaynak dosyalarının oluşturulması.(.c, .h, .cpp)
Text editörleri; vi ,emacs
versiyon kontrol sistemleri; rcs, cvs
Derleme (*.o) and bağlama.
Derleyiciler; gcc, g++
Otomatik yapı(building) araçları; make
Çalıştırma ve test etme(xdb,gdb)
Derleme şüreci

Temel g++ Örnekleri
- g++ hello.cpp
hello.cpp derlenir
çalıştırılabilir a.out dosyası üretir - g++ -o hello hello.cpp
hello.cpp derlenir
çalıştırılabilir hello dosyası üretir. - g++ -o hello hello.cpp util.cpp
hello.cpp ve util.cpp derlenir
çalıştırılabilir hello dosyası üretir.
Ayrı ayrı derleme: Herhangi bir kaynak dosyadan, daha sonra çalıştırılabilir yapmak için bağlanacak bir nesne dosyası oluşturabilirsiniz.
- g++ -c hello.cpp
- g++ -c util.cpp
- g++ -o hello hello.o util.o
g++ seçenekleri
- -c
kaynak dosyalarını derler ama bağlantı(link) yapmak
çıktı(output) kaynak dosyaya karşılık gelen bir nesne dosyasıdır. - o <dosya>
çıktıyı <dosya> adlı bir dosyaya koyar. - g
çıktıdaki hata ayıklama(debugging) sembollerini dahil et.
daha sonra hata ayıklama programı(gdb) tarafından kullanılacak. - Wall
Tüm uyarıları göster(program hala derlenebilir). - -D<macro>
macro ‘1’ string’i ile tanımlanır - -l<name>
lib<name>.a adlı kütüphaneyi dahil eder. - -I<path>
verilen dizinde bulunan dosyalar dahil etmek(include) için bakar. - L<path>
verilen dizinden bulanan kütüphanelere bakar.
g++’nın kütüphaneler ve dahil edilen dosyalar için varsayılan dizinleri vardır.
g++ ‘da tanımlar
Genellikle programlar aşağıdakilere dayanan koşullu parçalar içerir:
#ifdef DEBUG
printf(“value of var is %d”, var);
#endif
Önişlemci tanımlarını komut satırından ayarlayabilirsiniz:
g++ -DDEBUG -o prog prog.c
(genişletilecek…)
Derlemede’de make kullanımı
Çok sayıda dosya içeren orta ve büyük çaplı yazılım projelerini derlemek şu sebeblerden zor olur:
- Her seferinde tüm dosyaları doğru bir şekilde derlemek için komutların yazılması
- Hangi dosyaların değiştirildiğinin takip edilmesi
- Dosyalar arasındaki bağlılıkların takip edilmesi
make, bu işlemleri otomatikleştirir.
make’in temek işlemleri
Programı oluşturmak için, kurallar içeren [Mm]akefile adlı bir dosyayı okur.
- Eğer program başka bir dosyaya bağlıysa, ozaman bu dosya oluşturulur.
- Tüm bağımlılıklar bağımlılıklar zincirinde geri doğru çalışacak şekilde inşa(build)edilir.
- Programlar yalnızca bağlı(depend) oldukları dosyalardan daha eski ise inşa(built) edilebilir.
Temel Makefile Örnekleri
# mydb için Makefile
mydb: mydb.o user.o database.o
g++ -o mydb mydb.o user.o database.o
mydb.o : mydb.cpp mydb.h
g++ -c mydb.cpp
user.o : user.cpp mydb.h
g++ -c user.cpp
database.o : database.cpp mydb.h
g++ -c database.cpp

Bir Makefile’ın parçaları
Bağımlılık satırları:
- Hedef(target) adlarını ve bağımlılıklarını içerir (isteğe bağlı)
- bağımlılıklar(dependencies)
Dosyalar
hedefler Komutlar: - bağımlılık satırın altında olmalı
- her zaman bir çıkıntıyla(tab) başla
- bağımlılığı karşılamak için komutlar

Makrolar(macros) ve özel değişkenler
Makefile’daki metni temsil etmek için makrolar kullanılır.
- yazarken kaydeder.
- Makefile’ın kolayca değiştirilmesine izin verir.
- atamalar(assignment)
Kullanımı: ${MACRONAME}
Komutlarda özel değişkenler kullanılır:
- $@ hedefi temsil ediyor.
- $? bağımlılıkları temsil ediyor.
örneği basitleştirme
OBJS = mydb.o user.o database.o
CC = /usr/bin/g++
mydb: ${OBJS}
${CC} -o @?
mydb.o: mydb.cpp mydb.h
${CC} -c $?
user.o: user.cpp mydb.h
${CC} -c $?
database.o: database.cpp mydb.h
${CC} -c $?
Make’i cağırmak (invoking make)
- açıklama dosyası olduğundan emin olun
makefile veya Makefile olarak adlandırılır
kaynak dosyalarının bulunduğu dizinde - make
dosyada ilk hedefi oluşturur - make hedef(leri)
hedef(leri) oluşturur(builds). - diğer seçenekler:
-n: komutları çalıştırma, sadece listele
-f <file>: [Mm]akefile yerine <file> ‘ı kullan.
Diğer Makefile Notları ve Son ek(suffix) kuralları
Yorumlar ‘#’ ile başlar
Bir satırın başına veya yorum olmayan bir satırın sonuna konulabilir.
Çok uzun olan satırlar, önceki satırın sonuna line \ koyarak bir sonraki satırda devam edebilir.
Son Ek Kuralları: her .o dosyasını kaynak dosyadan nasıl oluşturacağını söylemek hala sıkıcı. Sonek kuralları bu tür durumları genelleştirmek için kullanılabilir.
- Varsayılan bir sonek kuralı, komut tarafından çalıştırarak kaynak dosyalarını, .o dosyalarına dönüştürür:
${CC} CFLAGS−c< - $< , önkoşul anlamına gelir (file.cpp)
En Basit Makefile Örneği
OBJS = mydb.cpp user.cpp database.cpp
CC = /usr/bin/g++
mydb: ${OBJS}
${CC} -o @?
Diğer Faydalı Makefile İpuçları
Ara dosyaları(intermediate) kaldırmanın bir yolunu dahil edin.
- clean:
rm –f mydb
rm -f * .o
çoklu programlar oluşturmak için bir hedef dahil edin.
- all: mydb mycalendar myhomework
Bölüm-10 Hata ayıklama (Debugging)
(pek yakında…)
Bölüm-11 Dosya yönetimi (File Management)
(devamı pek yakında…)
Sistem Çağrıları
Programlar, kütüphaneler üzerinden sistem çağrıları yapar.
- libc, sistem çağrıları için C arayüzü sağlar.
- doğrudan Unix çekirdeğine bir altprogram çağrısı.
4 ana sistem çağrısı kategorisi:
- Dosya yönetimi.
- Süreç yönetimi.
- İletişim.
- Hata ve sinyal işleme.
Program yürütmek
- Özel bir başlatma rutini (crt0) her zaman programınıza bağlanır.
- Bu rutin argümanları okur ve main’i çağırır.
- Libc kütüphanesi programınıza otomatik olarak bağlanır; bu şekilde birçok C işlevine (printf, open, vb.) erişiminiz vardır.
- Programınız, çıkışta dosya tanımlayıcılarını kapatan ve diğer kaynakları temizleyen özel işlevler de çağırır.
C’ye karşı C++
- string veri türü yok
Bunun yerine karakter dizileri kullanın Karakter dizilerini “atamak” için strcpy (), strncpy (), strcmp (), strncmp () kullanın. - Gömülü beyan yok(embedded declarations)
Tüm değişkenlerin bir kod bloğunun başlangıcında bildirilmesi gerekir - Çok farklı Dosya ve Standart G / Ç fonksiyonları
printf() cout’a karşı
scanf() ve fgets() cin’e karşı
Arabelleksiz(unbeffered) G/Ç vs. Standart G/Ç
Arabelleksiz giriş/çıkış:
- sistem çağrılarını kullanarak g/ç gerçekleştirebilir.(open,read,write,close,lseek)
- arabellek boyutunu ve bayt’ların numarasını belirtmeniz gerekir.
- Biçimlendirme seçeneği yok.
- Bu fonksiyonlar dosya tanımlayıcılarını argüman olarak kullanır.
- Sabitler unistd.h’da tanımlanır(STDIN_FILENO, gibi).
Standart giriş/çıkış:
- Bir C kütüphanesi fonksiyon dizisi(printf,scanf,getc)
- Arabellekleme(bufferin) otomatik.
- Birçok biçimlendirme seçeneği
- stdio fonksiyonları ilkel sistem çağrılarından oluşur.
- Sabitler stdio.h’da tanımlanır(stdin, gibi).
Temel Dosya G/Ç
- Unix’de herşeyin bir dosya olduğunu hatırlayın ve unutmayın.
- çekirdek(kernel), her süreç için açık dosyaların bir listesini tutar.
- Dosyalar okuma,yazma için açılabilir.
- G/Ç sistem çağrılarını kullanmak için <stdio.h> dahil edilmelidir.(include)
Not: Unix çekirdeği ile etkileşime girmek için kullanılan sistem çağrılarının bazıları diğer işletim sistemlerinde de mevcuttur. Bununla birlikte, bunlar çok farklı şekilde uygulanabilir (muhtemelen). Bazıları hiç mevcut değil. - Çoğu Unix G/Ç, 5 sistem çağrısı ile yapılabilir.
open, read, write, close, lseek - Her dosya bir dosya tanımlayıcısı tarafından referans edilir(bir tam sayı)).
- Üç dosya otomatik olarak açılır:
FD 0: standart giriş
FD 1: standart çıkış
FD 2: standart hata - Yeni bir dosya açıldığı zaman, en küçük FD’ye atanır.
- man -s 2 (daha fazla bilgi almak için)
open()
int open(char *path, int flags, mode_t mode);
path: absolute(mutlak) veya relative(göreceli) path
flags(bayraklar):
- O_RDONLY – okumak için aç
- O_WRONLY – yazmak için aç
- O_RDWR – okuma ve yazma için aç
- O_CREAT – mevcut değilse dosyayı oluştur
- O_TRUNC – eğer varsa dosyayı keser (üzerine yazar).
- O_APPEND – sadece dosyanın sonuna yaz
modu: eğer O_CREAT kullanıyorsanız, izinleri açıkca belirtin
Yeni atanan dosya tanımlayıcısını döndürür.
- Oluşturulma izinleri:
777 vs 0777 vs 0x777
izinler:- read(r–,4)
- write(-w-,2)
- execute(–x,1)
- user(0700)
- group(0070)
- others(0007)
- fd = open(”name”, O_RDWR|O_CREAT, 0700);
- fd dönen değer:
fd >= 0 – açma başarılır
fd < 0 – açma başarısız
read() ve write()
ssize_t read(int fd, void *buf, size_t nbytes);
ssize_t write(int fd, void *buf, size_t nbytes);
- fd, open tarafından döndürülen değerdir.
- buf genellikle bir veri dizisidir
- nbayt buf boyutu(okunan) veya yazılacak veri boyutudur
- döndürülen değer:
> 0 okunan veya yazılan bayt sayısıdır.
<= okumak için nbayt
0 EOF(dosya sonunun habercisi)
< 0 error
read() örneği
bytes = read(fd, buffer, count);
Fd ile ilişkili dosyadan okur.
okunan bayt sayısını veya hata mesajı yani -1 değerini döndürür
int fd=open("someFile", O_RDONLY);
char buffer[4];
int bytes = read(fd, buffer, 4);
write() örneği
bytes = write(fd, buffer, count); Fd ile ilişkili dosyaya yazar.
yazılan bayt sayısını veya hata mesajı yani -1 değerini döndürür
int fd=open("someFile", O_WRONLY);
char buffer[4];
int bytes = write(fd, buffer, 4);
stdin’i stdout’a kopyalamak
#define BUFFSIZE 8192
// bu nasıl seçilir
int main(void) {int n;
char buf[BUFFSIZE];
while ((n=read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
if (write(STDOUT_FILENO, buf, n) != n)
printf(“write error”);
if (n < 0)
printf(“read error”);
}
close()
int close(int fd);
- fd ile ilişkili dosya kapatılır.
- başarı durumda 0, hata durumunda -1 döndürür.
- close(1);
standart çıkışı kapatır.
lseek()
off_t lseek(int fd, off_t offset, int whence);
- Dosya işaretcisini(pointer) yeni konumuna taşır.
- fd, open tarafından döndürülen sayıdır.
- off_t , long veya int… değil, dörtlü olabilir.
- ofsett , bayt sayısıdır.
- nereden(whence):
SEEK_SET – dosyanın başlangıcından itibaren ofsett(bayt sayısı kadar)
SEEK_CUR – mevcut konumdan offsett
SEEK_END – dosyanın sonundan ofsett - Dosyanın başlangıcında veya hata durumunda offsett -1 döndürür.
lseek() örnekler
Standart girişin aranıp aranmayacağını test edelim.
int main(void) {
if (lseek(STDIN_FILENO, 0, SEEK_CUR)==-1)
printf("cannot seek\n");
else
printf("seek OK\n");
exit(0);
}
a.out < /etc/motd “seek OK ” verir.
$ cat < /etc/motd | a.out “cannot seek” verir
$ a.out < /var/spool/cron/FIFO “cannot seek” verir
char buf1[] = “abcdefghij“, buf2[] = “ABCDEFGHIJ”;
int fd = creat(“file.hole”, FILE_MODE);
write(fd, buf1, 10);
lseek(fd, 40, SEEK_SET);
write(fd, buf2, 10);
a.out
$ ls -l file.hole boyutunu kontrol et
-rw-r–r– 1 root 50 Jul 31 05:50 file.hole
$ od -c file.hole gerçek içeriğine bakalım
Bölüm-12 Süreç Yönetimi (Process Management)
Unix’te süreçler
Süreç(process): Temel yürütme birimi
- Bir programın çalıştırılması
- Bir süreç kimliğine sahiptir (PID)
- Bir süreç hiyerarşisinde oluşurlar (parents and children)
- Root(kök) başlangıç sürecidir.
- Her sürecin kendi durumu/içeriği/belleği vardır.
Süreçler ile ilgili shell komutları:
ps, top, kill, nice,…
süreç belirtmek

Dosya Nesneleri ve Dosya Tanımlayıcıları
Stdio kütüphanesi, arabellek(buffering) kullanan FILE nesneleri sağlar.
- FILE *stdin, *stdout, *stderr;
Neden arabelleğe alma? Verimlilik.
FILE nesneleri, dosya tanımlayıcıların üzerine inşa edilmiştir.
Süreç yönetimi görevleri için dosya tanımlayıcılarını kullanacağız.
Ara belleğe alma(Buffering)
- un-buffered(ara belleğe alınmamış):çıktı hemen görünür
stderr arabelleğe alınmaz - line buffered(satır ara belleğe alınmış): tam bir satır yazıldığı zaman çıktı görüntülenir.
stdout, ekrana giderken arabelleğe alınan satırdır - block buffered(blok ara belleğe alınmış): bir tampon doldurulduğunda veya bir tampon temizlendiğinde çıktı görünür.
normalde bir dosyaya çıkış blok arabelleğe alınır.
stdout bir dosyaya yönlendirildiğinde arabelleğe alınmış bloktur.
Dosya tanımlayıcıları
Düşük seviye I / O tarafından kullanılır:
- open(), close(), read(), write()
Bir tam sayı gibi tanımlanır.
- int fd;
Bir FILE nesnesini fd’ye dönüştürmek için kullanışlı bir sistem çağrısı.
- int fileno( FILE *fp);
Elbette bir dosya tanıtıcısına bir akış arayüzü atamak mümkündür.
- FILE *fdopen(int fd, const char *mode);
Süreç Yönetimi Sorunları
Sistem, aşağıdakilerle ilgilenmektedir: – Bir süreç oluşturmak – Programın ayarlanması bir süreç yürütür – Bir i sonlandırılması bekleniyor
- Bir süreçin sonlandırılması. – Bir süreçe sinyal gönderme.
- süreçler arasında iletişim kurmak
Unix’i başlatma

Hangi süreçlerin çalıştını görmek için: “top” , “ps -aux” komutlarını çalıştırın.
Yeni bir işlem oluşturmanın tek yolu mevcut bir işlemi kopyalamaktır. Bu nedenle tüm işlemlerin atası pid = 1 ile başlatılır.
Csh komutları nasıl çalıştırır?

- Bir komut yazıldığında, csh fork olur(yeni süreç doğurma) ve sonra yazılan komutu yürütür.
- fork’dan(çatallanma) sonra, dosya tanımlayıcıları 0, 1 ve 2, yeni süreçde hala stdin, stdout ve stderr’ye başvurur.
- Kurallara göre, yürütülen program bu tanımlayıcıları uygun şekilde kullanacaktır.

Süreç oluşturma
- fork sistemi çağrısı, çalışmakta olan programın bir kopyasını oluşturur.
- Kopya (child süreç) ve orijinal (parent süreç) her ikisi de fork noktasından aynı verilerle devam eder.
- Tek fark, fork çağrısından dönüş(return) değeridir.
Fork : PID’ler ve PPID’ler
- Sistem çağrısı: int fork() -fork() başarılı olursa, çocuk(child) PID’yi ebeveynine(parent) ve 0 değerini çocuğa döndürür;
- fork() başarısız olursa, parent’a -1 döndürür (alt öğe oluşturulmaz)
- İlgili sistem çağrıları:
int getpid() – geçerli süreçin PID değerini döndürür
int getppid() – parent süreçinin PID değerini döndürür (1’in ppid değeri 1’dir)
int getpgrp() – geçerli sürecin grup ID’sini döndürür
fork() başarısız olduğunda
İki olası sebep:
Bir kullanıcının oluşturabileceği maksimum işlem sayısının bir sınırı vardır.Bu sınıra ulaşıldığında (yani, işlem tablosu dolu), daha sonraki fork() çağrıları -1 değerini döndürür.
Çekirdek işlemlere sanal bellek ayırır Belleği yetersiz kaldığında, fork çağrıları başarısız olur.
fork () özellikleri
Çocuk tarafından ebeveynden miras alınan özellikler:
- UID, GID
- kontrol terminali
- CWD, kök dizini
- sinyal maskesi, çevre, kaynak sınırları
- paylaşılan bellek segmentleri
Ebeveyn ve çocuk arasındaki farklar:
- PID, PPID, fork()’dan dönüş değeri
- Bekleyen alarmlar çocuk için silindi.
- Çocuk için bekleyen sinyaller silinir.
fork örneği:
int i, pid;
i = 5;
printf(“%d\n”, i);
pid = fork();
if(pid != 0)
i = 6; /* sadece parent buraya gelir /
else
i = 4; / sadece child buraya gelir */
printf(“%d\n”, i);

PID/PPID örneği:
#include <stdio.h>
#include <unistd.h>
int main(void) {
int pid;
printf("ORIG: PID=%d PPID=%d\n",getpid(), getppid());
pid = fork();
if(pid != 0)
printf("PARENT: PID=%d PPID=%d\n",getpid(), getppid());
else
printf("CHILD: PID=%d PPID=%d\n",getpid(), getppid());
return(1);
}
çıktı:
ORIG: PID=27989 PPID=27167
PARENT: PID=27989 PPID=27167
CHILD: PID=27990 PPID=27989
Program Yürütme (Executing a Program)
- Exec sistem çağrısı, bir süreç tarafından yürütülen programın yerine farklı bir program koyar.
- Yeni program baştan yürütmeye başlar. Başarı durumunda, exec asla geri değer döndürmez, başarısızlık durumunda exec -1 döndürür.

Örnek:
Program X:
int i = 5;
printf("%d\n", i);
exec(“Y”);
printf("%d\n", i);
Program Y:
printf("hello\n");
exec() özellikleri
Yeni süreç çağrılan sürecinden aşağıdakileri miras alır:
- PID ve PPID, gerçek UID, GID, oturum ID.
- kontrol terminali.
- CWD, kök dizini, kaynak limitleri.
- süreç sinyali maskesi.
- Bekleyen sinyaller
- Bekleyen alarmlar
- dosya modu oluşturma maskesi (umask).
- dosya kilitleri.
altı versiyon exec():
- execl(char *path, char *arg0, …, (char *)0);
- execv(char *path, char *argv[]);
- execle(char *path, char *arg0, …, (char *)0,char *envp[]);
- execve(char *pathname, char *argv[], char *envp[]);
- execlp(char *file, char *arg0, …, (char *)0);
- execvp(char *file, char *argv[]);