Linux'un birçok programında ve man sayfalarında kod kullanıldığını gördüm fork()
. Neden kullanmamız gerekiyor fork()
ve amacı nedir?
Yanıtlar:
fork()
Unix'te yeni süreçleri nasıl yarattığınızdır. Aradığınızda fork
, kendi adres alanı olan kendi işleminizin bir kopyasını oluşturuyorsunuz . Bu, birden çok görevin, her biri makinenin tam belleğine sahipmiş gibi birbirinden bağımsız olarak çalışmasına izin verir.
İşte bazı örnek kullanımlar fork
:
fork
komut satırından çağırmak programları çalıştırmak için.fork
, her biri istekleri kendi adres alanında işleyen birden çok sunucu işlemi oluşturmak için kullanır . Biri ölürse veya bellek sızdırırsa, diğerleri etkilenmez, bu nedenle hata toleransı için bir mekanizma olarak işlev görür.fork
her sayfayı ayrı bir işlem içinde işlemek için kullanır . Bu, bir sayfadaki istemci tarafı kodunun tüm tarayıcınızı kapatmasını önleyecektir.fork
bazı paralel programlarda ( MPI kullanılarak yazılanlar gibi) süreçleri ortaya çıkarmak için kullanılır . Bunun, kendi adres alanı olmayan ve bir işlem içinde var olan iş parçacıkları kullanmaktan farklı olduğunu unutmayın .fork
alt işlemleri başlatmak için dolaylı olarak kullanır . Örneğin, subprocess.Popen
Python'daki gibi bir komutu her kullandığınızda , fork
bir çocuk süreç olursunuz ve çıktısını okursunuz. Bu, programların birlikte çalışmasını sağlar.fork
Bir kabukta tipik kullanımı şöyle görünebilir:
int child_process_id = fork();
if (child_process_id) {
// Fork returns a valid pid in the parent process. Parent executes this.
// wait for the child process to complete
waitpid(child_process_id, ...); // omitted extra args for brevity
// child process finished!
} else {
// Fork returns 0 in the child process. Child executes this.
// new argv array for the child process
const char *argv[] = {"arg1", "arg2", "arg3", NULL};
// now start executing some other program
exec("/path/to/a/program", argv);
}
Kabuk, kullanan bir çocuk süreci oluşturur exec
ve tamamlanmasını bekler, ardından kendi yürütmesine devam eder. Bu şekilde çatal kullanmak zorunda olmadığınızı unutmayın. Paralel bir programın yapabileceği gibi her zaman çok sayıda alt süreç üretebilirsiniz ve her biri aynı anda bir programı çalıştırabilir. Temel olarak, bir Unix sisteminde yeni süreçler oluşturduğunuzda, kullanıyorsunuzdur fork()
. Windows eşdeğeri için bir göz atın CreateProcess
.
Daha fazla örnek ve daha uzun bir açıklama istiyorsanız, Wikipedia'nın iyi bir özeti var. İşte modern işletim sistemlerinde süreçlerin, iş parçacığının ve eşzamanlılığın nasıl çalıştığına dair bazı slaytlar burada.
fork()
UNIX içinde yeni bir süreç oluşturmak, ancak bilgiçlik olmak, diğer en az bir tane yoludur: posix_spawn()
.
fork (), Unix'in yeni süreçler yaratmasıdır. Fork () adını verdiğiniz noktada, işleminiz klonlanır ve buradan iki farklı işlem yürütülmeye devam eder. Bunlardan biri, çocuk, fork () dönüş 0'a sahip olacaktır. Diğeri, ebeveyn, fork (), çocuğun PID'sini (süreç kimliği) döndürür.
Örneğin, bir kabuğa aşağıdakini yazarsanız, kabuk programı fork () 'u çağırır ve ardından altta geçirdiğiniz komutu (bu durumda telnetd) yürütür, üst öğe de istemi tekrar görüntüler. arka plan işleminin PID'sini belirten bir mesaj olarak.
$ telnetd &
Yeni süreçler yaratma nedeninize gelince, işletim sisteminiz aynı anda birçok şeyi bu şekilde yapabilir. Bu yüzden bir programı çalıştırabilir ve çalışırken başka bir pencereye geçip başka bir şey yapabilirsiniz.
fork (), alt süreç oluşturmak için kullanılır. Bir fork () işlevi çağrıldığında, yeni bir süreç ortaya çıkar ve fork () işlev çağrısı çocuk ve ebeveyn için farklı bir değer döndürür.
Dönüş değeri 0 ise, alt süreç olduğunuzu bilirsiniz ve dönüş değeri bir sayı ise (bu, alt süreç kimliği olur), ebeveyn olduğunuzu bilirsiniz. (ve negatif bir sayı ise, çatal başarısız oldu ve hiçbir alt süreç oluşturulmadı)
fork (), temelde bu işlevi çağırdığınız süreç için bir alt süreç oluşturmak için kullanılır. Bir fork () çağırdığınızda, çocuk kimliği için sıfır döndürür.
pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process
bununla ebeveyn ve çocuk için farklı eylemler sağlayabilir ve çoklu okuma özelliğini kullanabilirsiniz.
fork (), ebeveyn ile aynı olan yeni bir çocuk süreç yaratacaktır. Dolayısıyla, bundan sonra kodda çalıştırdığınız her şey her iki işlem tarafından da çalıştırılacaktır - örneğin bir sunucunuz varsa ve birden çok isteği işlemek istiyorsanız çok kullanışlıdır.
Uygulama yazıyorsanız, muhtemelen günlük programlamada fork kullanmanıza gerek yoktur.
Programınızın bir görevi yerine getirmek için başka bir programı başlatmasını isteseniz bile, C ve perl'deki "system" gibi arka planda fork kullanan daha basit arayüzler vardır.
Örneğin, uygulamanızın sizin için bir hesaplama yapması için bc gibi başka bir programı başlatmasını istiyorsanız, onu çalıştırmak için 'sistem'i kullanabilirsiniz. Sistem yeni bir süreç yaratmak için bir 'çatal' yapar, sonra bu süreci bc'ye dönüştürmek için bir 'exec' yapar. Bc tamamlandığında, sistem kontrolü programınıza döndürür.
Diğer programları eşzamansız olarak da çalıştırabilirsiniz, ancak nasıl olduğunu hatırlayamıyorum.
Sunucu, kabuk, virüs veya işletim sistemi yazıyorsanız, fork kullanmak isteyebilirsiniz.
system()
. Okuyordum fork()
çünkü C kodumun bir python betiği çalıştırmasını istiyorum.
Sistem çağrısı fork () süreçleri oluşturmak için kullanılır. Hiçbir bağımsız değişken almaz ve bir işlem kimliği döndürür. Fork () işlevinin amacı, arayanın çocuk süreci haline gelen yeni bir süreç yaratmaktır. Yeni bir çocuk süreç oluşturulduktan sonra, her iki süreç fork () sistem çağrısını takip eden sonraki talimatı yürütecektir. Bu nedenle ebeveyni çocuktan ayırmalıyız. Bu, fork () 'un döndürülen değerini test ederek yapılabilir:
Fork () negatif bir değer döndürürse, bir alt sürecin yaratılması başarısız olmuştur. fork (), yeni oluşturulan alt işleme sıfır döndürür. fork (), üst sürecin pozitif bir değeri olan alt sürecin süreç kimliğini döndürür. Döndürülen işlem kimliği, sys / types.h dosyasında tanımlanan pid_t tipindedir. Normalde işlem kimliği bir tamsayıdır. Ayrıca, bir işlem bu işleme atanan işlem kimliğini almak için getpid () işlevini kullanabilir. Bu nedenle, fork () 'a sistem çağrısından sonra, basit bir test hangi sürecin alt öğe olduğunu söyleyebilir. Unix'in ebeveynin adres alanının tam bir kopyasını oluşturup çocuğa vereceğini lütfen unutmayın. Bu nedenle, üst ve alt süreçlerin ayrı adres alanları vardır.
Yukarıdaki noktaları netleştirmek için bir örnekle anlayalım. Bu örnek, ebeveyn ve alt süreçleri ayırt etmez.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#define MAX_COUNT 200
#define BUF_SIZE 100
void main(void)
{
pid_t pid;
int i;
char buf[BUF_SIZE];
fork();
pid = getpid();
for (i = 1; i <= MAX_COUNT; i++) {
sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
write(1, buf, strlen(buf));
}
}
Yukarıdaki programın fork () çağrısı noktasına kadar çalıştığını varsayalım.
Fork () çağrısı başarılı bir şekilde yürütülürse, Unix, biri ebeveyn, diğeri alt öğe için olmak üzere iki özdeş adres alanı kopyası oluşturur. Her iki süreç fork () çağrısını izleyen sonraki ifadede yürütülmeye başlayacaktır. Bu durumda, her iki işlem de atamada yürütülmeye başlayacaktır.
pid = .....;
Her iki işlem de fork () sistem çağrısından hemen sonra yürütülmeye başlar. Her iki işlem de aynı ancak ayrı adres alanlarına sahip olduğundan, fork () çağrısından önce başlatılan bu değişkenler her iki adres alanında da aynı değerlere sahiptir. Her işlemin kendi adres alanı olduğundan, herhangi bir değişiklik diğerlerinden bağımsız olacaktır. Başka bir deyişle, üst öğe değişkeninin değerini değiştirirse, değişiklik yalnızca üst sürecin adres alanındaki değişkeni etkileyecektir. Fork () çağrıları tarafından oluşturulan diğer adres alanları, aynı değişken adlarına sahip olsalar bile etkilenmeyecektir.
Printf yerine write kullanmanın nedeni nedir? Bunun nedeni, printf () 'nin "arabelleğe alınmış" olmasıdır, yani printf () bir sürecin çıktısını birlikte gruplayacaktır. Üst süreç için çıktıyı arabelleğe alırken, çocuk aynı zamanda arabelleğe alınacak bazı bilgileri yazdırmak için printf kullanabilir. Sonuç olarak çıktı ekrana hemen gönderilmeyeceğinden beklenen sonucun doğru sırasını alamayabilirsiniz. Daha kötüsü, iki işlemin çıktıları tuhaf şekillerde karıştırılabilir. Bu sorunun üstesinden gelmek için, "arabelleğe alınmamış" yazmayı kullanmayı düşünebilirsiniz.
Bu programı çalıştırırsanız, ekranda aşağıdakileri görebilirsiniz:
................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
................
İşlem Kimliği 3456, ebeveyne veya çocuğa atanan olabilir. Bu süreçlerin eşzamanlı olarak çalıştırılmaları nedeniyle, çıktı hatları oldukça tahmin edilemez bir şekilde birbirine karıştırılmıştır. Ayrıca, bu satırların sırası CPU planlayıcısı tarafından belirlenir. Bu nedenle, bu programı tekrar çalıştırırsanız, tamamen farklı bir sonuç elde edebilirsiniz.
Çoklu işlem, bilgi işlemin merkezidir. Örneğin, IE'niz veya Firefox'unuz siz internette gezinirken sizin için bir dosya indirme işlemi oluşturabilir. Veya, bir kelime işlemcide bir belge yazdırırken, yine de farklı sayfalara bakabilir ve yine de bazı düzenlemeler yapabilirsiniz.
Fork (), her vücudun yazdığı gibi yeni süreçler oluşturmak için kullanılır.
İşte ikili ağaç şeklinde süreçler oluşturan kodum ....... İkili ağaçta süreç oluşturmak istediğiniz seviyelerin sayısını taramanızı isteyecek
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
int t1,t2,p,i,n,ab;
p=getpid();
printf("enter the number of levels\n");fflush(stdout);
scanf("%d",&n);
printf("root %d\n",p);fflush(stdout);
for(i=1;i<n;i++)
{
t1=fork();
if(t1!=0)
t2=fork();
if(t1!=0 && t2!=0)
break;
printf("child pid %d parent pid %d\n",getpid(),getppid());fflush(stdout);
}
waitpid(t1,&ab,0);
waitpid(t2,&ab,0);
return 0;
}
ÇIKTI
enter the number of levels
3
root 20665
child pid 20670 parent pid 20665
child pid 20669 parent pid 20665
child pid 20672 parent pid 20670
child pid 20671 parent pid 20670
child pid 20674 parent pid 20669
child pid 20673 parent pid 20669
İlk önce fork () sistem çağrısının ne olduğunu anlamalı. Açıklamama izin ver
fork () sistem çağrısı, üst işlemin tam olarak kopyasını oluşturur, Üst yığın, yığın, başlatılmış veriler, başlatılmamış verilerin kopyasını yapar ve kodu salt okunur modda üst işlemle paylaşır.
Fork sistem çağrısı, hafızayı yazma üzerine kopyalama esasına göre kopyalar, yani kopyalama gereksinimi olduğunda çocuğun sanal hafıza sayfasında yaptığı anlamına gelir.
Şimdi çatalın Amacı ():
fork()
bir çocuk süreci ortaya çıkarmak için kullanılır. Tipik olarak, iş parçacığı gibi benzer durumlarda kullanılır, ancak farklılıklar vardır. İpliklerden farklı olarak, fork()
tamamen ayrı süreçler yaratır, yani çocuk ve ebeveyn fork()
, çağrılan noktada birbirlerinin doğrudan kopyaları iken , tamamen ayrıdırlar, hiçbiri diğerinin hafıza alanına erişemez (normal sorunlara gitmeden) başka bir programın belleğine erişmeye gidersiniz).
fork()
hala bazı sunucu uygulamaları tarafından, çoğunlukla kullanıcı isteklerini işlemeden önce izinleri bırakan bir * NIX makinesinde root olarak çalışan uygulamalar tarafından kullanılmaktadır. Hala başka kullanım alanları var, ancak çoğu insan şimdi çoklu iş parçacığına geçti.
Fork () ile yeni bir işlemi başlatmak için sadece bir exec () işlevine sahip olmanın ardındaki mantık , unix yığın değişimindeki benzer bir soruya yanıt olarak açıklanmaktadır .
Esasen, fork mevcut süreci kopyaladığından, bir süreç için çeşitli olası seçeneklerin tümü varsayılan olarak belirlenir, bu nedenle programcı bunları tedarik edemez.
Windows işletim sisteminde, bunun aksine, programcılar ÇOK daha karmaşık olan ve yeni sürecin parametrelerini tanımlamak için çok çeşitli bir yapı oluşturmayı gerektiren CreateProcess işlevini kullanmak zorundadır.
Yani, özetlemek gerekirse, çatallamanın nedeni (yürütmeye karşı) yeni süreçler yaratmada basitliktir.
Fork () sistem çağrısı bir alt süreç oluşturmak için kullanın. Üst sürecin tam bir kopyasıdır. Fork, yığın bölümünü, yığın bölümünü, veri bölümünü, ortam değişkenini, üst öğeden komut satırı argümanlarını kopyalar.
Çatal () işlev Çağrılış mevcut işlem çoğaltarak yeni bir işlem oluşturmak için kullanılır. Bu işlevin çağrıldığı mevcut süreç ana süreç olur ve yeni oluşturulan süreç alt süreç olur. Daha önce belirtildiği gibi, çocuk ebeveynin kopyasıdır, ancak bunun bazı istisnaları vardır.
Çocuğun, işletim sisteminde çalışan diğer tüm işlemler gibi benzersiz bir PID'si vardır.
Çocuk, kendisini oluşturan sürecin PID'si ile aynı olan bir üst süreç kimliğine sahiptir
.
Alt süreçte kaynak kullanımı ve CPU zaman sayaçları sıfırlanır.
Çocukta bekleyen sinyaller kümesi boş.
Çocuk, ebeveyninden herhangi bir zamanlayıcı devralmaz
Misal :
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
int var_glb; /* A global variable*/
int main(void)
{
pid_t childPID;
int var_lcl = 0;
childPID = fork();
if(childPID >= 0) // fork was successful
{
if(childPID == 0) // child process
{
var_lcl++;
var_glb++;
printf("\n Child Process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
}
else //Parent process
{
var_lcl = 10;
var_glb = 20;
printf("\n Parent process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
}
}
else // fork failed
{
printf("\n Fork failed, quitting!!!!!!\n");
return 1;
}
return 0;
}
Şimdi, yukarıdaki kod derlendiğinde ve çalıştırıldığında:
$ ./fork
Parent process :: var_lcl = [10], var_glb[20]
Child Process :: var_lcl = [1], var_glb[1]