Proseslər

§3 Proseslər

3.1 Proseslərin Yaradılması

3.2 Proqramların icra olunması

3.3 Siqnallar

Unix-də icra olunan proqrama PROSES deyirlər. Biz proqramları icra eləmək üçün terminaldan onların adını daxil edirik. Proqram fayllarının üzərində iki dəfə klik etməklə də onları yükləyirik, bu zaman, proqram faylının adı arxa fonda icra olunan terminal proqramına parametr kimi ötürülür və terminal proqramı həmin proqramı yükləyir. İcra olunan proqram faylı (qısa olaraq, proqram) kompilyatorun mətn kodunun kompilyasiyası nəticəsində generasiya etdiyi ikili fayldır və o, sərt diskdə hər hansı qovluqda yerləşir.

Proqram faylını icra etdikdə isə, əməliyyatlar sistemi bu faylı sərt diskdən fiziki yaddaşa - RAM-a köçürür, icra edir və artıq proqram yox, proses adlandırılmağa başlanır.

Sistemdə hal-hazırda yüklənmiş proseslərin siyahısına baxmaq üçün ps əmrindən istifadə edirik. ps əmri hal-hazırda icra olunan proseslərin siyahısını verəcək. Əgər biz sistemdəki bütün proseslərin siyahısına baxmaq istəyiriksə, onda -e seçimindən istifadə etməliyik: ps -e.

Bu zaman ps əmri 1-ci sütunda prosesin identifikasiya nömrəsini, 2-ci sütunda prosesin icra olunduğu terminalı, 3-cüdə icraolunma vaxtını, 4-cü sütunda isə, prosesin icraolunma faylının adını göstərəcək.

Sadə bir proqram tərtib edək, hansı ki, sonsuz dövrə girir və icra edək. Daha sonra, bu proqram icra oluna-oluna başqa bir terminal pəncərəsi açıb ps əmrinin bizim proqram barədə çap elədiyi məlumatlara baxaq. Nümunə proqram kodu aşağıdakı kimi olacaq:


/* prg_3_1.c */

#include <stdio.h>

int main(){

printf("Hey, mən icra olunuram. \n");
/* sonsuz dovre giririk */
for(;;);
}

Proqramı icra edək:


[ferid@fedora progs]$ 
[ferid@fedora progs]$ gcc prg_3_1.c -o prg_3_1
[ferid@fedora progs]$ 
[ferid@fedora progs]$ ./prg_3_1
Hey, mən icra olunuram.

Daha sonra, başqa terminal pəncərəsindən ps əmri ilə bu proqram barəsində məlumatlara baxaq:


[ferid@fedora ~]$ ps -e | grep prg_3_1
31588 pts/9 00:00:20 prg_3_1
[ferid@fedora ~]$

Burada biz yalnız prg_3_1 proqramına aid nəticəyə baxmaq üçün ps-in nəticələrini grep əmrinə ötürürük.

Gördüyümüz kimi, bizim proqramın id nömrəsi 31588-dir. Prosesin id nömrəsi, qısa olaraq, pid - hər bir proses yaradılan zaman ona nüvə tərəfindən təyin edilir və prosesin icrası boyu həmin prosesə birqiymətli müraciət etmək üçün istifadə olunur.

Proqramımızı söndürmək üçün Ctrl-C düymələrini (ControlC düymələri cütünü bir yerdə) basırıq.

Prosesin pid nömrəsini əldə etmək üçün getpid() funksiyasından istifadə olunur. getpid funksiyası unistd.h faylında elan edilib: pid_t getpid(void);

getpid funksiyasından istifadə etməklə, sadə proqram nümunəsinə nəzər salaq:


/* prg_3_2.c */

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

int main(){

pid_t pid;
pid = getpid();

printf("Mənim pid-im %d-dir\n", pid);

/* sonsuz dövre giririk */
for(;;);
}

Nəticəyə baxaq:


[ferid@fedora progs]$ 
[ferid@fedora progs]$ gcc prg_3_2.c -o prg_3_2
[ferid@fedora progs]$ 
[ferid@fedora progs]$ ./prg_3_2
Hey, mən icra olunuram. 
Mənim pid-im 31778-dir

Proqram icraya başlayır, öz pid nömrəsini çap edir və sonsuz dövrə girir. Bu zaman başqa terminal pəncərəsindən ps ilə proqramın pid nömrəsini öyrənib, bizim nəticələr ilə müqaisə edə bilərik:


[ferid@fedora ~]$ 
[ferid@fedora ~]$ ps -e | grep prg_3_2
31778 pts/9 00:03:13 prg_3_2
[ferid@fedora ~]$ 
[ferid@fedora ~]$

Proqramı söndürmək üçün Ctrl-C düymələrini basırıq.


3.1 Proseslərin Yaradılması

Unix-də proseslər nə cür yaradılır?

Yeni prosesin tamamilə 0-dan yaradılması çox mürəkkəb məsələdir. Lakin Unix-də bu məsələ asan yolla həll olunur. Yeni prosesin bütün məlumat strukturlarını tamamilə yenidən yaratmaq əvəzinə, hər hansı mövcud prosesin nüsxəsi yaradılır.

Nəticədə, sistemdə verilmiş prosesin iki nüsxəsi mövcud olur. Biri ilkin proses, digəri isə oxşarı. İlkin və oxşar proseslər bir-birindən heç nə ilə fərqlənmir. Qeyd edək ki, xarici ədəbiyyatlarda ilkin və oxşar proseslər uyğun olaraq ata və oğul proseslər kimi adlandırılır. Biz də bu adlandırmaya riayət edəcəyik.

Sistemdə hər hansı prosesin oxşarını yaratmaq üçün fork() sistem çağrısından istifadə olunur. fork() funksiyası unistd.h başlıq faylında təyin edilib və elanı aşağıdakı kimidir:


pid_t fork(void);

fork() funksiyası heç bir parametr qəbul etmir və çağırıldığı prosesin nüsxəsini yaradır. Hər iki proses fork() çağırıldığı yerdən sonrakı hissədən icra olunmağa başlayır. Fork nəticə olaraq ata prosesdə oğul prosesin pid nömrəsini, oğul prosesdə isə 0 qiymətini qaytarır. Bu yolla ata və oğul prosesləri bir-birindən fərqləndirilir.




Gəlin, bütün bu dediklərimizi proqram nümunələri ilə test edək.

Sadə bir proqram tərtib edək, hansı ki, hər hansı məlumat çap edir.


/* prg_3_3.c */

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


int main(){

printf("Hey, salam \n");

printf("Hey, necəsən? \n");
}

Proqramı icra edək:


[ferid@fedora progs]$ 
[ferid@fedora progs]$ gcc prg_3_3.c -o prg_3_3
[ferid@fedora progs]$ 
[ferid@fedora progs]$ ./prg_3_3
Hey, salam 
Hey, necəsən?
[ferid@fedora progs]$ 
[ferid@fedora progs]$

İndi isə, baxdığımız prosesin fork() funksiyası vasitəsilə oxşarını yaradaq:


/* prg_3_4.c */

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


int main(){

printf("Hey, salam \n");

/* bu hissədə prosesin nüsxəsi yaradılır və sonrakı proqram kodu
artıq ayrı-ayrı proseslərdə icra olunur.
*/

fork();

printf("Hey, necəsən? \n");
}

Proqramı icra edək:


[ferid@fedora progs]$ 
[ferid@fedora progs]$ gcc prg_3_4.c -o prg_3_4
[ferid@fedora progs]$ 
[ferid@fedora progs]$ ./prg_3_4
Hey, salam 
Hey, necəsən? 
Hey, necəsən? 
[ferid@fedora progs]$ 
[ferid@fedora progs]$

Gördüyümüz kimi, bu zaman "Hey, necəsən?" sətri iki dəfə çap olundu. Səbəb isə odur ki, əvvəlcə "Hey, salam" sətri çap olundu, daha sonra, fork() funksiyası çağırıldı. Nəticədə eyni prosesin iki nüsxəsi yarandı və hər iki proses fork()-dan sonrakı yerdən icra olunmağa başladı. printf ("Hey, necəsən? \n"); proqram kodu iki müxtəlif prosesdə icra olunduğundan, bu sətrin iki dəfə çap olunduğunu görürük. Əgər fork()-u proqramın lap əvvəlində çağırsaydıq, onda bütün proqram kodu iki müxtəlif prosesdə icra olunardı və biz hər iki sətrin iki dəfə çap olunduğunu görərdik.


/* prg_3_5.c */

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


int main(){

fork();

printf("Hey, salam \n");

printf("Hey, necəsən? \n");
}

Proqramı icra edək:


[ferid@fedora progs]$ 
[ferid@fedora progs]$ gcc prg_3_5.c -o prg_3_5
[ferid@fedora progs]$ 
[ferid@fedora progs]$ ./prg_3_5
Hey, salam 
Hey, necəsən? 
Hey, salam 
Hey, necəsən? 
[ferid@fedora progs]$ 
[ferid@fedora progs]$

Gördüyümüz kimi, fork()-dan sonra gələn proqram kodu iki dəfə icra olundu.

İndi isə, fork()-un qaytardığı nəticələri yoxlamaqla, ata və oğul proseslərini bir-birindən fərqləndirməyə çalışaq.


/* prg_3_6.c */

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


int main(){

int k;

printf("Hey, salam \n");

k = fork();

if (k==0){ 
printf("Mən oğul prosesəm \n");
}
else{ 
printf("Mən ata prosesəm \n");
}
}

Nəticəyə baxaq:


[ferid@fedora progs]$ 
[ferid@fedora progs]$ gcc prg_3_6.c -o prg_3_6
[ferid@fedora progs]$ 
[ferid@fedora progs]$ ./prg_3_6
Hey, salam 
Mən ata prosesəm 
Mən oğul prosesəm 
[ferid@fedora progs]$ 
[ferid@fedora progs]$ 

Nəticədən görürük ki, "Mən ata prosesəm" və Mən oğul prosesəm" sətirlərinin fork()-dan sonra gəlməsinə baxmayaraq, yalnız bir dəfə çap olundu.

İzahı:

fork() çağırılan zaman prosesin nüsxəsi yaradılır və hər iki proses fork()-dan sonrakı koddan (if operatoru) icra olunmağa başlayır. Ata prosesdə fork() nəticə olaraq oğulun id nömrəsini, oğul prosesdə isə 0 qiymətini qaytarır.fork()-dan sonra gələn proqram kodunda əvvəlcə if operatoru vasitəsilə k-nın qiyməti yoxlanılır. Əgər k-ın qiyməti 0-dırsa, deməli proses oğul prosesdir və buraya oğul prosesdə icra olunmalı proqram kodu yerləşdirilməlidir, əks halda isə, proses ata prosesdir və bu hissəyə ata prosesdə icra olunmalı proqram kodu yerləşdirilməlidir.

Bu qaydadan istifadə etməklə, biz sistemdə istədiyimiz sayda yeni proses yarada bilərik. Nəticədə sistemdə paralel icraolunan bir neçə proses yaranar və biz onlardan hər birini bu və ya digər məqsədlər üçün istifadə edə bilərik.


3.2 Proqramların icra olunması

Proqramın icraolunan faylı (kompilyatorun mətn faylından yaratdığı fayl) bir neçə hissədən ibarət olur. Bu hissələrdən ən əsası məlumat və kod hissələridir. Proqramın ikili faylını icra etmək üçün onun mətn, kod və digər hissələrini prosesin yaddaş sahəsinə yükləmək lazımdır. Bütün bu əməliyyatları yerinə yetirmək üçün Unix-də execve() sistem çağrısından istifadə edilir. execve() verilmiş proqram faylını daimi yaddaşdan oxuyur və çağırıldığı prosesin yaddaş sahəsinə köçürür.

execve() funksiyası unistd.h başlıq faylında təyin olunmuşdur və elanı aşağıdakı kimidir:


int execve(const char *filename, char *const argv[], char *const envp[]);

Burada filename icraolunmalı proqram faylının adını (yolunu) bildirir, argv və envp dəyişənləri isə, uyğun olaraq proqrama ötürülən parametrlər və mühit dəyişənləridir. argv cərgəsinin ilk elementi də filename dəyişəni kimi proqramın adını özündə saxlamalıdır. Sadəlik xətrinə envp dəyişəninin qiyməti olaraq NULL qiymətindən istifadə edəcəyik.

Gəlin, execve() funksiyasından istifadə etməklə, bir proqramdan başqa bir proqramı yükləyək.

Əvvəlcə başqa proqram vasitəsilə yükləyəcəyimiz sadə proqramı tərtib edək:


/* prg_3_7.c */

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


int main(){

printf("Mən sadə proqramam \n");
}

Proqramı icra edək:


[ferid@fedora progs]$ 
[ferid@fedora progs]$ ./prg_3_7
Mən sadə proqramam 
[ferid@fedora progs]$ 
[ferid@fedora progs]$

İndi isə, başqa bir proqram tərtib edək, hansı ki, execve() funksiyası vasitəsilə prg_3_7 proqramını icra edəcək.

Kod aşağıdakı kimi olar:


/* prg_3_8.c */

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

int main(){

char *ad, *argv[2];
int k;

ad = malloc(100);
memset(ad,'\0',100);

argv[0] = malloc(100);
memset(argv[0],'\0',100);

printf("Yüklənməli proqramın adını daxil edin: \n");

k=read(1,ad,100);

/* sonuncu simvol \n olduğuna görə onu silirik */

k--;
*(ad+k)='\0';

strcpy(argv[0],ad);
argv[1] = NULL;

printf("%s proqramını yükləyirik:\n",ad);

execve(ad, argv, NULL);
}

Proqramı icra edək:


[ferid@fedora progs]$ 
[ferid@fedora progs]$ gcc prg_3_8.c -o prg_3_8
[ferid@fedora progs]$ 
[ferid@fedora progs]$ ./prg_3_8
Yüklənməli proqramın adını daxil edin: 
prg_3_7
prg_3_7 proqramını yükləyirik:
Mən sadə proqramam 
[ferid@fedora progs]$ 
[ferid@fedora progs]$

İzahı:

Əvvəlcə adargv dəyişənləri üçün yaddaşda yer ayırırıq. Daha sonra, read vasitəsilə istifadəçinin daxil etdiyi sətri ad dəyişəninə yazırıq. Bu zaman ad buferinin sonuncu simvolu yeni sətir simvolu - \n olduğundan onu silirik.

Daha sonra isə, execve ilə istifadəçinin daxil etdiyi proqram faylını icra edirik.

Eyni proqramdan istifadə edib istənilən digər proqramı icra etmək olar. Misal üçün, əgər icra olunmaq üçün ls əmrinin proqram faylını daxil etsəydik, onda ls əmri icra olunardı, aşağıdakı kimi:


[ferid@fedora progs]$ ./prg_3_8
Yüklənməli proqramın adını daxil edin: 
/bin/ls
/bin/ls proqramını yükləyirik:
fayl1 prg_2_3 prg_2_5.c~ prg_2_7 prg_3_2.c prg_3_4.c prg_3_7 qov
pr_3_8 prg_2_3.c prg_2_5.o prg_2_7.c prg_3_2.c~ prg_3_5 prg_3_7.c test
prg_2_1 prg_2_4 prg_2_5_tmp.c prg_2_7.c~ prg_3_3 prg_3_5.c prg_3_7.c~ test1
prg_2_1.c prg_2_4.c prg_2_6 prg_3_1 prg_3_3.c prg_3_6 prg_3_8 test2
prg_2_2 prg_2_5 prg_2_6.c prg_3_1.c prg_3_3.c~ prg_3_6.c prg_3_8.c test4
prg_2_2.c prg_2_5.c prg_2_6.c~ prg_3_2 prg_3_4 prg_3_6.c~ prg_3_8.c~
[ferid@fedora progs]$ 
[ferid@fedora progs]$

Unix-də adətən bir çox əmrlərin proqram kodları /bin, /usr/bin, /usr/sbin qovluqlarında yerləşir.

Baxdığımız proqram nümunəsindən görürük ki, execve funksiyası ilə hər hansı proqramı icra edərkən, həmin proqram cari prosesin yaddaş sahəsinə yüklənir. Lakin praktikada isə, əsas proses yeni proqramı icra etdikdən sonra başqa işlər görməli olur. Bu zaman əvvəlcə fork() funksiyası vasitəsilə cari prosesdən yenisi yaradılır, daha sonra isə ,execve() funksiyası ilə yeni yaradılmış prosesin yaddaş sahəsinə tələb olunan proqram faylı yüklənir və icra olunur.

Yuxarıda daxil etdiyimiz prg_3_8.c-in fork - exec modelinə baxaq:


/* prg_3_9.c */

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

int main(){

char *ad, *argv[2];
int k;

ad = malloc(100);
memset(ad,'\0',100);

argv[0] = malloc(100);
memset(argv[0],'\0',100);

printf("Yüklənməli proqramın adını daxil edin: \n");

k=read(1,ad,100);

/* sonuncu simvol \n olduğuna görə onu silirik */

k--;
*(ad+k)='\0';

strcpy(argv[0],ad);
argv[1] = NULL;


if (fork()==0){ /* oğul proses */

printf("%s proqramını yükləyirik:\n",ad);
execve(ad, argv, NULL); 

}
else {
/* ata proses */
printf("Mən ata prosesəm  \n");
}
}

Proqramı icra edək:


[ferid@fedora progs]$ 
[ferid@fedora progs]$ gcc prg_3_9.c -o prg_3_9
[ferid@fedora progs]$ 
[ferid@fedora progs]$ 
[ferid@fedora progs]$ ./prg_3_9
Yüklənməli proqramın adını daxil edin: 
prg_3_7
Mən ata prosesəm 
prg_3_7 proqramını yükləyirik:
[ferid@fedora progs]$ Mən sadə proqramam 

[ferid@fedora progs]$ 
[ferid@fedora progs]$

Mən nəticəni aldığım kimi daxil etmişəm. Nəticədə bir balaca qarma-qarışıqlıq hiss olunur. Buna səbəb odur ki, fork() funksiyası icra olunduqdan sonra yaranan oğul proses ata-dan ayrı müstəqil proses kimi, paralel icra olunur və atanın, ya oğulun - hansının daha əvvəl icrasını bərpa etməsini demək çətindir. Burada bəzi məqamları diqqətinizə çatdırmaq istəyirəm. Əgər oğul proses icrasını bitirirsə, onda o, proseslər siyahısından silinməyəcək. Bu cür proseslərə zombi proseslər deyilir. Oğul prosesi normal bitirmək üçün ata proses waitpid() funksiyası ilə onun bitməsini gözləməlidir.

wait() funksiyası sys/wait.h başlıq faylında elan olunmuşdur, aşağıdakı kimi:

pid_t wait(int *status);

wait() funksiyası ata proses tərəfindən oğul prosesin statusunun dəyişməsi barədə xəbərdar olmaq üçün çağırılır. Bu zaman ata proses oğul prosesin statusunda hər hansı dəyişiklik baş verənə qədər gözləyəcək. Status dəyişikliyi deyərkən, oğul prosesin icrasını başa çatdırması, hər hansı digər prosesdən və ya nüvədən siqnal qəbul etməsi, öz icrasını bərpa etməsi və s. nəzərdə tutulur. Oğul prosesin statusu barədə məlumat status dəyişəninə yerləşdirilir.

Yuxarıdakı proqramın wait() funksiyasından istifadə olunan halına baxaq:


/* prg_3_10.c */

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

int main(){

char *ad, *argv[2];
int k;

ad = malloc(100);
memset(ad,'\0',100);

argv[0] = malloc(100);
memset(argv[0],'\0',100);

printf("Yüklənməli proqramın adını daxil edin: \n");

k=read(1,ad,100);

/* sonuncu simvol \n olduğuna görə onu silirik */

k--;
*(ad+k)='\0';

strcpy(argv[0],ad);
argv[1] = NULL;


if (fork()==0){ /* oğul proses */

printf("%s proqramını yükləyirik:\n",ad);
execve(ad, argv, NULL); 

}
else {
/* ata proses */
int status;

wait(&status);
printf("Oğul proses sona çatdı \n");
}
}

Proqramı icra edək:


[ferid@fedora progs]$ 
[ferid@fedora progs]$ gcc prg_3_10.c -o prg_3_10
[ferid@fedora progs]$ 
[ferid@fedora progs]$ ./prg_3_10
Yüklənməli proqramın adını daxil edin: 
prg_3_7
prg_3_7 proqramını yükləyirik:
Mən sadə proqramam 
Oğul proses sona çatdı
[ferid@fedora progs]$ 
[ferid@fedora progs]$

3.3 Siqnallar

Siqnal nədir? Siqnal Unix-də proseslərarası əlaqə vasitələrindən biridir və müasir nüvələrdə çox uğurla tətbiq olunur. Siqnal vasitəsilə nüvə və proseslər bir-biri ilə əlaqə yarada, hər hansı hadisənin baş verməsi haqda biri digərini məlumatlandıra bilər. Başqa bir proses və ya nüvə tərəfindən qəbul olunmuş siqnala cavab reaksiyası da müxtəlif ola bilər.

Ümumiyyətlə, Unix-də proses qəbul elədiyi siqnala 3 cür cavab verə bilər:
1. Proses siqnala əvvəlcədən nəzərdə tutulmuş cavab verir.
2. Proses siqnalı inkar edir.
3. Proses siqnalı inkar edə bilmir və məcburi cavab reaksiyasını yerinə yetirir.
Adətən 3-cü qrupa aid siqnallar nüvə tərəfindən prosesə göndərilən siqnallardır, hansı ki, çox vaxt prosesin xətası zamanı nüvə tərəfindən onu söndürmək üçün göndərilir və bu siqnal inkar oluna bilməz.

Aşağıda biz bir prosesdən digərinə siqnal göndərəcəyik, mənsəb prosesdə bu siqnalı qəbul edib ona reaksiya verəcəyik. Bunun üçün iki müxtəlif terminal pəncərəsindən istifadə edəcəyik. Siqnal göndərən və siqnalı qəbul edən prosesləri uyğun olaraq proca və procb ilə işarə edək. Hər iki proqramın mənbə kodu aşağıdakı kimi olar.


/* prg_3_11.c
proca siqnal göndərən */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>

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

pid = atoi(argv[1]);

kill(pid, SIGINT);
return 0;
}

/* prg_3_12.c
procb siqnal qəbul edən */

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


int main()
{

printf("a prosesi pid =%d\n",getpid());
for(;;); /* sonsuz dövrə giririk */

return 0;
}

İzahı: Əvvəlcə hər iki proqramı kompilyasiya edirik, daha sonra iki terminal pəncərəsi açırıq. Birinci terminal pəncərəsindən procb-ni yükləyirik. procb icra olan zaman, ilk öncə öz proses identifikatorunu çap edəcək, daha sonra, sonsuz dövrə keçəcək. Digər terminal pəncərəsindən isə proca-nı çağırırıq və ona parametr kimi procb-nin pid nömrəsini veririk. proca icra olan kimi procb-yə SIGINT signalı göndərəcək və nəticədə procb icrasını başa çatdıracaq.

Unix-də bir prosesdən digərinə siqnal göndərmək üçün kill() funksiyasından istifadə olunur. kill() funksiyası signal.h başlıq faylında təyin olunub və elanı aşağıdakı kimidir:


int kill(pid_t pid, int sig);

İki parametr qəbul edir: siqnal göndərilən prosesin pid nömrəsi və göndərilən siqnalın nömrəsi.

Qeyd elədik ki, siqnalı qəbul edən proses bu siqnala 3 cür cavab verə bilər. Əgər əvvəlcədən hər hansı cavab təyin olunmayıbsa, onda susmaya görə (default) cavab reaksiyasını verər. Adətən bu prosesin sona çatmasıdır. İndi isə, gəlin, procb-də qəbul olunan siqnala cavab verən funksiya təyin edək. Nəticədə hər hansı mənbədən siqnal alınan zaman proses tərəfindən bu siqnala cavab olaraq, bizim təyin etdiymiz funksiya yerinə yetiriləcək.

procb-nin yeni versiyası aşağıdakı kimi olacaq:


/* prg_3_13.c
procb siqnal qəbul edən
*/

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

void siqnala_cavab(int siqnal)
{
printf("Siqnal qəbul olundu\n");
}

int main()
{
printf("b prosesi pid nömrə=%d\n",getpid());
siqnal(SIGINT, siqnala_cavab);
for(;;); /* sonsuz dövrə giririk */
return 0;
}

Əgər bu dəfə bayaqkı prosesi təkrar etsək, artıq proca ilə procb-yə siqnal göndərən zaman procb sönməyəcək, əksinə, ekranda "Siqnal qəbul olundu" sətrini çap edəcək. Bu zaman maraqlı bir eksperiment aparmaq olar. procb icra oluna-oluna CTRL^C düymələri ilə onu söndürməyə çalışın və nəyin baş verdiyini müşahidə edin.

İzahı: Baxdığımız proqramda biz sadə bir funksiya təyin etdik, hansı ki, yerinə yetiriləndə ekranda "Siqnal qəbul olundu" sətrini çap edir. Daha sonra, siqnal() funksiyası vasitəsilə siqnala_cavab funksiyasını SIGINT siqnalına pərçimlədik. Yəni bundan sonra, proqram SIGINT tipli siqnal qəbul etdikdə, siqnala_cavab funksiyasını icra edəcək. signal() funksiyası signal.h başlıq faylında təyin olunub və elanı aşağıdakı kimidir:


sighandler_t signal(int signum, sighandler_t handler);

Burada signum qəbul edilən siqnalı, handler isə, həmin siqnal qəbul edilən zaman çağırılmalı olan funksiyanı bildirir.


«« Fayllar    Proseslər    Proseslər arası əlaqə »»
Share


Muəllif: Əhməd Sadıxov