28 Mart 2014 Cuma

Linux Sistem Programlama - Bölüm 5

Fork isleminden sonra once ust prosesin mi yoksan alt prosesin mi cizelgelenecegi sistemden sisteme degisebilmektedir. 


/*-----------------------------------------------------------
 Üst proses
------------------------------------------------------------*/

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

void exitsys(const char *msg);

int g_x;

int main(int argc, char *argv[])
{
 pid_t pid;

 if ((pid = fork()) < 0)
  exitsys("fork");

 if (pid != 0) {
  g_x = 10;
  printf("parent process...\n");
 
 }
 else {
  sleep(2);
  printf("child process: %d\n", g_x);  /* 0 yazdýrýlacak */
 }

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

 return 0;
}

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

exec Fonksiyonlari

Exec fonksiyonlari calistirilabilen bir dosyanin yol ifadesini parametre olarak alir. Mevcut prosesin bellek alanini tamamen bosaltir. Yerine belirtilen programi yukler ve proses calismasina baska bir program olarak devam eder. Proses kontrol blogu degismez. Dolayisi isle prosesin id'si de degismez.Biz exec cagrisindan sonra birtakim kodlar yazsak bile bu kodlar asla calismayacaktir. Yani exec calismakta olan programin yerine artik baska programin calismasini saglamaktadir.
Bu durumda hem kendi programimiznin devam etmesini istiyorsa ve hemde baska bir programi calistirmak istiyorsak tek yapilan sey once fork yapmak sonrasinda child prosesde exec ile istedigimiz uygulamayi calistirmaktir.
linux cekirdegi 2.6 surumu ile birlikte preemptive sistemlere gecmistir.
Fork isleminden sonra exec yapmak maliyetli olarak algilanabilir. Yani madem alt prosesde exec yapilacak neden ust prosesin bellek alani alt prosese kopyalansin ki? Iste eskiden eger eger forkdan sonra exec yapilacak ise bu maliyeti azaltmak icin vfork isimli bir fonksiyon eklenmisti. vfork fonksiyonu hemen exec yapilacagi fikri ile ust prosesten cok kucuk bir bellek alnini kopyalamaktadir. Ancak bugunku islemci mimarileri dikkate alindiginda zaten fork adeta vfork gibi calismaktadir. Bu nedenle bugun vfork fonksiyonuna gerek kalmamistir.
Execl fonksiyonunun prototipi su sekildedir.


#include <unistd.h>
int execl(const char *path, const char *arg, ...);

Fonksiyonun birinci parametresi calistirilacak programin yol ifadesini alir. Diger parametresleri programin komut satiri argumanlarini belirtir. Arguman listesinin sonuna NULL adres girilmesi gerekmektedir. Programin ilk komut satiri argumaninin program ismi olmai C standartlarinda zorunludur. Beklenti o yondedor. Ancak gercek hayatta zorunlu degildir. Fonksiyon basarisizlik durumunda -1 degerine geri doner. Basari durumunda zaten geri donmez.
Onemli : Arguman listasinin sonundaki null adress gosteriye donusturulerek verilmelidir. Aksi taktirde bu argumana karsi uc nokta () karsi gelecegi icin NULL yada 0 int olarak degerlendirilir. Bu da su kosullar altinda sorunlara yol acmaktadir.
     >>  Null adresin 0 olmamasi durumunda
     >>  int turu ile gosterici uzunlugunun ayni olmadigi sistemlerde (64 Bit)
Suphesis bu donusturme gerekligi yanlizca prototipde 3 nokta oldugu durumda gecerlidir. Yoksa protatipde NULL yada 0 in karsiliginda acikca bir gosterici var ise derleyici o sifirin zaten null adres anlamina geldigini bilir. 


/*-----------------------------------------------------------------
 execl fonksiyonunun uygulanmasý
-----------------------------------------------------------------*/

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

void exitsys(const char *msg);

int main(int argc, char *argv[])
{
 printf("starts...\n");

 if (execl("/bin/ls", "/bin/ls", "-l", (char *) NULL) < 0)
  exitsys("execl");

 printf("unreachable code!..\n");

 return 0;
}

void exitsys(const char *msg)
{
 perror(msg);
 exit(EXIT_FAILURE);
}
Exec fonksiyonlarinin v'li versiyonlari komut satiri argumanlarini bir dizi olarak alir.

#include <unistd.h>
int execv(const char *path, char *const argv[]);

Bu dizinin sonunda yine NULL adres sabiti olmalidir.
C Standartlarina gore arvg ile belirtilen komut satiri arguman listesinin sonunda null adres bulunmak zorundadir. Bu durumda ornegin biz komut satiri argumanlarini su sekilde de yazdirabilir.  

for( i=0 ; argv[i] != 0 ; NULL; ++i){
    puts(argv[i]);
}

bir execv ornegi;

/*------------------------------------------------------
    execv fonksiyonu ornegi
------------------------------------------------------*/

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

void exitsys(const char *msg);

int main(int argc, char *argv[])
{
 char *args[] = {"/bin/ls", "-l", "-i", NULL};

 if (execv("/bin/ls", args) < 0)
  exitsys("execl");

 printf("unreachable code!..\n");

 return 0;
}

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

komut satirindan girilen bir komutun calistirilmasi uygulamasi;

/*--------------------------------------------------------------
  execv fonksiyonun kullanımı
---------------------------------------------------------------*/

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

void exitsys(const char *msg);

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

 if (execv(argv[1], &argv[1]) < 0)
  exitsys("execl");

 printf("unreachable code!..\n");

 return 0;
}

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

Exec fonksiyonlarinin p'li versiyonlari path cevre degiskenlerine bakmaktadir.
execlp ve execvp fonksiyonlarinin prototipleri su sekildedir.

#include <unistd.h>
int execlp(const char *file, const char *arg, ...);
int execvp(const char *file, char *const argv[]);

Bu fonksiyonlar su sekilde calismaktadir;
  1. Eger calistirilabilen dosyanin yok ifadesinda hivbir / karakteri kullanilmamissa, yani dogrudan program ismi yazilmissa, bu durumda ilgili dosya $PATH cevre degisken ile belirtilen dizinlerde aratilir. 
  2. Eger yol ifadesinde en az bir bolu karakteri var ise bu durumda yol ifadesi normak kurallara gore $PATH cevre degiskenine bakilmadan calistirilir. Yani bu durumda fonksiyonlarin calismasi execl ve execv'den farkli degildir. 
Kabuk programlari kabuk uzerinde program calistirilmak istendiginde bu calistirma islemini exec fonksiyonlarinin p'li versiyonlariyla yapmaktadir. Bu nedenle calisma dizinindeki sample programini "sample" diyerek calistiramayiz. Araya bir bolu karakteri yerlestirerek exec fonksiyonlarinin p'li versiyonlarinin $PATH cevre degiskenine bakmasini engelleyebiliriz. Bunu saglamanin enpratik yolu ./sample ifadesi ile programi calistirmaktir.
/*--------------------------------------------------------------
  execv fonksiyonun kullanımı
--------------------------------------------------------------*/

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

void exitsys(const char *msg);

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

 if (execp(argv[1], &argv[1]) < 0)
  exitsys("execl");

 printf("unreachable code!..\n");

 return 0;
}

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

Ayrica
 bir de exec fonksiyonlarinin e'li versiyonlari vardir. Bu e'li 
versiyonlari ayrica cevre degiskenleri de alirlar. Bu konu ileride ele 
alinacaktir.

#include <unistd.h>
int execve(const char *filename, char *const argv[],  char *const envp[]);

Fonsiyonun e'li versiyonlari cevre degisken listesini de bizim belirlememe olanak saglar.

Fork ve Exec Fonksiyonlarinin bir arada uygulanmasi

Birkez fork yapip alt prosesde exec uygulama kalibi soyle uygulanabilir.

pid_t pid;

if((pid = fork()) < 0){
    perror("fork");
    exit(EXIT_FAILURE);
}

if(pid == 0){
    if(exec(...) < 0){
        perror("exec");
        exit(EXIT_FAILURE);
    }
}
/* parent */

Bu bu kullanim asagidaki kullanim ile tamamen esdegerdir.

if((pid == 0) && (exec(...) < 0)){
    perror("exec");
    exit(EXIT_FAILURE);
}
/* parent */

Fork ve exec fonksiyonlarinin birlikte kullanilmasi ornegi;

/*----------------------------------------------------------
   fork ve exec fonksiyonlarinin birlikte kullanilmasi
-----------------------------------------------------------*/

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

void exitsys(const char *msg);

int main(int argc, char *argv[])
{
 pid_t pid;
 
 if (argc < 2) {
  fprintf(stderr, "wrong number of arguments!..\n");
  exit(EXIT_FAILURE);
 }
 
 if ((pid = fork()) < 0)
  exitsys("fork");
 
 if (pid == 0 && execvp(argv[1], &argv[1]) < 0)
  exitsys("execvp");
 
 printf("parent continues...\n");
 
 return 0;
}

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

8 Tane child proses olusturan ve ekrana test yazan bir uygulama

/*------------------------------------------------------------------
// Aþağıdaki programda 8 tane alt proses oluşur 
// dolayısıyla ekrana 8 tane "test" yazısı basılır
-------------------------------------------------------------------*/

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

void exitsys(const char *msg);

int main(int argc, char *argv[])
{
 if (fork() < 0)
  exitsys("fork");
 
 if (fork() < 0)
  exitsys("fork");
 
 if (fork() < 0)
  exitsys("fork");
 
 printf("test\n");
  
 
 return 0;
}

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

ProsesID'lerine iliskin POSIX Fonksiyonlari

getpid fonksiyonu calismakta olan prosesin kendi prosesid'sini bize verir. getppid fonksiyonu ise calismakta olan prosesin ust prosesinin proses id'sii bize verir.

#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
ps komutu default olarak komutu uygulayan kullanicinin komutun uygulandigi terminale bagli prosesleri listeler. ps -e yada ps -A sistemdeki tum proseseleri listeler. ps -a terminale bagli olmayan yani baska terminale bagli olan prosesleri de listeler (o kullaniciya ait). -l secenegi daha fazla bilgiden olusan bir liste bize verir. 
/*----------------------------------------------------------------
 getpid ve getppid fonksiyonlarý
-----------------------------------------------------------------*/

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

void exitsys(const char *msg);

int main(int argc, char *argv[])
{
 pid_t pid;

 if ((pid = fork()) < 0)
  exitsys("fork");

 if (pid == 0)
  printf("(Child Process) pid = %ld, ppid = %ld\n", (long) getpid(), (long) getppid());
 else {
  printf("(Parent Process) pid = %ld, ppid = %ld\n", (long) getpid(), (long) getppid());
  sleep(1);
 }

 return 0;
}

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

Alt proseslerin beklenmesi ve exit kodlarinin elde edilmesi

Her proses sonlandiginda sisteme bir exit kodu iletir. Prosesin exit kodu tipik olarak prosesin kontrol blogunda saklanmaktadir. Prosesin hangi degerde sonlandiginin sistem bakimindan bir onemi yoktur. Bundan prosesi yaratan ust proses faydalanabilir. Fakat geleneksel olarak basari durumu icin 0 basarisizlik durumu icin 0 disi degerler ile sonlanir.

Exit standart bir C fonksiyonudur. Prototipi su sekildedir.

#include <stdlib.h>
void exit(int status);

Unix/Linux sistemlerinde proses sonlandiran posix fonksiyonu _exit isimli fonksiyondur.

#include <unistd.h>
void _exit(int status);

_exit fonksiyonu linux sistemlerinde dosgrudan sys_exit isimli sistem fonksiyonunu cagirmaktadir. 

  exit ---->  _exit -----> sys_exit
 (Std C)      (POSIX)        (Linux)

Biz C programlarinda mumkun oldugunda standart exit fonksiyonunu kullanmaliyiz. Cunku bu fonksiyon standart C kutuphanelerine iliskin geri birakma islemleri yapmaktadir. Ozellikle standad C nin dosya modunda kullanilmissa bu cok onemlidir. 
Aslinda bir C programinda ilk calisan fonksiyon main fonksiyonu degildir. BIzim yazdigimiz programla  birlikte derleyicinin baslangic kodu (startup code) denilen bir kod birlikte link edilir ve calistirilabilen dosyanin baslangic noktasi (entry point) bu baslangic kodunun icerisindedir. Main fonksiyonu baslangic kodundan cagirilmaktadir. 

Derleyicinin baslangic kodu
....
....
....
call main
PUSH eax
CALL exit

O halde akis main fonksiyonunu bitirirse exit fonksiyonu main fonksiyonunun geri donus degeri ile cagirilmaktadir. Standartlarda programin asagidaki bicimde sonlanmasi gerektigi acikca belirtilmistir. 

exit(Main());

C Programlama dilinde Main fonksiyonuna ozgu olarak eger main'de hic return kullanilmaz ise ve akis return gormeden main fonksiyonunu bitirirse main fonksiyonunun 0 ile geri dondugu kabul edilir.
Bir proses sonlandiginda o prosesin bellek alanlari ve kullandigi kaynaklar sisteme iade edilir. Fakat proses kontrol blogu dolayisi ile proses id'si ust proses onun exit kodunu alabilir diye bekletilir. Bu durumda gercekte sonlanmis fakat proses kontrol blogu silinmemis, dolayisi ile proses id'si serbert birakilmamis bir durum olusur. Bu duruma dusen proseslere hortlak (Zombie) prosesler denilmektedir. Zombi prosesin engellenmesi icin tipik olarak ust prosesin wait fonksiyonlari ile alt proseslerin exit kodunu almasi gerekmektedir. Birkac senaryo soz konusudur. 
  1. Once alt proses sonlanmistir. Ust proses alt prosesin exit kodunu henuz almamistir. Bu durumda alt proses zombi durumundadir.
  2. Alt proses sonlanmistir. daha sonra ust proses alt prosesin exit kodunu almadan sonlanmistir. Budurumda init proses yardimi ile alt prosesin kontrol blogu bosaltilir. Yani hortlaklik ortadan kalkar.
  3. Alt proses devam ederken ust proses sonlanmistir. Bu proseslere oksuz (orphan) proses denilmektedir. Oksuz proseslerin ust prosesligine init proses atanir. Oksuz proses sonlandiginda init proses onun exit kodunu alir. Alt proses zombi olmaktan kurtarilir.
O halde hortlaklik alt proses sonlandigi halde ust prosesin onun exit kodunu almadan devam etmesi durumunda ve bu sure zarfinda olusmaktadir.

/*----------------------------------------------------------------------
 30 saniye süresince hortlak oluşturan proses
-----------------------------------------------------------------------*/

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

void exitsys(const char *msg);

int main(int argc, char *argv[])
{
 pid_t pid;
 
 if ((pid = fork()) < 0)
  exitsys("fork");
 
 if (pid) {
  sleep(30);  
 }
 else {
  exit(EXIT_SUCCESS);
 }
 
 return 0;
}

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

Wait Fonksiyonlari

En cok kullanilan wait fonksiyonlarinin prototipi su sekildedir.

#include <sys/wait.h>
pid_t wait(int *status);

Fonksiyon ilk alt proses sonlanana kadar blokede bekler, onun exit kodunu alir, parametresiyle aldigi adresdeki int turden nesneye yerlestirir. O alt prosesin ID degeri ile geri doner. Parametre NULL gecilebilir. Bu durumda proses exit kodunu alir fakat bize vermez. Alt proses zombilikten kurtulmus olur.  Fonksiyon basarisizlik durumunda -1 degerine geri doner.
aslinda fonksiyon adresini verdigimiz int nesnenin icerisine yalnizca programin exit kodunu kodlamaz, prosesin neden sonlandigina iliskin bazi bilgileri de bazi bitlere kodlar. WEXITSTATUS isimli makro bize exit kodunu verir. WIFEXITED bize programin normal olarak sonlanip sonlanmadigi bilgisini eririr. WIFSINGLED makrosu bize prosesin bir sinyalle sonlanip sonlanmadigi bildisini verir. Proses normal sonlanmamis ise exit kodunun bir anlamu yoktur. Bu nedenle once programcinin once WIFEXITED  makrosu ile kontrol yapmasi gerekmektedir.


/*--------------------------------------------------------------
 wait fonksiyonun kullanımı
---------------------------------------------------------------*/

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

void exitsys(const char *msg);

int main(int argc, char *argv[])
{
 pid_t pid, pidWait;
 int status;
 
 if ((pid = fork()) < 0)
  exitsys("fork");
 
 if (pid != 0) {
  printf("parent is waiting for the child to exit...\n");
  if ((pidWait = wait(&status)) < 0)
   exitsys("wait");
  
  if (WIFEXITED(status))
   printf("Ok, child (%ld) exited with %d\n", (long) pidWait, 
               WEXITSTATUS(status));
  else if (WIFSIGNALED(status))
   printf("child stopped by a signal!\n");
  }
 else {
  sleep(20);
  return 100;
 }
 
 return 0;
}

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

Waitpid isimli fonksiyon islevsel olarak wait fonksiyonun kapsar. Daha detaylidir. 

#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);

Fonksiyonun birinci parametresi beklenmek istenen prosesin ID degerini belirtir. Fonksiyon birinci parametresi -1 girilir ise Bu durumda ilk sonlanan alt proses beklenir(tipki wait gibi). Birinci parametre 0'dan buyuk bir deger girilir ise o Id'ye sahip bir alt proses beklenir. Birinci parametre 0 ise proses grup id'si waitpid fonksiyonunu cagiran fonksiyon ile ayni olan prosesler beklenir. Nihayet Birinci parametre -1 den kucuk bir deger girilir ise bu degerin mutlak degeri alinir ve proses id'si bu deger olan prosesler beklenir. Fonksiyonun ikinci parametresi yine prosesin exit kodunun yerlestirilecegi nesne adresidir. Bu parametre yine NULL gecilebilir. Nihayet son parametre 0 gecilebilir fakat bazi ozel degerler de gecilebilir. Ornegin WNOHANG gecilir ise fonksiyon hic bekleme yapmaz. Sonlanmis proses var ise onun exit kodunu alir. Yoksa da hemen sonlanir. Fakat bu durumda -1'e geri doner. 

Bu durumda asagida gosterilen iki fonsiyon cagrisi tamamen birbirine esdegerdir.
1. wait(&status)
2. waitpid(-1, &status, 0);
linux'da geri donus degerleri 1 byte uzunlugundadir.

/*----------------------------------------------------
 waitpid fonksiyonu
-----------------------------------------------------*/

/* sample.c */

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

void exitsys(const char *msg);

int main(int argc, char *argv[])
{
 pid_t pid;
 int status;
 
 if ((pid = fork()) < 0)
  exitsys("fork");
 
 if (pid != 0) {
  printf("parent is waiting for the child to exit...\n");
  if (waitpid(pid, &status, 0) < 0)
   exitsys("wait");
  
  if (WIFEXITED(status))
   printf("Ok, child exited with %d\n", WEXITSTATUS(status));
  else if (WIFSIGNALED(status))
   printf("child stopped by a signal!\n");
  }
 else {
  if (execl("testprog", "testprog", (char *) NULL) < 0)
   exitsys("execl");
 }
 
 return 0;
}

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

/* testprog.c */

#include <stdio.h>

int main(void)
{
 printf("testprog starts...\n");
 
 return 250;
}

21 Mart 2014 Cuma

Linux Sistem Programlama - Bölüm 4

Bir dosyanin hardlink'i komut satirinda ln komutu ile olusturulmaktadir. 
cx@debx86:~$ ls -il a.txt
1061483 -rw-r--r-- 1 cx cx 4 Feb 18 15:10 a.txt
cx@debx86:~$ ln a.txt b.txt
cx@debx86:~$ ls -il *.txt
1061483 -rw-r--r-- 2 cx cx 4 Feb 18 15:10 a.txt
1061483 -rw-r--r-- 2 cx cx 4 Feb 18 15:10 b.txt
cx@debx86:~$

Bir dizin olusturuldugu zaman nokta ve nokta nokta isimli iki dizin otomatik yaratilmaktadir. BUnlar aslinda birer hardlink girisleridir. Boylece nokta dizini kendi dizinin hardlink sayacini 1 artirir. ikinokta dizini de ust dizinin harlnik sayacini 1 artirir.

Sembolik Link Dosyaları

http://www.kaanaslan.com/resource/article/display_article.php?id=74 adresinden detayli bilgilerin elde edilmesi lazim
Sembolik link dosyalari normal bir dosya gibidir. NOrmal bir dosyanin icerisinde dosyanin bilgileri vardir fakat sembolik link dosyalarinin icerisinde o dosyanin belirtdigi asil dosyanin yol ifadesi vardir. Pekcok sistem ve posix fonksiyonuna biz sembolik bir dosyanin yol ifadesini verdigimizde o aslinda o dosyani belirttigi asil dosya uzerinde islem yapar.
Bazen sembolik link izlemek probleme yol acabilir. Ornegin dizin agacini dolasirken biz baska bir dizine bir sembolik link gordugumuzde o dizine gecersek ve o dizin de bizim dizinimizi kapsiyor ise sonsuz dongu olusur. Bu nedenle bazi fonksiyonlari "l" ile baslayan versiyonlari olusturulmustur. Fponksiyonlarin bu "l" 'li versiyonlari sembolik link dosyasi soz konusu oldugunda o dosyanin kendisi uzerinde islem yapar. Dosyanin gosterdigi dosya uzerinde degil. Ornegin stat fonksiyonunun lstat isimli bir verisonu vardir.
Sembolik baglanti dosyasi baska bir sembolik baglanti dosyasini gosterebilir. Hatta dongusel bir durum bile olusabilir. Boyle bir dongusel durumda sistem fonsiyonlar bunu tespit eder ve hata ile donus yapar. errno degiskenine ELOOP degeri set eder. Bazi fonksiyonlar hic sembolik baglantilari izlememektedir.  Dogrudan sembolk dosyanin kendisi uzerinde islem yapmaktadir. hardlink  dosyasi olusturabilmek icin link isimli bir fonksiyon vardir. Sembolik link olusturabilmek icin symlink isimli fonksiyon kullanilmaktadir.

#include <unistd.h>
int link(const char *oldpath, const char *newpath);
int symlink(const char *oldpath, const char *newpath);

Komut satirindan sembolik link dosyalari ln -s komutu ile olusturulurlar.
Sembolik dosyanin gosterdigi dosya silinir ise bu duruma danling link denilmektedir. Boylesi bir durumda open fonksiyonu ile bir dosya acmaya calisirsak olmayan bir dosyayi acmayaclisir gibi oluruz.
Sembolik linkler ozellikle bazi dizinlere pratik erisim icin tercih edilmektedir. 

Pogramin Calisma Dizini

Her prosesin bir calisma dizini vardir. Prosesin calisma dizini prosesin kontrol blogunda saklanmaktadir. Prosesin calisma dizin fork sirasinda ust prosesden aktarilmaktadir. Prosesin calisma dizini goreli yol ifadelerinin cozulmesinde kullanilir. Eger yol ifadesinin ilk karakteri / ise bu mutlak bir yol ifadesidir. Mutlak yol ifadelerinin cozulmesi kok dizinden itibaren yapilir. Eger yol ifadesi / ile baslamiyor ise o zaman prosesin calisma dizininde itibaren belirtilen yolu kullanir. Prosesin calisma dizini goreli yol ifadelerinin cozulmesi icin bir orijin gorevi yapar.?

Peki biz login oldugumuzda shell prosesinin calisma dizini nasil belirlenmektedir?

Biz shell altinda bir program calistirdigimizda calismadizini shell ile ayni olacaktir. iste login programi bize ssiteme sokarken /etc/passwd dosyasindaki belirtilen programi (genellikle /bin/bash ) yine orada belirtilen calisma dizini olarak ayarlayarak calismaktadir.
Bir proses calisirken kendi calisma dizinini chdir fonksiyonu ile degistirebilmektedir.


#include <unistd.h>
int chdir(const char *path);

finksiyonun parametresi prosesin yeni calisma dizini olacaktir. Fonksiyon basari durumunda 0 basarisizlik durumunda -1 olarak geri doner.
Bir proses baska bir prosesin calisma dizinini degistiremez. Boyle bir sistem fonksiyonu yoktur. Bu durumda ornegin shelin cd komutu aslinda calistirilabilir bir program degildir. shelin icsel komutudur.

Bir prosesin proses kontrol kontrol blogundaki / dizini kaydi degistirilebilir. Boylelikle uygulamayi bir dizin icerisine hapsedebiliriz.

Prosesin calisma dizini getcwd isimli posix fonksiyonu ile alinir. Fonksiyonun prototipi su sekildedir.

#include <unistd.h>
char *getcwd(char *buf, size_t size);

Fonksiyonun birinci parametresi calisma dizininin yerlestirilecegi bellek alani adresini ikinci parametresi de dizinin uzunlugunu alir. Fonksiyon basari durumunda birinci parametre ile girilen parametrenin aynisina, basarisizlik durumunda null olarak geri doner.

/*-----------------------------------------------------
 getcwd fonksiyonu
------------------------------------------------------*/

#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);

int main(int argc, char *argv[])
{
 char cwd[1024];

 if (getcwd(cwd, 1024) == NULL)
  exitsys("getcwd");

 puts(cwd);

 return 0;
}


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

Ornek Bir Shell Programi

/*--------------------------------------------------------
 Örnek bir shell programi
---------------------------------------------------------*/

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

/* Symbolic Constants */

#define MAX_CMDLINE  1024
#define MAX_CMDPARAMS 100
#define MAX_PATH  4096

/*  Type Declarations */

typedef struct tagCMD {
 char *cmdText;
 void (*proc)(void);
} CMD;

/* Function Prototypes */

void exitsys(const char *msg);
void parse_cmdline(void);
char *getls(const char *path, const struct stat *finfo, int linkSpace, int sizeSpace);
int getdigit_count(int val);

void cmd_remove(void);
void cmd_cp(void);
void cmd_clear(void);
void cmd_ls(void);
void cmd_cd(void);

/* Global Definitions */

char g_cmdLine[MAX_CMDLINE];
char *g_cmdParams[MAX_CMDPARAMS];
int g_nParams;
char g_cwd[MAX_PATH];

CMD g_cmds[] = {
 {"rm", cmd_remove },
 {"cp", cmd_cp },
 {"clear", cmd_clear },
 {"ls", cmd_ls},
 {"cd", cmd_cd},
 {"chdir", cmd_cd},

 {NULL, NULL}
};

/* Function Defitinions */

int main(int argc, char *argv[])
{
 int i;

 for (;;) {
  if (getcwd(g_cwd, MAX_PATH) < 0)
   exitsys("cwd");
  
  printf("CSD:%s>", g_cwd);
  gets(g_cmdLine);
  parse_cmdline();
  if (g_nParams == 0)
   continue;
 
  if (!strcmp(g_cmdParams[0], "exit"))
   break;
 
  for (i = 0; g_cmds[i].cmdText != NULL; ++i)
   if (!strcmp(g_cmdParams[0], g_cmds[i].cmdText)) {
    g_cmds[i].proc();
    break;
   }
  if (g_cmds[i].cmdText == NULL)
   printf("%s: command not found\n", g_cmdParams[0]);
 }


 return 0;
}

void parse_cmdline(void)
{
 char *str;

 g_nParams = 0;
 for (str = strtok(g_cmdLine, " \t"); str != NULL; str = strtok(NULL, " \t"))
  g_cmdParams[g_nParams++] = str;
 g_cmdParams[g_nParams] = NULL;
}

void cmd_remove(void)
{
 printf("remove command...\n\n");
}

void cmd_cp(void)
{
 int fds, fdd;
 char *buf;
 struct stat finfo;
 ssize_t n;

 if (g_nParams != 3) {
  printf("invalid cp command!..\n");
  return;
 }

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

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

 if (stat(g_cmdParams[1], &finfo) < 0) {
  perror("stat");
  return;
 }

 if ((buf = malloc(finfo.st_blksize)) == NULL) {
  printf("not enough memory!..\n");
  return;
 }
 
 while ((n = read(fds, buf, finfo.st_blksize)) > 0)
  if (write(fdd, buf, n) < 0) {
   perror("write");
   exit(EXIT_FAILURE);
  }

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

 close(fds);
 close(fdd);

 free(buf);

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

void cmd_clear(void)
{
 if (g_nParams > 1) {
  printf("invalid clear command!..\n");
  return;
 }

 system("clear");
}

void cmd_ls(void)
{
 char *path;
 DIR *dir;
 struct dirent *dire;
 char fpath[MAX_PATH];
 int nfiles;
 struct stat *finfos = NULL;
 struct dirent *dirents = NULL;
 int maxLink, maxSize, maxLinkSpace, maxSizeSpace;
 int i;

 if (g_nParams == 1)
  path = g_cwd;
 else if (g_nParams == 2)
  path = g_cmdParams[1];
 else {
  printf("ls: invalid command syntax\n");
  return;
 }

 if ((dir = opendir(path)) < 0) {
  perror("opendir");
  return;
 }

 for (nfiles = 0; (dire = readdir(dir)) != NULL; ++nfiles) {
 
  if (nfiles % 32 == 0) {
   if ((finfos = realloc(finfos, (nfiles + 32) * sizeof(struct stat))) == NULL)
    exitsys("realloc");
   if ((dirents = realloc(dirents, (nfiles + 32) * sizeof(struct dirent))) == NULL)
    exitsys("realloc");
  }
   
  dirents[nfiles] = *dire;
  sprintf(fpath, "%s/%s", path, dire->d_name);
  if (stat(fpath, &finfos[nfiles]) < 0) {
   perror("stat");
   closedir(dir);
   free(finfos);
   return;
  }
 }

 maxLink = maxSize = 0;
 for (i = 0; i < nfiles; ++i) {
  if (maxLink < finfos[i].st_nlink)
   maxLink = finfos[i].st_nlink;
  if (maxSize < finfos[i].st_size)
   maxSize = finfos[i].st_size;
 }

 maxLinkSpace = getdigit_count(maxLink);
 maxSizeSpace = getdigit_count(maxSize);
 
 for (i = 0; i < nfiles; ++i) {
  sprintf(fpath, "%s/%s", path, dirents[i].d_name);
  printf("%s\n", getls(fpath, &finfos[i], maxLinkSpace, maxSizeSpace));
 }

 free(finfos);
 free(dirents);
  
 closedir(dir);
}

char *getls(const char *path, const struct stat *finfo, int linkSpace, int sizeSpace)
{
 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;
 char *lastSlash;
 
 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", linkSpace, (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", sizeSpace, (long) finfo->st_size);
 buf[index++] = ' ';

 ftime = localtime(&finfo->st_mtime);

 lastSlash = strrchr(path, '/');
 index += sprintf(&buf[index], "%s %d %02d:%02d %s", months[ftime->tm_mon], 
                        ftime->tm_mday,ftime->tm_hour, ftime->tm_min, lastSlash + 1);
 buf[index] = '\0';

 return buf;
}

void cmd_cd(void)
{
 if (g_nParams != 2) {
  printf("cd: invalid command syntax!\n");
  return;
 }

 if (chdir(g_cmdParams[1]) < 0) {
  perror("cmd:");
  return;
 }
}

int getdigit_count(int val)
{
 int count = 0;

 while (val) {
  ++count;
    val /= 10;
 }

 return count;
}

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

bu yazmis oldugumuz shell uygulamasini /etc/passwd icerisinde bir kullanicinin shelli icine yazarsak eger login olan kullanici bizim yazdigimiz shell uygulamasina girer.

test:x:1001:1000:test,,,:/home/test:/bin/csdsh

Derledigimiz uygulamamizi /bin/sh icerisine ekleyoruz. Bu uygulama icin calistirma yetkisi de vermemiz lazim.

Dosyalarin Silinmesi

Dosyayi silmek icin remove ve unlink isimli iki esdeger fonksiyon kullanilabilmektedir. Unlink fonksiyonunu prototipi unistd.h icerisinde, remove fonksiyonunun prototipi stdio.h icerisindedir.her iki fonksiyon da once hardlink sayacini 1 eksiltir. Duruma gore dosyanin inoce bilgilerini siler.Bir dosyayi silebilmek icin yada bir dosyayi yaratabilmek icin o dosyanin icinde bulundugu dizine yazma hakkimizin olmasi gerekmektedir. Ayrica biz baskasinin sahibi oldugu dosyalari silemeyiz. Yani silecegimiz dosyanin userid prosesimizin etkin userid degeri ile ayni olmasi gerekmetedir. Fakat silem islemi icin dosyaya write hakki olmasi gerekmemetedir

#include <unistd.h>
int unlink(const char *pathname);
#include <stdio.h>
int remove(const char *pathname);

Dizinlerdeki x hakki

Dizinlerdeki erisim haklari

Bir dosyaya pathname resolution sirasinda erisebilmek icin yol ifadesinde bulunan butun dizinlere x hakkinin olmasi gerekmektedir. Yani dizinlerde x hakki icinden gecmek anlamina gelmektedir. ornegin biz stat fonksiyonu ile yada diger bir fonksiyonla /home/kaan/ali/x.dat dosyasini belirtmek isteyelim. Bizim bu islemi yapabilmemiz icin home, kaan ve ali dizinine x hakkina sahip olmamiz gerekmektedir. Bir dizine write hakkinin olmasi o disin dosyasinin dizin girisleri uzerinde degisiklikler yapilabilmesi denilmektedir. Yani o dizinden dosya yaratma, dosya silmek,rename islemi yapmak bu haklari gerektirir.
Ornegin biz /a/b/c dizininde bir dosya yaratmak isteyelim. Burada bizim a, b ve c dizinlerinin hepsine x hakkimizin olmasi gerekir. fakat c dizinine w hakkimizin olmasi yeterlidir.Bir dizine read hakkinin olmasi demek o dizinin dizin listesinin goruntulenebilmesi demekdir. Teknik olarak bu kontrol opendir fonksiyonu tarafindan yapilmaktadir. Yine /a/b/c/ dizininin listesini almak istediginizde a/b/c ye x hakkimizin olmasi gerekmektedir. Fakat yalnizca c dizinine read hakki olmasi yeterlidir.
Ilginc bir nokta su olabilir. Asagidaki open fonksiyonunda tum dizinlere x hakkinin oldugunu dusunelim. Dosyaninda gercekten orada var oldugunu kabul edelim. Dosyanin bulundugu dizine okuma hakkimiz olmadigi halde eger dosyaya okuma hakkimiz var ise bu islem basarilidir.
fd = open("/a/b/c/x.dat", O_RDONLY)

Unix/Linux Sistemlerde Proseslerle Islemler

Unix/Linux Sistemlerinde her prosesin sistem genelinde tek olan bir id degeri vardir. Bu sostemlerde proses yaratmak icin fork isimli proses kullanilir. vfork isimli bir program vardir. O da fork turevidir. Fork, bir prosesin ozdes bir kopyasindan olusturuluyor. Fork islemi yapildiginda yeni bir proses kontrol blogu yaratilir ve o ust prosesten kopyalanir. Ust prosesin de bellek alaninin bellek alaninin bir kopyasindan cikartilir. Boylece fork fonksiyonuna tek bir akis girer 2 akis cikar. Ust proses'de alt proses'de ayni programin kodlarini calistirmaya devam eder.


#include <unistd.h>
pid_t fork(void);

Fork fonksiyonundan ust proses alt prosesin id degeri ile cikar. Alt proses ise 0 degeri ile cikar. Suphesiz alt prosesin de bir id'si vardir ancak fork isleminden boyle cikar. Fork fonksiyonunun kendisi de basarisiz olabilir. Bu durumda -1 ile geri doner.

Tipik bir fork kalibi su sekildedir.

if ((pid = fork() == -1){
    perror("fork");
    exit(EXIT_FAILURE)
}
if ( pid != 0 ){
    /*parent*/
}else{
    /*child*/
}

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);
}