Linux Fu: Fork İçin Tuhaf Bir Kullanım()


Eğer bir Star Trek hayranıysanız, muhtemelen şu ifadeyi hatırlayacaksınız: “Bir yıldız gemisinde işlerin neden yürüdüğünü öğrenmelisin.” Gerçek şu ki, çoğu bölümde, başka bir geminin konsolunu nasıl geçersiz kılacağını veya barut yapmayı bilmek pek işe yaramadı, ama oğlum, işe yaradığında gerçekten günü kurtardı. Linux buna çok benzer. Muhtemelen çok sık bilmeniz gerekmeyen birkaç şey vardır, ancak bilmeniz gerektiğinde, büyük bir fark yaratır. Bu özel gönderide, garip bir kullanıma bakmak istiyorum. fork sistem çağrısı. Birçok amaç için, bu özel düzensiz kullanımı asla bilmeniz gerekmeyecek. Ama ihtiyacın olduğunda, gerçekten ihtiyacın olacak.

Bu aslında, her gün büyük ve çok kritik bir rapor çalıştırmak için Unix’i kullanan eski bir müşterime dayanıyor. Rapor, bir şeyi optimize etmeye çalıştıkları ve ardından çok sayıda rapor oluşturduğu için çok fazla matematik içeriyordu. O günlerde, raporun çıktısı, bir satır yazıcıdaki eski yeşil çubuk kağıdındaydı. Sorun, çıktılar dahil raporun çalışmasının 14 saat sürmesiydi. Birisi yanlış bir şey keşfettiyse, raporu yeniden çalıştırmak için zaman yoktu çünkü sonraki günün raporunun ikinci çalıştırma bitmeden başlaması gerekecekti.

İstemcinin bir sürü Windows programcısı vardı ve – o zaman – gerçek bir programa gerçekten benzeyen hiçbir şey yoktu. fork Windows’ta arayın. Koda baktım ve muhtemelen kodun çoğunun çıktıyı yazdırmak için zaman harcadığını fark ettim. Bilgisayarın birden fazla CPU’su ve birden çok yazıcısı vardı, ancak bu program tek yazıcıda asılıydı. Çok fazla veri vardı, bu yüzden bir veritabanına yazıp buna karşı farklı raporlar çalıştırmak harika bir seçenek değildi. Cevap, gücünü kullanmaktı. fork. 30 dakikadan az süren kod değişikliğiyle rapor beş saat içinde yayınlandı. Çok memnun kaldılar.

Peki nasıl yaptım? Cevap nasıl yatıyor fork İşler. Hemen hemen her gördüğünüzde forkbir çeşit görüyorsun exec yeni bir program başlatmak için arayın. Yani eğer düşünürsen fork muhtemelen bunun yeni bir programa başlama şeklinizin bir parçası olduğunu düşünüyorsunuz ve çoğu zaman bu doğru.

Nedir fork() tam olarak yap?

Ancak arama çok garip bir şey yapar. Aslında tüm çalışan süreci yeni bir sürece kopyalar. Daha sonra yeni süreci çalıştırır. Tabii ki, orijinal süreç de çalışıyor. Normalde, çatal gördüğünüzde şöyle görünür:

int childPID;
childPID = fork();
if (childPID == 0) exec....  /* load child program and run that */
/* the parent only gets here with childPID set to the new process' PID */
...

Başka bir deyişle, dönüş değeri fork bir alt süreç için sıfır ve ana süreç için başka bir şeydir. Bazı eski Unix sistemleri, çalışan süreçteki her şeyi gerçekten kopyaladı. Ancak, özellikle çoğu zaman hemen yeni bir program yüklediğinizde, bu gerçekten verimsizdir.

Modern sistemler COW veya Copy On Write semantiğini kullanır. Bu, yeni işlemin, orijinal işlem belleğine bir işaretçi miktarı kadar aldığı ve alt veya ana program o bellek bölgesinde değişiklik yaptığında yalnızca nispeten küçük miktarda belleği kopyaladığı anlamına gelir. Bu, çok az insan hala kendi kendini değiştiren kod yazdığından, zaten değişmemesi gereken talimat alanları gibi şeyler için iyidir. Bu, hemen ardından bir fork arama, hem ebeveyn hem de çocuk aynı verileri görür, ancak yaptıkları herhangi bir değişiklik karşı tarafa yansımaz.

Paralel İşleme Kolaylaştı

Müvekkilimin uzun raporuna göre, program çoğunlukla G/Ç’ye bağlıydı. Bununla birlikte, her raporun yürütülebileceği noktaya ulaşmak için gereken tüm matematiğe ek olarak, her raporun kendisiyle birlikte gidecek oldukça karmaşık bir matematiği de vardı. Hepsini tek bir işlemde yürütmek yerine programı birden çok parçaya böldüm. İlk parça, neredeyse her şeye uygulanabilecek kadar matematik yaptı. Sonra program aradı fork birkaç kez ve her çocuk sadece kendisi için biraz daha matematik yapan ve çıktıyı yazacak bir yazıcı talep eden bir rapor başlattı.

CPU’nun birden fazla işlemcisi olduğu için her şey hızlandı. Üçüncü rapor, birinci ve ikinci raporların tamamlanmasını beklemek zorunda değildi. Herkes aynı anda yazıcıları sürebiliyordu. Genel bir galibiyetti ve bu düzeltmeyi yapmak neredeyse hiç zaman almadı.

Elbette, her sorun bunun gibi bir düzeltmeye izin vermeyecektir. Ancak her bir rapor sürecine verilerin bir bellek kopyasını vermek, bir dosyadan veya veritabanından okumaya kıyasla çok hızlıydı. Raporlar başladıktan sonra veriler değişmedi, bu nedenle gerçek bellek tüketimi de çok kötü değildi.

Bir örnek

Peki gerçekten bu kadar basit mi? Bu. Şimdiki tek sorun, modern makinelerde tekniği göstermek için basit bir problem bulmanın zor olmasıdır. Sonunda sadece basit bir şey yapmaya karar verdim, ama çok şey yaptım. Hazır görevim: gerçekten büyük bir çift kesinlikli kayan noktalı sayı dizisini bazı yapılmış ancak öngörülebilir verilerle doldurun ve ardından ortalamayı bulun. Gerçekten büyük derken, 55 milyon veya daha fazla girişi kastediyorum.

İşi iki şekilde yapabilen bir program oluşturdum. İlk olarak, bunu mümkün olan en basit şekilde yapar. Bir döngü dizideki her öğeyi yürütür, onları toplarsınız ve sonunda bölersiniz. Benim makinemde bunu birkaç kez çalıştırmak ortalama 458 milisaniye sürüyor. time Bunu anlamak için komut.

Program ayrıca bir F komut satırındaki parametre. Bu geçerli olduğunda, kurulum aynıdır, ancak bir çatal diziyi ikiye bölmek ve her bir yarının ortalamasını bulmak için iki işlem oluşturur. Çocuğun süreçle iletişim kurmasını istemedim, ama bu elbette mümkün. Bunun yerine, gerçek ortalamayı elde etmek için iki ortalamayı okumanız, bunları toplamanız ve ikiye bölmeniz yeterlidir. Sonucu bildirmek için ek yükü eklemek istemedim, ancak bunu yapmak yeterince kolay olurdu.

Çatal sürümünün çalışma zamanı? Yaklaşık 395 milisaniye. Tabii ki, sonuçlarınız değişecektir ve 60 milisaniye çok fazla görünmese de, birlikte çalışan iki işlemin birden fazla çekirdeğin aynı anda çalışmasına izin verebileceğini gösteriyor.

Dizi ne kadar büyük olursa, zaman tasarrufu da o kadar büyük olur. Örneğin, boyutu 155.256,000 olarak ayarlamak, yaklaşık 150 milisaniyelik bir tasarruf sağladı. Tabii ki, bu zamanlamalar bilimsel değil ve dikkate alınması gereken birçok faktör var, ancak veriler açıkça gösteriyor ki, işi iki süreç arasında bölmek daha hızlı çalışıyor.

kod

Kod basittir. İş zor değil, sadece çok var.

</pre>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

// compile: gcc -o stress stress.c
// run: time stress
// time stress F

#define SIZE 55256000 // how big is the array?
double bigarray[SIZE];

// The process routine will go from llimit to ulimit
// For the single case, that's everything
// For the child case we will split in half
unsigned int ulimit=SIZE;
unsigned int llimit=0;

double total; // running total

// Load up the array with some bogus data
void setup(void)
   {
   unsigned int i;
   for (i=llimit;i<ulimit;i++)
      {
      bigarray[i]=i/3.0f;
      }
   }

// Average the range defined by llimit and ulimit
void process(void)
   {
   unsigned int i;
   unsigned int n=ulimit-llimit;
   total=0.0;
   for(i=llimit;i<ulimit;i++)
      {
      total+=bigarray[i];
      }
   printf("Avg=%f\n",total/n);
   }



int main(int argc, char *argv[])
   {
   int dofork=0;
   int pid;
   if (argc>1 && (*argv[1]=='f' || *argv[1]=='F')) dofork=1; // f or F will trigger a fork
   setup(); // load array
   if (!dofork)
      {
// single case
// ulimit and llimit are already set
      process();
      exit(0);
      }
   else // forking here
      {
      if (pid=fork())
         {
// parent -- adjust ulimit
         ulimit=SIZE/2;
         process();
         waitpid(pid,NULL,0); // wait for child
         exit(0);
         }
      else
         {
// child -- adjust lower and upper limit
         llimit=SIZE/2;
         ulimit=SIZE;
         process();
         exit(0);
         }
     }
// we never get here
   }

Bir Yıldız Gemisinde Neden İşler Çalışır?

Şimdi nasıl olduğunu biliyorsun fork gerçekten işe yarıyor. İyi sıralama. Çocuğa hangi tutamaçların geçtiği ve hangilerinin geçmediği konusunda birçok nüans var. Ve dediğim gibi, buna çok sık ihtiyacınız olmayacak. Ama gerçekten günü kurtaracağı zamanlar vardır.

Çoklu göreve daha üst düzey bir bakış istiyorsanız, daha eski bir Linux Fu deneyin. Veya GNU Parallel aracına göz atın.


Kaynak : https://hackaday.com/2022/04/21/linux-fu-an-odd-use-for-fork/

Yorum yapın