16 Mart 2014 Pazar

Linux Sistem Programlama - Bölüm 3

chown ve fchown Fonksiyonu

#include <unistd.h>
int chown(const char *path, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);
int lchown(const char *path, uid_t owner, gid_t group);

Chown fonksiyonu bir dosyanin sahiplik bilgilerini degistirir. Fonksiyonun prototipi yukaridaki gibidir.
Fonksiyonunlarin birinci parametresi ilfili dosyanin yol ifadesi yada dosya betimleyicisidir. Ikinci parametresi dosyanin yeni sahibinin id'sini, ucuncu parametre de dosyanin yeni grubunun id'sini alir. Ancak root processi bis dosyanin sahiplik bilgisini degistirebilir. Dosyanin sahibinin sahiplik bilgisini yada grup bilgisini degistirebilmesi bazi unix turevi sistemlede mumkundur. Bazilarinda ise degildir. POSIX bunu isletim sisteminin istegine birakmistir. Linux'da default durumda dosyanin sahibi sahipligini baska birisine devredemez. Fakat genel olarak dosyanin sahibi dosyanin grubunu degistirmektedir. Ancak dosyanin sahibi dosyanin grubunu POSIX standartlarina gore herhangi bir grup olarak degistiremez. Kendi etkin grupid'si olarak yada ek gruplarindan biri olarak degistirebilir.

POSIX Standartlarinda degisik baslik dosyalarinda xxx_t biciminde typedef edilmis bazi tur isimleri vardir. BU turlerin gercekte hangi tur olarak typedef edilecegibazi kosullar altinda isletim sistemini yazanlara birakilmistir. Bunlarin hepsi ayrica bir grup olarak /sys/types.h dosyasi icinde tanimlanmistir. Tipik xxx_t turleri sunlardir:
-> mode_tnlink_tuid_tgid_tid_t bir tamsayi olarak typedef edilmek zorundadir.
-> blksize_tpid_tssize_tblkcnt_t ve off_t isaretli tamsayi olacak bicimde typedef edilmek zorundadir.
-> size_t isaretsiz bir tamsayi turu olarak typedef edilmek zorundadir.
-> time_t ve clock_t tam sayi yada gercek sayio turunden typedef edilmek zorundadir.

stat ve fstat fonksiyonlari

Fonksiyonun prototipi su sekildedir.

#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);

Fonksiyonlarin birinci parametreleri bilgisi elde edilecek olan dosyani yol  ifadesini yada betimleyicisini almaktadir. ikinci parametre sys/types.h dosyasinda bildirilmis olan stat isimli bir yapi turunden nesnenin adresini alir. Fonksiyon basari durumunda 0 basarisizlik durumunda -1 degerine geri doner. 
Stat yapisinin icerisinde su bilgiler vardir. 
  • Dosyanin icinde bulundugu aygitin aygit numarasi.
  • Dosyanin inode numarasi
  • Dosyanin erisim bilgileri 
  • Dosyanin userid ve groupid bilgileri 
  • Dosyanin uzunlugu
  • Dosyani son erisim, son degistirilme, son statu degisimine iliskin tarih ve zaman bilgisi
  • Kopyalama gibi islemlerdeki etkin tampon buyuklugu 
  • Bu dosya icin kac disk blogu tahsis edildigi

Bir dosyanin kac bloktan olustugu bilgisi belirtilirken bir blogun kac byte uzunlugunda oldugu sistemden sisteme degisebilmektedir. Block kavrami Linux sistemlerde biraz lastik hale getirilmistir. Stat fonksiyonunda ise block 512 byei temsil eder,. Halbulki baska yarlerde de bloack terimi kullanilmaktadir ve baska uzunlugu gostermektedir. Sonuc olarak linux sistemiinde block terinide karsilasildiginda, o baglamda blogun kac byte dan olustugu bilgisini de edinmek gerekmektedir. 

/*-----------------------------------------------------------------------------------
 stat fonksiyonu
------------------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>

void exitsys(const char *msg);

int main(int argc, char *argv[])
{
 struct stat finfo;
 struct passwd *pwd;
 struct group *grp;
 struct tm *ftime;

 if (argc != 2) {
  fprintf(stderr, "cannot get stat info\n");
  exit(EXIT_FAILURE);
 }

 if (stat(argv[1], &finfo) < 0)
  exitsys("stat");

 if ((pwd = getpwuid(finfo.st_uid)) == NULL)
  exitsys("getpwuid");
 if ((grp = getgrgid(finfo.st_gid)) == NULL)
  exitsys("getgrgid");

 printf("%s\n", argv[1]);
 printf("User: %s(%lu)\n", pwd->pw_name, (unsigned long) finfo.st_uid);
 printf("Group: %s(%lu)\n", grp->gr_name, (unsigned long) finfo.st_gid);
 printf("Length: %lu\n", (unsigned long) finfo.st_size);
  
 ftime = localtime(&finfo.st_atime);
 printf("Access time: %02d/%02d/%04d %02d:%02d:%02d\n", ftime->tm_mday,
         ftime->tm_mon + 1, ftime->tm_year + 1900,ftime->tm_hour,
         ftime->tm_min, ftime->tm_sec);

 ftime = localtime(&finfo.st_mtime);
 printf("Modification time: %02d/%02d/%04d %02d:%02d:%02d\n", ftime->tm_mday,
         ftime->tm_mon + 1, ftime->tm_year + 1900,
         ftime->tm_hour, ftime->tm_min, ftime->tm_sec);

 ftime = localtime(&finfo.st_ctime);
 printf("Status time: %02d/%02d/%04d %02d:%02d:%02d\n", ftime->tm_mday,
         ftime->tm_mon + 1, ftime->tm_year + 1900,
         ftime->tm_hour, ftime->tm_min, ftime->tm_sec);

 return 0;
}

void exitsys(const char *msg)
{
 perror(msg);
 exit(EXIT_FAILURE);
}

/etc/passwd ve /etc/group dosyalarinin formatlari sistemden sisteme farkli olacilir/ Bilindigi gibi sistemler icsel olarak hep sayilarla calisirlar. Ornegin stat fonksiyonundan elde etmis oldugumuz userid ve groupid degerleri sayisal degerlerdir. Bunlari ancak biz /etc/passwd ve /etc/group dosyalarina bakarak isme donusturebiliriz. Iste bu isi yapan birkac posix fonksiyonu bulundurulmustur.
getpwuid ve getgrgid fonksiyonlari parametre olarak bizden userid ve groupid degerini alir. ve bize passwd ve group isimli yapi adresi ile geri doner. Bu yapilar, /tec/passwd ve /etc/group dosyalarindaki satirlari temsil etmektedir. tipik olarak bu fonksiyonlar sayisal userid ve gruopid degerlerini user ve group isimlerine donusturmek icin kullanilir. Bu islemin tersini yapan yani isimlerden sayi elde edilmesini saglayan getpwnam ve getgrnam fonksiyonlari vardi

geri donus degeri pointer olan fonksiyonlarda hata durrumunda geri donus -1 degil NULL pointerdir. Fonksiyon geri donusunu kontrol etmek icin NULL olup olmadigina bakmamiz omenlidir.

Stat fonksiyonu ile verilen tarihler bize 1.1.1970 tarihinden gecen saniye sayisi olarak verilmektedir. Stat yapisinin icerisinde erisim haklari st_mode icerisine bit bit kodlanmistir. Buradan dosyanin turunu almak icin s_isxxx biciminde makrolar bulundurulmustur. ayrica ilgili hakkin olup olmadigini anlamak icin daha once gorulmus olan S_IXXXX sembolik sabitleri ile bit end islem olusturmak gerekir.

/*------------------------------------------------------------------------------------
 dosya bilgilerinin ls -l tarzýnda elde edilmesi
-------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>

void exitsys(const char *msg);
char *getls(const char *path, const struct stat *finfo);

int main(int argc, char *argv[])
{
 struct stat finfo;

 if (argc != 2) {
  fprintf(stderr, "cannot get stat info\n");
  exit(EXIT_FAILURE);
 }

 if (stat(argv[1], &finfo) < 0)
  exitsys("stat");

 puts(getls(argv[1], &finfo));

 return 0;
}

char *getls(const char *path, const struct stat *finfo)
{
 char static buf[4096];
 int index;
 int i;
 int aflags[] = {S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP,
                     S_IROTH, S_IWOTH, S_IXOTH};
 char *rights = "rwx";
 struct passwd *pwd;
 struct group *grp;
 char *months[] = {"Oca", "Þub", "Mar", "Nis", "May", "Haz", "Tem", "Aðu", "Eyl", "Eki",
                     "Kas", "Ara"};
 struct tm *ftime;

 index = 0;
 if (S_ISREG(finfo->st_mode))
  buf[index] = '-';
 else if (S_ISDIR(finfo->st_mode))
  buf[index] = 'd';
 else if (S_ISCHR(finfo->st_mode))
  buf[index] = 'c';
 else if (S_ISBLK(finfo->st_mode))
  buf[index] = 'b';
 else if (S_ISFIFO(finfo->st_mode))
  buf[index] = 'p';
 else if (S_ISLNK(finfo->st_mode))
  buf[index] = 'l';
 else if (S_ISSOCK(finfo->st_mode))
  buf[index] = 's';

 ++index;

 for (i = 0; i < 9; ++i)
  buf[index++] = (finfo->st_mode & aflags[i]) ? rights[i % 3] : '-';
 buf[index++] = ' ';

 index += sprintf(&buf[index], "%lu", (unsigned long) finfo->st_nlink);
 buf[index++] = ' ';

 if ((pwd = getpwuid(finfo->st_uid)) == NULL)
  index += sprintf(&buf[index], "%lu", (unsigned long) finfo->st_uid);
 else
  index += sprintf(&buf[index], "%s", pwd->pw_name);
 buf[index++] = ' ';

 if ((grp = getgrgid(finfo->st_gid)) == NULL)
  index += sprintf(&buf[index], "%lu", (unsigned long) finfo->st_gid);
 else
  index += sprintf(&buf[index], "%s", grp->gr_name);
 buf[index++] = ' ';

 index += sprintf(&buf[index], "%ld", (long) finfo->st_size);
 buf[index++] = ' ';

 ftime = localtime(&finfo->st_mtime);
 index += sprintf(&buf[index], "%s %d %02d:%02d %s", months[ftime->tm_mon],
             ftime->tm_mday,ftime->tm_hour, ftime->tm_min, path);

 buf[index] = '\0';

 return buf;
}

void exitsys(const char *msg)
{
 perror(msg);
 exit(EXIT_FAILURE);
}

Dizin icindeki dosyalarin ele gecirilmesi
Bir dizin icerisindeki dosyalarin elde edilmesi su sekilde yapilir. 
Dizin opendir fonksiyonu ile acilir. Fonksiyon basari durumunda DIR isimli bir yapi turunden handle gorevi yapa bir adres verir.basarisizlik durumunde NULL adres verir.

#include <dirent.h>
DIR *opendir(const char *name);
DIR *fdopendir(int fd);

Readdir isimli fonksiyon her cagirildiginda bize siradaki dosyanin isminin ve inode numarasinin bulundugu dirent isimli bir yapinn adresini verir. Dizinin sonuna gelindiginde fonksiyon NULL adres verir.

#include <dirent.h>
struct dirent *readdir(DIR *dirp);

Nihayet acilmis olan dizin closedir fonksiyonu ile kapatilir.

#include <dirent.h>
int closedir(DIR *dirp);

bir onceki konuda yapilan ls komutu ornegini dizindeki dosyalar icin yapilandiralim

/*---------------------------------------------------------------------------------
 Dizin içerisindeki dosyalarýn elde edilmesi
----------------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <dirent.h>

void exitsys(const char *msg);
char *getls(const char *path);

int main(int argc, char *argv[])
{
 struct stat finfo;
 DIR *dir;
 struct dirent *dire;
 char path[1024];

 if (argc != 2) {
  fprintf(stderr, "wrong number of arguments\n");
  exit(EXIT_FAILURE);
 }

 if ((dir = opendir(argv[1])) == NULL)
  exitsys("opendir");

 while ((dire = readdir(dir)) != NULL) {
  sprintf(path, "%s/%s", argv[1], dire->d_name);
  printf("%s\n", getls(path));
 }
  
 closedir(dir);

 return 0;
}

char *getls(const char *path)
{
 char static buf[4096];
 struct stat finfo;
 int index;
 int i;
 int aflags[] = {S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP,
                     S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH};
 char *rights = "rwx";
 struct passwd *pwd;
 struct group *grp;
 char *months[] = {"Oca", "Þub", "Mar", "Nis", "May", "Haz",
                     "Tem", "Aðu", "Eyl", "Eki", "Kas", "Ara"};
 struct tm *ftime;
 char *fname;

 if (stat(path, &finfo) < 0)
  return NULL;
  
 if ((fname = strrchr(path, '/')) == NULL)
  return NULL;

 index = 0;
 if (S_ISREG(finfo.st_mode))
  buf[index] = '-';
 else if (S_ISDIR(finfo.st_mode))
  buf[index] = 'd';
 else if (S_ISCHR(finfo.st_mode))
  buf[index] = 'c';
 else if (S_ISBLK(finfo.st_mode))
  buf[index] = 'b';
 else if (S_ISFIFO(finfo.st_mode))
  buf[index] = 'p';
 else if (S_ISLNK(finfo.st_mode))
  buf[index] = 'l';
 else if (S_ISSOCK(finfo.st_mode))
  buf[index] = 's';

 ++index;

 for (i = 0; i < 9; ++i)
  buf[index++] = (finfo.st_mode & aflags[i]) ? rights[i % 3] : '-';
 buf[index++] = ' ';

 index += sprintf(&buf[index], "%lu", (unsigned long) finfo.st_nlink);
 buf[index++] = ' ';

 if ((pwd = getpwuid(finfo.st_uid)) == NULL)
  index += sprintf(&buf[index], "%lu", (unsigned long) finfo.st_uid);
 else
  index += sprintf(&buf[index], "%s", pwd->pw_name);
 buf[index++] = ' ';

 if ((grp = getgrgid(finfo.st_gid)) == NULL)
  index += sprintf(&buf[index], "%lu", (unsigned long) finfo.st_gid);
 else
  index += sprintf(&buf[index], "%s", grp->gr_name);
 buf[index++] = ' ';

 index += sprintf(&buf[index], "%ld", (long) finfo.st_size);
 buf[index++] = ' ';

 ftime = localtime(&finfo.st_mtime);
 index += sprintf(&buf[index], "%s %d %02d:%02d %s", months[ftime->tm_mon],
                     ftime->tm_mday,
      ftime->tm_hour, ftime->tm_min, fname + 1);

 buf[index] = '\0';

 return buf;
}

void exitsys(const char *msg)
{
 perror(msg);
 exit(EXIT_FAILURE);
}

Recursive olarak bir dizin icindeki bilgilerin elde edilmesi

Dizin agacini dolasabilmek icin ozyinelemeli (recursive) bir dolasim gerekmektedir. Tipik islem su sekilde yapilir.Kok dizinden dosyalar bulunarak ilerlenir. Bulunan dosyanin normal bir dosya mi yoksa bir dizin dosyasi mi olduguna bakilir.  Eger Bir dosya dizin dosyasi ise chdir fonksiyonu ile prosesin calisma dizini degistirilip fonksiyonun kendisini cagirmasi saglanir. Dizin bittiginde yine chdir fonksiyonu ile ust dizine gecilir. Her alt dizin icerisinde nokta (.) ve nokta nokta (..) isimli iki dizin otomatik olarak olusturulur. bunlarin dikkate alinmamasi gerekir.

C Programlama icerisinde yer alan recursive algoritmalar konusuna goz alilmasi gerekmektedir.
sdasd

/*-------------------------------------------------------------------------------------
                Dizin agacinin dolasilmasi
-------------------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>

void exitsys(const char *msg);
void walktree(const char *path);

int main(int argc, char *argv[])
{
                if (argc != 2) {
                               fprintf(stderr, "wrong number of arguments\n");
                               exit(EXIT_FAILURE);
                }
               
                walktree(argv[1]);
               
                return 0;
}

void walktree(const char *dirpath)
{
                struct stat finfo;
                DIR *dir;
                struct dirent *dire;
                char filepath[1024];
                              
                if ((dir = opendir(dirpath)) == NULL) {
                               perror("opendir");
                               return;
                }
               
                while ((dire = readdir(dir)) != NULL) {
                               if (!strcmp(dire->d_name, ".") || !strcmp(dire->d_name, ".."))
                                               continue;
                               sprintf(filepath, "%s/%s", dirpath, dire->d_name);
                               if (lstat(filepath, &finfo) < 0) {
                                               perror("stat");
                                               continue;
                               }
                               printf("%s\n", filepath);
                              
                               if (S_ISDIR(finfo.st_mode))
                                               walktree(filepath);
                }
                                              
                closedir(dir);
}

void exitsys(const char *msg)
{
                perror(msg);
                exit(EXIT_FAILURE);
}

Dosya Baglari (linkleri)

Dosya baglari siki (hard) ve sembolik olmak uzere ikiye ayrilmaktadir. Hard link sisteminin anlasilabilmesi icin diskin metadata formatinin biliniyor olmasi gerekmektedir.
Bir diskte okunabilecek veya yazilabilecek en kucu birim sektordur. Bir sektor varsayilan olarak 512 byte uzunlugundadir. Ancak isletim sistemleri diski sektpor temelinde organize etmek yerine block yada cluster denilen birimler ile organize etmektedir. Bir block yada Cluster ardisil n sektorden olusmaktadir. Bu sektorun sayisi formatlarken belirlenir. Bir block yada cluster bis dosyanin parcasi olabilen en kucuk birimdir(ornegin 1 byte uzunlugunda bir dosya olusturacak olsak sistemda bu dosya icin 1 block yer ayrilir). Bir volumu ext2, ext3 gibi inode tabanli bir dosya sistemi ile formatlarsak kabaca diskte 3 bolum olusturulur. Bunlar super blockinode blockdata block bolumleridir.
Super Blok, bolumlerin yerlerini uzunluklarini vesaire tutan bir blogun kaz sektorden olustugunu tutan bolumdur. Volume'nin kalbidir. 
Inode block, inode elemanlarindan olusur. Her inode elemanina 0'dan baslayarak bir sira  numarasi karsilik dusurulmustur. Bir dosyanin butun bigileri, yani stat fonksiyonu ile aldigimiz bilgiler inode elemaninda tutulur. Bir dosya yaratildiginda onun icin bir inode elemani olusturulur. Yani her dosyanin bir inode elemani vardir. Dizinler de birer dosya gibidir. Onlar icin de birer inode elemani vardir. BIr dosyanin inode numarasi demek o dosyanin bilgilerinin inode blogunun kacinci inode elemaninda saklandigi demekdir. dosyanin inode numarasi ls -i ile goruntulenmektedi
0 Numarali inode elemani kok dizinin elemanidir. Dizinler de dosyalar gibi bir dizin dosyasinin icerisinde dosya ismi ve inode numaralarindan olusan kayitlar vardir. 

 isim 
 inode no
 isim 
 inode no
 isim
 inode no
 ... 
 ...

Diskin data bolumu ilk blok sifirdan baslayacak sekilde bloklarla numaralandirilmistir. Her dosya data bolumu icerisinde bloklara parcalanmis bir bicimde oradaki bloklarda tutulmustur. Bu bloklar ardisil olmak zorunda degildir.  Dosyanin parcalarinin hangi bloklarda oldugu inode elemaninda tutulmaktadir. 
isletim sisteminin dosya sistemi yol ifadelerinden oncelikle bir bir inode elemani elde etmelidir. Cunku dosyani butun kontrol bilgisi inode elemanindadir. Genel olarak bir yol ifadesinden o dosyaya iliskin inode elemaninin elde edilmesi surecine "Pathname resolution" denilmektedir. Pathname resolution su sekilde gerceklestirilir. Ornegin /home/csd/test.dat dosyasina erisilmek istensin;
  1. Sistem 0 numarali inode elemanina gider. Onun bloklarini okur ve orada inode kayitlari vardir. Orada home isminii arar. Bulursa onun inode numarasini elde eder,
  2. Eger bulunan home ismi bir dizin dosyasina iliskinse ve erisim hakki varsa onun inode elemanina gide ve onun da bloklarini okur. Orada csd ismini arar ve onun inode numarasini elde eder.
  3. Artik csd'nin inode elemaninda onun bloklarina bakilarak bu kez orada test.dat aranir. ve nihayet dosyanin bilgisine erisir.

Hardlink nedir?

Hardlink farkli dizin girislerinin ayni inode numarasina sahip olmasidir. Boylece iki farkli yol ifadesiyle pathname resolution yapildiginda aslinda ayni dosyaya erisilmis olur.
Ornegin


Goruldugu gibi /home/kaan/b.dat dizin girisi ile /home/ali/y.dat dizin girisi ayni inode elemanini gostermektedir.

Peki rm gibi bir komutla bu dosyalardan biri silinse ne olur?

Eger dosyanin inode elemani ve inode bloklar free hale getirilirse diger yol ifadesiyle ardik diger dosyaya erisemiyoruz.iste bunun icin inode elemaninda isletimsistemi hardlink sayisini tutar. Dosya silinmeye calisildiginda once dizin girisini siler. Sonra dosyanin inode elemanina giderek oradaki hardlink sayacini 1 eksiltir. Eger sayac sifira duserse dosyanin inode numarasini ve o inoda bagli data bloklarini siler(metadata alanlari silinmis gibi gosterilir).
Inode tabanli dosya sistemlerinde ayrica hangi inode elemanlarinin  bos oldugu ve hangi data bloklarinin bos oldugu disk uzerinde bir yerlerde tutulmalidir. Suphesiz her blok yada inode elemani icin 1 bit yeterlidir. Ornegin ext2 dosya sisteminde bu bilgiler hemen superblok'tan asonra block bitmap ve inode bitmap isimleri ile tutulurlar.


9 Mart 2014 Pazar

Linux Sistem Programlama - Bölüm 2

Dosya erişim kontrolü şöyle yapılır. 
  1. Önce Open fonksiyonunu çağıran prosesin root olup olmadığına bakılır. root ise hiçbir kontrol yapılmaz. Erişim kabul edilir.
  2. Değil ise ikinci erişim istenen processin etkin UserID'sinin dosyanın UserID'si ile ayni olup olmadığına bakılır. Eğer aynı ise erişim haklarından owner kısmı dikkate alınır. Değil ise 3. maddeye geçilir.
  3. Erişmek isteyen prosesin etkin grupid'sinin dosyanın grupid'si ile aynı olup olmadığına bakılır. Aynı ise grup haklar dikkate alınır. Değil ise 4. maddeye geçilir.
  4. Bu durumda dosyanın other bilgileri dikkate alınır.
Belirli bir yıldan sonra bir prosesin tek bir gruba ait olması yetersiz görülmüştür. Çünkü bir kişi birden fazla projede çalışabilmektedir. işte bu durumda  ek grupid kavramı ortaya çıkmıştır. Bugünkü sistemde bir prosesin bir tane gerçek grupid'si ve etkin grupid'si vardır. Fakat birden fazla ek gruba da sahiptir. 
Ek gruplar tamamen yukarıdaki 3. madde sırasında prosesin etkin grupid ile dosyanın grupid'si karşılaştırılırken etkin grupid ile ayni değerde karşılaştırılmaktadır. Kullanıcının ek gruplari /etc/group dosyasında belirtilmektedir.

POSIX Programlamada Kullanılan Önemli Bazı Bilgiler

POSIX fonksiyonları standart C fonksiyonları ile birlikte glibc/libc diye bilinen kütüphanenin içerisindedir ve GCC sistemi bu kütüphaneye bakmaktadır. Fakat fonksiyonların prototipleri çeşitli baslık dosyası içinde olabilir. Bunların ayrıca include edilmedi gerekmektedir.
POSIX Fonksiyonlarının çok büyük bolumu basari durumunda 0 başarısızlık durumunda -1 değerine geri dönerler. Programcı programın başarısızlığını test etmelidir. Tabi başarısızlığın da bir sebebi vardır. iste başarısızlığın nedeni int türden global errno isimli bir değişkene yerleştirilmiştir. Fonksiyon basarisiz olduğunda bu değişkenin içine bakarsak neden basarisiz olduğunu anlayabiliriz. Tüm başarısızlık değerleri errno.h dosyası içinde EXXX bicimde sembolik sabitlerle define edilmiştir. Programı isterse doğrudan bu sembolik sabitleri kullanabilir.

if( open(.........) ){
    if(errno == ENOENT)
    {
         ......
    }
}

POSIX Standartlarında her posix fonksiyonu için başarısızlık durumunda her fonksiyon için errno değişkeninin alabileceği tüm değerler listelenmiştir.
Fonksiyon basarisiz olduğunda uygun mesajın verilmesi zahmetli olabilir. Bunun için perror ve strerror fonksiyonları bulunmaktadır. perror bizden bir yazı alır önce o yazıyı yazdırır. Sonra bir iki nokta üst üste karakteri yazdırır sonra da o anki errno değerine bakarak ona karsı gelen yazıyı yazdırır (stderr dosyasina). stderror fonksiyonu ise bizden bir hata kodunu alarak. Ona karşı gelen yazıyı verir. Bu durumda tipik kontrol su şekilde yapılabilir.

if(open(...) == -1){
    perror("open");
    exit(EXIT_FAILURE);
}

Dosya İşlemleri Yapan Temel POSIX Fonksiyonları

Open Fonksiyonu

#include <fcntl.h>
int open(const char *path, int oflags, ...);

Fonksiyonun birinci parametresi açılacak olan dosyanın yolunu alır. İkinci parametresi açış modunu belirtir. Açış modu fcntl.h içerisinde tanımlanmış olan O_XXXX biçimindeki sabitlerin bit OR işlemi ile birleştirilmesi ile olur. Açış modu şunlardan yalnızca birini içermek zorundadır.
  • O_RDONLY
  • O_WRONLY
  • O_RDWR
O_CREAT, dosya yok ise yarat, var ise bir şey yapma anlamına gelmektedir. O_TRUNCO_RDONLY ya da O_RDWR ile birlikte kullanılmak zorundadır. Bu durumda dosya varsa sıfırlanır ve açılır. O_EXCLO_CREAT ile birlikte kullanılmak zorundadır. Bu birlikte kullanım sadece yeni bir dosya yaratmak ve açmak için kullanılır. Dosya var ise open fonksiyonu başarısız olur. O_APPEND, Tüm yapılan yazma işlemlerinin dosyanın sonuna eklenmesi gerektiği anlamına gelmektedir. Bu durumda dosyanın herhangi bir yerinden okuma yapılabilir. Fakat herhangi bir yerine yazılamaz. Her yazılan sonuna eklenir.
Open fonksiyonunda  ikici parametrede O_CREAT kullanılmışsa bu durumda dosyanın yaratılması gibi bir durum oluşur. Artık programcının da dosyanın erişim haklarını üçüncü parametresi belirtmesi gerekir.
Fonksiyonun 3. parametresinde belirtilecek erişim hakları sys/stat.h dosyasında belirtilen sembolik sabitlerin bit OR işlemi ile birleştirilmesi ile oluşturulur. Bu sembolik sabitlerin isimleri su şekilde oluşturulmuştur.

   

R
USR
S_I
W
GRP

X
OTH

Bu gösterimin kombinasyonları olacak şekilde olur. Örnegin;

S_IRUSR -> User'a read hakki verir
S_IXGRP -> Gruba calistirma haki verir

Örnek bir uygulama:
/*-----------------------------------------------------------------------------------
 open fonksiyonunun kullanýmý
------------------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>

int main(void)
{
 int fd;

 if ((fd = open("a.dat", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
  perror("open");
  exit(EXIT_FAILURE);
 }

 printf("success...\n");

 return 0;
}

Open fonksiyonu basari durumunda dosya betimleyicisi (file descriptor) denilen bir handle değerine geri döner. Bu handle değeri diğer fonksiyonlarda parametre olarak kullanılacaktır. Başarısızlık durumunda fonksiyon -1 değerine geri döner.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcnlt.h>
#include <sys/stat.h>

int main(void)
{
    int fd;
   
    if((fd = open("a.dat", O_RDWR)) == -1){
        fprintf(stderr, "Error ")       
    }
    return 0;
}

Close Fonksiyonu

#include <unistd.h>
int close(int fd);
Açılan her dosya bir biçim de kapatılmalıdır. Çünkü açım sırasında bir takım kaynaklar tahsis edilmiş olur. Kapatma sırasında bu kaynaklar geri bırakılır. Ancak dosyayı biz hiç kapatmasak bile process kapandığında bile dosya kesinlikle kapatılmalıdır. Fakat artık kullanılmayacak olan bir dosyanın kapatılması iyi bir tekniktir.

posix fonksiyonlarının bir çoğu unistd.h dosyası içerisinde tanımlanmıştır.

Fonksiyon parametre olarak open fonksiyonu ile dosyanın betimleyicisini alır. Başarılı ise 0 değil ise -1 ile geri dönüş yapar.

/*-------------------------------------------------------------------------------------
 close fonksiyonu
-------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

int main(void)
{
 int fd;

 if ((fd = open("a.dat", O_RDWR)) == -1) {
  perror("open");
  exit(EXIT_FAILURE);
 }

 close(fd);

 return 0;
}

Dosya Göstericisi Kavramı

Dosyadaki her bir byte'in bir ofset numarası vardır. Dosya göstericisi imleç görevindedir. Bir ofset belirtir. Okuma ve yazma işleminin dosyanın hangi noktasından itibaren yapılacağını belirtir.  Dosya açıldığında dosya göstericisi 0. ofsettedir. Okunan ya da yazılan miktar kadar otomatik ilerletilir. Dosya göstericisinin dosyanın son byte’indan sonraki byte’ı gösterme durumuna EOF durumu denir. Bu durumda okuma yapılamaz ancak yazma yapılabilir. Bu dosyaya ekleme anlamına gelir.

Read ve Write Fonksiyonları

Read fonksiyonunun prototipi şu şekildedir

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

Fonksiyonun birinci parametresi dosya betimleyicisini, ikinci parametresi okunan bilgilerin yerleştirileceği adresi, üçüncü parametresi ise okunacak byte sayısını belirtir. Read fonksiyonu ile olandan daha fazla okunmak istenebilir bu durumda fonksiyon okuyabildiği kadar byte'i okur okuyabildiği byte sayısına geri döner. read IO hatası dolaysı ile hiç okuma yapamaz ise -1 değerine EOF'dan dolayı hiçbir şey okuyamaz ise 0 değerine geri döner.

/*-------------------------------------------------------------------------------------
 read fonksiyonunun kullanim ornegi
--------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

int main(void)
{
 int fd;
 char buf[30 + 1];
 ssize_t n;

 if ((fd = open("sample.c", O_RDONLY)) == -1) {
  perror("open");
  exit(EXIT_FAILURE);
 }

 if ((n = read(fd, buf, 30)) == -1) {
  perror("read");
  exit(EXIT_FAILURE);
 }

 buf[n] = '\0';
 puts(buf);

 close(fd);

 return 0;
}

Write fonksiyonunun prototipi şu şekildedir.

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

Fonksiyonun birinci parametresi dosya betimleyicisini, ikinci parametresi dosyaya yazılacak bilgilerin bulunduğu bellek adresini, üçüncü parametresi ise yazılacak byte miktarını belirtir. Fonksiyon başarılı ise yazılabilen byte sayısına basarisiz ise -1 değerine geri döner.

/*-------------------------------------------------------------------------------------
 Dosya kopyalama
-------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#define BUFSIZE  8192

int main(int argc, char *argv[])
{
 int fds, fdd;
 char buf[BUFSIZE];
 ssize_t n;

 if (argc < 3) {
  fprintf(stderr, "wrong number of argument!..\n");
  fprintf(stderr, "usage: mycp <source fie> <dest file>\n");
  exit(EXIT_FAILURE);
 }

 if ((fds = open(argv[1], O_RDONLY)) < 0) {
  perror("open");
  exit(EXIT_FAILURE);
 }

 if ((fdd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC,
                 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) {
  perror("open");
  exit(EXIT_FAILURE);
 }

 while ((n = read(fds, buf, BUFSIZE)) > 0)
  if (write(fdd, buf, n) < 0) {
   perror("write");
   exit(EXIT_FAILURE);
  }

 if (n < 0) {
  perror("read");
  exit(EXIT_FAILURE);
 }

 printf("1 file copied...\n");

 close(fds);
 close(fdd);

 return 0;
}
Bu örnekte okuma yaptigimiz konksiyonun yetkilerini alip hedef dosyaya eklemek icin stat fonksiyonunu kullanmak gerekiyor. 

lseek fonksiyonu

lseek fonksiyonunun prototipi su sekildedir

#include <sys/types.h> // bu dosya include edilmek zorunda deiliz. Edilirse guzel olur.
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

Fonksiyonun birinci parametresi dosya betimleyicisi ikinci parametresi konumlandırma ofseti, üçüncü parametresi konumlandırma orijinini belirtir.  Üçüncü parametre 3 değerden birini alabilir. 
SEEK_SET (0) konumlandırmanın baştan itibaren yapılacağını belirtir. Bu durumda ofset parametresi >= 0 olmak zorundadır. SEEK_CUR (1) konumlandırmanın dosyanın göstericisinin konumu nerede ise ona göre yapılacağı anlamına gelir. Bu durumda ikinci parametre  pozitif, negatif ya da 0 olabilir. Nihayet SEEK_END (2) konumlandırmanın EOF pozisyonuna göre yapılacağı anlamına gelmektedir. Bu durumda ikinci parametre  =< 0 olmak zorundadır. Fonksiyon basari durumunda dosya göstericisinin yeni konumuna başarısızlık durumunda -1 değerine geri döner.

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>

int main()
{
        int file=0;
        if((file=open("testfile.txt",O_RDONLY)) < -1)
                return 1;

        char buffer[19];
        if(read(file,buffer,19) != 19)  return 1;
        printf("%s\n",buffer);

        if(lseek(file,10,SEEK_SET) < 0) return 1;

        if(read(file,buffer,19) != 19)  return 1;
        printf("%s\n",buffer);

        return 0;
}

Bu örnekde exitsys fonksiyonunu kendi yazdığımız uygulamalarda direk kullanabiliriz.
lseek komutu ile örneğin 5 bayt uzunluğunda bir dosya 15. bayttan itibaren veri yazabiliyoruz. Buna dosya deliği denilmektedir. (File hole) Bu konu ile ilgili http://www.kaanaslan.com/resource/article/display_article.php?id=64 adresinden ayrıntılı bilgi elde edilebilir.

Dosya baytlardan oluşmaktadır. Fakat halk arasında içinde yalnızca yazı olan dosyalara text dosyalar, içinde yazının dışında başka şeyler de olabilen dosyalara binary dosyalar denilmektedir. Ayrıca test dosyalarda her bir karakterin hangi tablo ile kodlandığını bilmek gerekir. Buna karakter kodlaması denilmektedir.

Unix / Linux   |    Windows 
a\nb                |    a\r\nb
LF                   |    CR/LF

Windows da test editörler aşağı satıra geçiş için CR ve LF karakterlerini (0D ve 0A) kullanır. Fakat Unix/Linux’ta yalnızca LF karakteri kullanılmaktadır. Bu da uyumsuzluğa yol açmaktadır. Bu karışıklığı çözmek için ve özellikle text okuma ve yazmaları kolaylaştırmak için C ve bazı başka dillerde dosyayı açarken text mode ve binari mode seklinde yapay seçenekler oluşturulmuştur. Bir dosyayı text modda açtığımız zaman şunlar olur;
  1. Dosyadan 1 bayt okurken eğer dosya göstericisi Windows da CR karakterinin üzerinde ise CR ve LF karakterlerinin ikisini de okur fakat bize fonksiyon LF karakteri okumuş gibi geri döner.
  2. Biz dosyaya text modda açılmışsa \n karakterini yazdırdığımızda aslında Windows da aslında CR/LF biçiminde 2 byte yazılmaktadır.
Biz dosyayı binary modda açtığımız zaman hangi karakteri yazdırırsak yalnızca onu yazar.
Görüldüğü gibi Unix/Linux sistemlerinde dosyanın text mode veya binary modda açılması arastanda hiçbir fark yoktur.

Text Mode / Binary Mode Örnekleri

/*-------------------------------------------------------------------------------------
                C'de text mode / binary mode'
-------------------------------------------------------------------------------------*/

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
                FILE *f;

                if ((f = fopen("a.txt", "w")) == NULL) {
                               fprintf(stderr, "cannot open file!\n");
                               exit(EXIT_FAILURE);
                }

                fprintf(f, "a\nb");                           /* Windows'ta 4 byte UNIX/Linux'ta 3 byte */

                fclose(f);

                return 0;
}

/*-----------------------------------------------------------------------------------
                C'de text mode / binary mode'
------------------------------------------------------------------------------------*/

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
                FILE *f;

                if ((f = fopen("a.txt", "wb")) == NULL) {
                               fprintf(stderr, "cannot open file!\n");
                               exit(EXIT_FAILURE);
                }

                fprintf(f, "a\nb");                           /* Windows'ta 3 byte UNIX/Linux'ta 3 byte */

                fclose(f);

                return 0;
}

Dosya Sistemine İlişkin Yardımcı POSIX Fonksiyonları

chmod ve fchmod fonksiyonları

#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

Yaratılmış bir dosyanın erişim hakları open fonksiyonunda ilk kez belirlenir. Ancak bu fonksiyonlarla daha sonra değiştirilebilir. chmod fonksiyonu yol ifadesinden hareketle fchmod fonksiyonu dosya betimleyicisinden hareketle modu değiştirir.
Bir prosesin bir dosyanın erişim haklarını değiştirebilmesi için ya root olması ya da prosesin etkin userid'sinin dosyanın sahibi ile ayni olması gerekir.

root hakkı, ya hep ya hiç biçiminde bir haktır. Yani root proses her şeyi yapabilir. Diğerleri sadece kendilerine yönelik şeyleri yapabilir. Bu nedenle bazı sistemlerde proses root olmadığı halde bazı işlemleri yapabilecek durum oluşturulmuştur. Prosesin bu yetkilendirme bilgisi ilgili sistemlerde proses kontrol bloğunda saklanır. Fakat her sistem bunu desteklememektedir.
> Ayrıca komut satırında chmod isimli bir komutta vardır.

/*--------------------------------------------------------------------------------------
 chmod örneği
-------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>

void exitsys(const char *msg);

int main(int argc, char *argv[])
{
 int arights;
 int i, k;
 int aflags[] = {S_IXOTH, S_IWOTH, S_IROTH, S_IXGRP, S_IWGRP,
                     S_IRGRP, S_IXUSR, S_IWUSR, S_IRUSR};
 int mflag;

 if (argc < 3) {
  fprintf(stderr, "wrong number of arguments!..\n");
  fprintf(stderr, "usage: mychmod <access rights> <file>\n");
  exit(EXIT_FAILURE);
 }

 sscanf(argv[1], "%o", &arights);

 for (i = 2; i < argc; ++i) {
  mflag = 0;
  for (k = 0; k < 9; ++k)
   if (arights & aflags[k])
    mflag |= aflags[k];
  if (chmod(argv[i], mflag) < 0)
   exitsys("chmod");
 }

 return 0;
}

void exitsys(const char *msg)
{
 perror(msg);
 exit(EXIT_FAILURE);
}

1 Mart 2014 Cumartesi

Linux Sistem Programlama - Bölüm 1

Unix/Linux Sistemlerinin Tarihsel Gelisimi

Unix  1969-1970yıllarında  AT&T  Bell  Lab  tarafından  DEC-PDP  7  makinaları   için üretilmiştir.  Unix  projesinden önce AT&T MIT gibi üniversitelerle işbirliği yaparak MULTICS isimli bir işletim sistemi geliştirme projesinde çalışıyordu. Daha sonra grup çeşitli haklı gerekçelerle bu projeden çekilmiştir. Unix ismi B. Kernighan tarafından Multics sözcüğünden kelime oyunu yaparak uydurulmuştur. ilk Unix sistemi tamamen PDP makinalarının sembolik makine dilinde yazılmştı. Oyıllara kadar tüm işletim sistemleri zaten sembolik makine dilinde yazılıyordu. Unix işletim sistemi geliştirilirken Ken Thompson bir işletim sisteminin ciddi bir sistem olabilmesi için yüksek seviyeli bir derleyiciye sahip olması gerektiğini düşünüyordu.
Thompson, önceleri Fortran üzerinde durduysa da daha sonra BCPL gibi bir sistemin daha uygun olduğuna karar vermiştir. Bundan sonra Thompson BCPL'nin benzeri olan B dilinin tasarımında da çalşmştır. B bir yorumlayıcı sistemdi ve yavaş çalışıyordu. B programlama dili proje grubu tarafından yerel bir biçimde ve bazı sistem programlarında kullanılmştır. Proje grubu K.Thompson, D.Ritche, B.Kernighan gibi önemli kişilerden oluşuyordu. Grubun asıl amaçlarından biri Unix sistemini yüksek seviyeli bir dille yeniden yazmaktı. D.Ritche, B programlama dilini geliştirerek ilk kez 1971 yılında C programlama dilini tanımlamştir. 1973 yılında Unix işletim sistemi DEC-PDP 11 için çok büyük oranda C ile yeniden yazılmştır. Unix yüksek seviyeli bir programlama diliyle yazılmş olan ilk işletim sistemidir. Bu durum Unix işletim sisteminin farklı platfromlarda yazılmasını çok kolaylaştırmştır.  AT&T Unix işletim sisteminin kaynak kodlarını pekçok araştırma grubu ve üniversitelere dağıtmştır. Bu durum C programlama dilinin tanınmasına yol  açarken Unix sistemlerinin taşınabilirliğinin bozulması sonucunu da doğurmuştur. Çünkü kaynak kodları ele geçiren  kurumlar  orijinal Unix  sistemlerinde  değişiklikler  yaparak  farklı  Unix  sistemleri  oluşturmuşlardır.  Bu sistemlerin  en  önemlilerinden  biri  Berkeley  Sistemleridir, bu sistemler BSD (Berkeley software distrubition) olarak bilinmektedir.  BSD'nin FreeBSD, NetBSD ve OpenBSD biçiminde versiyonları vardır. AT&T kendi Unix sistemlerine de numara vermiştir. Örneğin, system III, systemV gibi...
Bugün Unix sistemlerinin en temel biçimi SVR4 (system 5 release 4 ) sürümüdür. Bundan sonra AT&T'nin çeşitli Unix sistemleri çıktıysa da, SVR4 bir standart anlatan özellik olmuştur.
80'li yıllarda pekçok özel firmada kendi Unix sistemlerini yazmaya başlamştır. Örneğin; Sun firmasının Solaris, HP firmasının HP-UX, SCO firmasının SCO_UNIX, IBM firmasının AIX örnek olarak verilebilir. 
AT&T  80  liyıllarının  başında  Unix  sistemlerine  telif  uygulamaya  başlamştır. Bu durumdan tedirgin olan üniversiteler ve araştırıcılar bedava Unix sistemleri geliştirme yönünde motive olmuşlardır. Bunun  üzerine  Hollandali profesor  A.  Tanenbaum işletim  sistemi derslerinde kullanmak üzere MINIX isminde mini bir Unix sistemi yazmştır. MINIX ticari amaçla çeşitli kesimler tarafından kullanılmş olsa da ciddi bir yaygınlık kazanmamştir. Fakat kaynak kodları pekçok geliştiriciye ilham vermiştir. 1985 yılında Richard Stallman FSF (Free  Software Foundation) isimli  bir  kurum  altında serbest  yazılım  fikrini atmştır.  Serbest  yazılım  bedava yazılımdan daha çok geliştirmede özgürlüğü  belirtmektedir.  Yani ,  programcı  yazdığı  programı  kaynak  koduyla  birlikte  verir, kaynak  kodu  sahiplenip  değişti rilmesini  engelleyemez.  Kaynak  kodu  elde  eden  programcı orijinal  yazardan alıntı yaptığını belirterek  kodu  değiştirebilir.  Serbest  yazılım  bedava olabilirde  olmayabilir de,  fakat  serbest  yazılım  parayla  satılsa  bile  kaynak  kodu  verilmek zorundadır.  Richard  Stallman  FSF  kurumunu  oluşturduktan  sonra  GNU  isimli  bir  proje oluşturmuştur.  GNU  (GNU’  not  UNIX)  bir işletim  sistemi  ve  yardımcı  ek  programlar oluşturmayı  hedefleyen  bir  projedir.  Bu  proje  hala  devam  etmektedir (www.fsf.org).  GNU projesi  kapsamında  pek çok  yararlı  programlar  yazılmştır (gcc  derleyicisi ). Ancak  maalesef işletim sisteminin kendisi  yazılamamştır. 
1990 yılında  Linus  Torvalds  GNU  lisansı  altında  MINIX  ve  UNIX  sistemlerinden  alıntılarla serbest  bir  Unix  sistemi  oluşturmuştur.  Bugün  Linux  sistemlerindeki  yazılımlar GNU  lisanslı yazılımlardır.  Yani,  Linux  sistemleri  çekirdeği  Linus  Torvalds  tarafından  tasarlanmş  olan yaralı programları GNU projesi kapsamıyla elde edilmiş olan bir sistemdir. Pekçok kişi bugün kullanılan bu sisteme Linux yerine GNU/Linux  denmesinin  daha  uygun  olacağını düşünmektedir. 
1980'li yılların sonlarına doğru UNIX sistemleri standart hale getirilmeye çalışılmştır. Bunun için IEEE bünyesinde stadardizasyon ekibi kurulmuş ve POSIX standartları diye bilinen standartlar oluşturulmuştur. POSIX bir grup standartlardan oluşan bir standartlar topluluğudur. Bu grup  içerisindeki herbir standart POSIX 1003.X  biçiminde isimlendirilmiştir. Örneğin,  1003.1  POSIX uyumlu  UNIX  sistemlerinin C'de  bulundurması gereken sistem fonksiyonlarına  ayrılmştır.  1003.2 Shell programlarının  kullandığı  standart komutları  tanımlamaktadır.

(Bu yazının tarihsel gelişim bölümü C ve sistem programcıları derneğinin Unix/Linux sistem programlama dökümanının giriş ksımından aynen kopyalanmıştır. Dökümana buradan erişim sağlayabilirsiniz.)

Giriş

Sistem fonksiyonları user mod'da uygulamayı donanıma en yakın yapan fonksiyonlardır.
Sistem çekirdeği içerisinde 2 türlü programlama yapilir.
  • Kernel modülü yazmak
  • Kernel yapısını degiştirerek yeniden kerneli derlemek

Standat C fonksiyonlari, POSIX fonksiyonlari, Sistem fonksiyonları

  • Standat C fonksiyonları bütün C derleyicilerinde olmasi gereken fonksiyonlardır ve bu fonksiyonlar en alt seviyeli fonksiyonlardir. 
  • POSIX fonksiyonlarıda butun POSIX sistemlerde var olan fonksiyonlardır ve bütün Unix/Linux sistemler icin kullanilabilir fonksiyonlardır. 
  • Sistem fonksiyonlari da biraz daha sisteme ozellestirilmis fonksiyonlardir.
Yazılımda genel olarak kod tekrarı istenmez. Cünkü kod tekrarı hem fazla yer kaplar hemde bakımı zordur. Bu nedenle yazılım sistemleri katman katman oluşturulur. Yani yeni bir katman başka bir katmanın var olduğu fikriyle oradaki fonksiyonlari cağırarak yapılır. Yazılımsal olarak en asağı katman işletim sisteminin dış dünyaya sunduğu aşagi seviyeli sistem fonksiyonlaridır.  

Örnek bir C kodu:

void add(a, b)
{
    return a+b;
}

int main(void)
{
    int a=10, b=20, c;
    c = add(a,b);
    ...    
}

Bu C kodunun Assembler karşılığı;

main proc near:
    push ebp
    mov ebp, esp
    sub ebp,12
    mov [ebp-4], 10
    mov [ebp-8], 20
    mov eax,[ebp-4]
    push ecx
    mov

Fakat sistem fonksiyonları isim ve parametrik yapı bakımından sistemden sisteme değisir. Hatta versiyondan versiyona bile değisir. Bunun yerine sistem fonksiyonalrını cağiran daha taşınabilir fonksiyon katmanı bulunmaktadır. Buna windows dünyasinda Windows API fonksiyonları, Unix/Linux dunyasinda POSIX fonksiyonları denilmektedir. 
POSIX fonksiyonları yalnızca linux sistemlerinde bulunmaktadir. Windows sistemlerde bulunmamaktadır.  Standart C fonksiyonları ise tum C derleyicilerinde bulunmaktadır. Java gibi .NET gibi QT gibi pekcok platfrom ve kütüphane kendi dünyasinda daha yüksek seviyeli ve taşınabilir fonksiyonlar ve sınıflar bulundurmaktadırlar.
Bazı POSIX fonksiyonları doğrudan bir sistem fonksiyonunu cağırmaktadir. Bazıları ise birden fazla sistem fonksiyonunu cagirir, bazıları ise hiçbir sistem fonksiyonunu cağırmayabilir. Bu durum Windows API fonksiyonları için de geçerlidir.
Linux sistemlerde trapgate mekaniması için aşağıdaki link bağlantıları faydalı olabilir.
  • http://stackoverflow.com/questions/15168822/intel-x86-vs-x64-interrupt-call
  • http://stackoverflow.com/questions/9111039/for-signal-in-linux-who-calls-int-0x80

İşlemcilerin Sunduğu Koruma Mekanizması

Koruma mekanizması bir processin zararli etkilerinden diğer procesleri ve isletim sistemini korumak icin olusturulmus bir mekanizmadır. Birinci elden işlemci tarafından uygulanır.
Koruma mekanizmasının iki yönü vardır;
  • bellek korumasi 
  • komut korumasi
Bellek koruması; bir processin kendi sınırlarının dışına erişip erişmediğini denetler. Komut koruması ise sistemi çökertecek komutların kullanılmasını yasaklamaktadir. Bu koruma mekanizmaları işletim sisteminin kullanıcı moduna (user modda) geçerli korumalardır.
Prosesler kernel ve user mod olmak üzere iki moddan birinde çalışır. Normal programlar user modda çalıştırılırlar. İşletim sisteminin kodlari ve aygıt sürücüler kernel modda çalışırlar. Kernel modda ise koruma mekanizmasi uygulanmaz. Hersey yapilabilmektedir.
Bir proses sürekli user modda kalmayabilir. Bir sistem fonksiyonu cağırıldığında proses user moddan gecici olarak kernel moda gecer. Sistem fonksiyonu kernel modda çalıştırılır. Sonra yeniden user moda geri dönülür.

Unix/Linux Sistemlerde Prosesler

Çalışmakta olan programlara proses denir. Her prosesin kernel alanı içerisinde oluşturulmuş olan bir process kontrol bloğu vardır. Tipik olarak process kontrol bloğu'nun icinde şu bilgiler bulunur.
  • Processin yetkilendirme bilgileri 
  • Processin ID'si
  • Processin bellek alanının nerede olduğu
  • Processin erişim hakları
  • Processin çalisma dizini
  • Processin açmis olduğu dosyalar
  • Processin istatistiksel bilgileri (örneğin process yaşamının ne kadarını kernel, ne kadarını user modda geçirmiştir)
  • Processin kesilme sırasındaki yazmaç (register) bilgileri
  • Processin Thread'leri hakkında bilgiler
  • .....

---------------

  • Bir process oluşturulurken, user modda oluşturulan hafıza alanının yanısıra bir de kernel alanı icerisinde (kernel stack) de bir alan ayırılır.
  • lxr.linux.no : linux kaynak kodlarını görebilecegimiz bir navigator. Versiyonlar arasında karşılaştırma da yapabilmektedir.
  • Linux sistemlerde kullanılan sistem çağrılarını http://docs.cs.up.ac.za/programming/asm/derick_tut/syscalls.html linkinden görebiliriz.

Linux sistemlerde proses kontrol bloğu task_struct ile temsil edilmektdir. Proses kontrol bloğu dallı budaklı bir yapı biçiminde olabilir. Yani, bazı büyük bilgiler ayrı veri yapılarındadırlar. Proses konrtol bloğundan oralar (büyük veri alanları) göstericiler ile gösterilmektedir. Bir bilginin process kontrol blogunda olmasi demek ona proses kontrol blığu ile erişilebiliyor demekdir. 

Not : proc dosya sisteminde procese ait bilgi silinir ise processi ps komutundan gizleyebiliriz 
Not : Windows ile ilgili dokümante edilmemiş bazı yapıları reactos.org adresinden görebilir ve inceleyebiliriz. Windows'da EPROCESS yapısı ile process kontrol blok tanımlanabilir. Bu yapı içerisinde PEB tanımlanmıştır. 

Linux'da threadler'i processler gibi yapmislar. Kendi proses kontrol bloğu bulunmaktadır. 
Unix/Linux sistemlerinde her prosesin sistem genelinde tek olan bir ID değeri vardır. Prosesler arasıda altlık/üstlük (parent/child) ilişkisi söz konusudur. Her proses başka bir proses tarafından yaratılır. Örneğin UNIX/Linux sistemlerindeki komut satırı da aslında bir programdır. Yani bir processdir (/bin/bash).

Sistem Boot Edildiğinde Proseslerin Durumu Nedir?

Bootdan gelen akış ilk prosesi oluşturur. Buna swapper yada pager denilmektedir ve bu prosesin ID degeri 0'dir. Bu proses init denilen prosesi yaratır ve artık swapper birşey yapmadan arka planda bekler. Sonra bir dizi olaylar sırasında akış shell'e kadar gelir. 

Kernel kendini yükledikten sonra try_to_run_init_process fonksiyonu ile baslangıç prosesini çalıştırır.

İşletim sistemi aslında bir kod yığınının hazırda beklemesidir.

Proses'in UserID ve GroupID Değerleri

Her prosesin proses kontrol bloğunda saklanan UserId ve GroupID degerleri vardır. UserId ve GroupId değerleri birer sayı olmasına karşın bunlara birer yazı da karşılık düşürülmüştür. Kernel işlemlerini bu isimlerin sayısal değerleri ile gerçeklestirmektedir. Ancak kullanıcı ve grup isimleri daha akılda kalıcıdır. 
Geleneksel olarak UserID'nin isimsel karşılığı /etc/passwd dosyasında GroupID'nin ise isimsel karşılığı /etc/group dosyasında tutulmalıdır. 
Aynı zamanda her prosesin effective UserID (etkin kullanici id'si) ve effective GrupID değeri de vardır. Normalde UserID ile etkin UserID, GrupID ile etkin GroupID aynı değerdedir. Fakat bazı seyrek durumlarda bunlar farklılaşabilmektedir. Fakat test işlemini her zaman etkin UserID ve etkin GrupID sokulur. 
Processin UserID, GrupID,etkin UserID,etkin GrupID değerleri proses yaratılırken üst prosesden aktarılır. Biz shell'den bir program çalıştırdığımızda shell'de bir process olduğuna gore çalıştırdığımız prosesin UserID ve GroupID değerleri shell'inki ile aynı olur. 

Shell üzerinden "id" isimli komut o anda shell processinin etkin UserID ve etkin GroupID değerlerini bize verir. 

Biz xWindow sistemi veya terminal yolu ile sisteme giriş yaptığımızda ilk UserID ve GroupID nasıl oluşturulmaktadır?

Işte bize, sisteme login olunurken bir kullanıcı ve parola sorulur. Dogrulama yapılır ve /etc/passwd'de gösterilen bir program bu user ve group id ile calistirilir. 

Uygulamanin calisma sirasi su sekilde  boot |---> Kernel |---> init |--->  tty  |--->  login

0 numarali user id değerine sahip prosese süper proses yada root prosesi denilmektedir. Pekcok linux dağıtımında işin başında ismi root olan ve userId değeri 0 olan bir kullanıcı da /etc/passwd dosyasında yaratılmıştır. 
Aslında /etc/passwd dosyasının kendisi, init programı ve login programı kernel kodlarına dahil değildir. Fakat kernel kodları prosesin etkin kullanıcı ve grup id'lerine bakacak şekilde yazılmıştır. Biz /etc/passwd dosyasını silersek login programı calıştığında bu dosyanın ici boş olduğu icin giriş işlemi yapılamayacaktır.

Prosesin UserID ve GroupId Değerlerinin Anlamı

Processin UserId ve GrupId'si buyuk ölçüde dosya sistemlerine ilişkin teste sokulurlar. Unix/Linux sistemlerinde bir dosyanın açılması ve yaratılması işlemleri yalnızca open isimli POSIX fonksiyonu ile yapılmaktadır. Bu fonksiyon linuxda sys_open fonksiyonunu cağırmaktadır.  
Glibc kütüphanesinin kaynak kodu içerisinde POSIX fonksiyonlarının kaynak kodları bulunmaktadır. 
Unix/Linux sistemlerinde her dosyanın user/group id değerleri vardir. Bir dosyanın userId'si o dosyayi yaratan prosesin yani open fonksiyonunu cağıran processin etkin user id'si olarak alınır. Dosyanın grup id değeri ise sistemden sisteme değişebilecek biçimde iki seçenekten birisi olabilir. 
  1. Dosyayı yaratan prosesin etkin GrupID'si olarak
  2. Dosyanın içinde yaratıldığı dizinin etkin GrorpID'si olarak 
Linux'da default durum dosyanın group id'sinin onu yaratan processin etkin group idsi biçiminde alınmasıdır. Fakat bu durum çesitli koşullarda değiştirilebilmektedir.


26 Aralık 2013 Perşembe

Windows API Programlamaya İlişkin Temel Bilgiler

Windows API programlama yaparken kaynak kodun içerisine windows.h dosyasi include edilmelidir. Bu dosya içerisinde içerisinde gereken pekçok bildirim vardir.
Sistem programlamada yazim stili olarak macar notasyonu (hungarian notation) kullanilir.

Macar notasyonuna göre;
  • Fonksiyonlar pascal tarzı harflendirilir. Yani her sözcüğün ilk harfi büyüktür. (her sozcugun ilk harfi buyuktur. Fiiller ince kullanilir.)
  • GetWindowLong(), CreateProcess()
  • Degiskenler genellikle onun turunu belirten bir önek ile başlatılır. Degişkenler deve notasyonu ile isimlendirilir. (camel notation)
  • BYTE, WORD ve DWORD typedef isimleri sırasiyla 1, 2 ve 4 byte lik işaretsiz tam sayı türlerini temsil eder.
  • Typedef isimlerindeki P öneki ve LP öneki gösterici anlamına gelir. (Windows 3.x sistemlerinde farkli anlama gelmektedir.)
  • Word kavramı donanım ve yazılımda farklıdırlar. Yazılımda 2 byte, ama donanimda register uzunlugu olarak adlandirilirlar.
  • Gösterdigi yer const olan const göstericilerde PC veya LPC öneki kullanılır.
  • Yazi türleri (char türden göstericiler) SZ yada LPSZ ile baslatilir.
  • PSTR, LPSTR, LPCSTR yazıyı gösteren gösterici anlamındadır.
  • BOOL türü başarı ve başarısızlığı anlatan bir türdür. INT olarak typedef edilmistir. ( 0 basarisiz, 0 disi basarisiz)
  • fonksiyon tanimlarinda in, in_out, ve __out ifadeleri sirasiyla ver kullanayim, ver icini ben de doldurayim ve ver icicin doldurayim manasinda kullanilir. CreateProcess() fonksiyonunun msdn icerisindeki dokumani icerisinde gorulebilir.

Unicode Kullanımı


  • java ve c# da char turu 2 byte tutar. C de ise 1 byte dir.
  • typedef unsigned short wchart;
  • 32 bi windows sistemlerine gecildiginde unicode kullanimi da devreye girmistir. Butun yazi parametresi alan fonksiyonlardan aslinda 2 tane bulunmektedir. Sonu A ile birenler ASCII sonu W ile bitenler UNICODE anlamina gelmektedir.
  • Biz program yazarken genellikle A ve W olmayan isimleri kullaniriz bu isimler duruma gore A ve W lu bicime donusturulurler.
  • IDE de bu ayar proje seceneklerinden yapilmaktadir ve default durum UNICODE bicimindedir.
  • CreateProcessA (ascii)
  • CreateProcessW (unicode)
  • C de ascii string “xxxx” biciminde, unicode string L”xxxxx” biciminde yazilir. Eger programimizi ascci ile unicode arasinda istedigimiz zaman cevirebilecegimiz bicimde yazmak istiyorsak. Butun iki tirnaklari TEXT macrosu ile girmelidir


#ifdef UNICODE
#define TEXT() L##S
#else
#define TEXT(,) S
#endif


  • Ayrica Standat C fonksiyonlarinin ascii ve unicode versiyonlari vardir. Bunlar arasinda otomatik gecis yapmak icin ozel makrolar kullanilmaktadir. Bu makrolar tchar.h dosyasi icerisinde.
  • Aslinda standartlara gore printf fonksiyonunun ascii ismi printf unicode ismi wprintf dir. Windowsta _tprintf der isek bu ide deki ayara gore wprintf veya printf kullanir.

HANDLE Kavramı


  • Bir veri yapisina erismekte kullanilan anahtar degerlere handle denir.
  • Handle genel olarak bir veri yapisina erismekte kullanilan tekil anahtar degerdir. Handle tipik olarak bir adres formunda bulunabuilir. Bir dizide index belirtiyor olabilir.
  • Handle kullanan kisi onun ne anlam ifade ettigini bilmek zorunda degildir.
  • Bir handle sisteminde tipik olarak handle sistemini yaratan bir fonksiyon bulunur. Bunlar tipik olarak CreateXxxxx veya OpenXxxx olarak adlandirilir
  • Handle sistemini kullanan fonksiyonlar vardir. bunlara biz handle degerini veririz ve giris yaptiririz. Nihayet handle sistemini yok eden fonksiyonalr vardir. Bunlar CloseXxx veya DestroyXxxx olarak adlandirilir.