Process ve Thread Kavramlarıyla Node.js Uygulamasını Sonlandırmak

Cem Topkaya
5 min readFeb 12, 2021
  • Önce process ve thread kavramlarını daha iyi anlayacağız.

Süreç (Process) ile İş Parçacığı (Thread) Kavramları

Süreçler

Bir uygulama çalıştırıldığında en az bir Ana Süreç (main process) ve ana sürecin başlattığı yan süreçleriniz, Türkçe çevrisiyle “Çocuk Süreçler” (child processes) ile koşar.

https://www.informit.com/articles/article.aspx?p=25075&seqNum=3

SÜREÇLER (processes) ve iş parçacıkları (threads) tek bir hedefe sahiptir:

  • Bir bilgisayarın aynı anda birden fazla şey yapmasını sağlamak.

Bunu yapmak için, işlemci (veya işlemciler) bilgisayarın kaynaklarını paylaşacak şekilde tasarlanmış uygulama programları gerektiren çeşitli görevler arasında sorunsuz bir şekilde geçiş yapmalıdır. Bu nedenle programcılar, programların yaptıklarını süreçlere ve iş parçacıkları olarak ayırmalıdır.

Node.js uygulamanız tek bir iş parçacığı veya ana süreç üzerinde çalışır. Fazladan işleri halletmek için ek çocuk süreçler oluşturabilirsiniz.

Her program çalışırken, kendi adres alanı ve akış kontrolü vardır.

Adres Alanı (Address Space): Programın bellekte (RAM) işgal ettiği alan.

Akış Kontrolü (Flow Control): İşlemcinin herhangi bir anda programın hangi bölümünü çalıştırdığını bilmenin bir yolu

Başka bir deyişle, süreç (process); bir programın bellekte işgal ettiği yer ve ne yaptığını takip etmenin bir yoludur. Aynı anda birkaç program çalışırken, her birinin kendi adres alanı ve kontrol akışı vardır.

Birden fazla kullanıcıya hizmet vermek için, bir sürecin bir alt süreç oluşturmak için çatallanması (fork) veya kendisinin bir kopyasını yapması gerekebilir.

Node.js alt süreçleri spawn, fork, exec, execFile ile oluşturuyoruz.

https://www.freecodecamp.org/news/node-js-child-processes-everything-you-need-to-know-e69498fe970a/

Ana süreçten çıkmak Node’dan çıkmamızı sağlar.

Üst süreç gibi, alt sürecin de kendi adres alanı ve kontrol akışı vardır. Bununla birlikte, genellikle, bir üst süreç sonlandırıldığında, başlattığı tüm alt süreçler de otomatik olarak öldürülür.

Unix veya Windows gibi çok görevli bir işletim sistemi, çalışan işlemler arasında geçiş yaparak her birine sırayla CPU kullanma zamanı verir. Bir bilgisayarın birden fazla CPU’su varsa, her işlem özel olarak CPU’lardan birine atanabilir. Bu bir problem çünkü süreçler arasında geçiş yapmak zaman alıyor. Modern CPU’lar, herhangi bir işlemin başka birinin adres alanını aşmasını engelleyen bellek yönetim birimlerini (MMU) içerir. Bağlam değiştirme (context switching) adı verilen bir işlemden diğerine geçmek, MMU’nun farklı bir adres alanına işaret edecek şekilde yeniden programlanması ve işlem bilgilerinin kaydedilmesi ve geri yüklenmesi anlamına gelir. Her süreç diğerlerinden izole edildiğinden, süreçler arasında iletişim, sinyaller ve borular gibi isimlerle özel işlevler gerektirir. Bağlam anahtarları gibi, işlemler arası iletişim, işlemci süresi gerektirir.

Ne kadar çok işlem çalışırsa, CPU ve işletim sisteminin pahalı bağlam değiştirme (context switching) için harcayacağı zaman yüzdesi o kadar büyük olur. Yeterli işlem çalıştırıldığında, bir sunucu zamanının neredeyse tamamını süreçler arasında geçiş yaparak geçirebilir ve hiçbir zaman gerçek bir iş yapmaz.

Çözüm: İş Parçacıkları (Thread)

Bu sorunu önlemek için programcılar iş parçacıkları kullanabilir. Bir iş parçacığı, belirli bir işlemle ilişkili tüm evrelerin aynı adres alanını paylaşması dışında, bir alt süreç (child process) gibidir.

Örneğin bir web sunucusunda hizmet veren uygulama farklı kullanıcılardan gelen her isteği aynı uygulama içinde iş parçacıkları (thread) oluşturarak karşılar. İstek yapan kullanıcı için sanki web sunucusu sadece kendisi için çalışan bir süreç (process) olduğunu düşünür.

Her iş parçacığının kendi akış kontrolü (flow control) vardır, ancak aynı adres alanını (RAM üstünde kapladığı alan) ve çoğu veriyi aynı işlemde çalışan diğer tüm iş parçacıklarıyla paylaşır. Her kullanıcının anlayabildiği kadarıyla, program sadece onun için çalışıyor gibi görünüyor.

Avantajı:

  1. İşlemciler arasında geçiş yapmak çok daha az CPU zamanı alır, çünkü adres alanlarını değiştirmeye gerek yoktur.
  2. Ek olarak, adres alanını paylaştıkları için, bir süreçteki iş parçacıkları birbirleriyle daha kolay iletişim kurabilir.
  3. Program birden çok işlemciye sahip bir bilgisayarda çalışıyorsa, tek işlemli bir program yalnızca bir CPU tarafından çalıştırılabilirken, iş parçacıklı bir program iş parçacıklarını mevcut tüm işlemciler arasında bölebilir.

Dolayısıyla, iş parçacıklı bir programı çok işlemcili bir sunucuya taşımak, daha hızlı çalışmasını sağlamalıdır.

Dezavantajı:

  1. İş parçacığı (thread) kullanan programların yazılması ve hata ayıklanması daha zordur.
  2. Tüm programlama kitaplıkları iş parçacıklarıyla kullanılmak üzere tasarlanmamıştır. Ve eski uygulamaların tümü iş parçacıklı uygulamalarla iyi çalışmaz.
  3. Bazı programlama araçları, iş parçacıklı kod tasarlamayı ve test etmeyi de zorlaştırır. İş parçacığı ile ilgili hataları bulmak da daha zor olabilir.
  4. Bir süreçteki iş parçacıkları, birbirlerinin verilerine müdahale edebilir.
  5. İşletim sistemi, aynı anda veri okuma ve yazma gibi işlemleri gerçekleştirebilecek işlem sayısını sınırlayabilir. Çatışmaları önlemek için farklı iş parçacıkları planlamak bir kabus olabilir. Yine de karmaşık, paylaşılan kod ve çok işlemcili sunucular daha yaygın hale geldikçe, iş parçacıkları çoklu görevi hızlandırmaya devam edecek.

Node.js Uygulamasından Çıkmak

Komut satırında başlatılan bir Node.js programından çıkmak için komut dosyasının yürütülmesinin bitmesini bekleyebiliriz. Node.js işlemi (process) komut dosyasının sonuna ulaştığında çıkacaktır.

// ornek.js
var x = 10;
var y = x + 5;
process.on('exit', cikisKodu => {
return console.log('Uygulamanın çıkış kodu: ',cikisKodu);
});

Eğer bir alt süreç varsa ana süreçten çıkış ancak alt süreç tamamlanınca gerçekleşir.

Eğer bitmeyen bir alt süreç varsa ana süreçten çıkış mümkün olmaz!

process.exit()

Ana süreci gecikmeli çalışma içinde de kırabiliyoruz. Bir tam sayı olan çıkış kodunu alır. 0 Değerli çıkış kodu sorunsuz çıkışı temsil eder. Diğer tam sayılar uygulamanın kullanıcısına “nasıl bir çıkış yapmak zorunda olduğunu gösterir”.

process.kill()

Ayrıca, çalışan Node.js işlemini sonlandırmak için process.kill’i kullanarak Node.js’den çıkabiliriz. Bu ve process.exit arasındaki fark, process.kill’in, öldürmek istediğimiz sürecin pid’sini veya işlem kimliğini ve sürece göndermek istediğimiz isteğe bağlı bir sinyali almasıdır. Bu, ana Node.js işlemi dışındaki işlemleri sonlandırmak için sinyaller gönderebileceğimiz anlamına gelir. Bu, çok sayıda çalışan işlemle yüksek düzeyde eşzamanlı uygulamalarda kullanışlıdır.

process.abort()

Aynı şekilde, Node.js’den çıkmak için process.abort’u kullanabiliriz. Bu yöntem REPL’de, betiklerde ve uygulamalarda çalışır. process.abort, process.kill ve process.exit arasındaki fark, process.abort’un her zaman Node.js’den hemen çıkması ve bir çekirdek dosya oluşturmasıdır. Ayrıca, hiçbir olay geri araması çalışmayacaktır.

Eğer REPL modunda çalışıyorsanız .exit yazarak veya Ctrl+C ile çıkış yapabilirsiniz. Bu, SIGINT’i veya kesinti sinyalini REPL’e gönderir. Bu, genellikle POSIX sistemlerindeki programlardan çıkmak için kullanılır.

Referanslar

--

--

Cem Topkaya

Evlat, kardeş, ağabey, eş, baba, müzik sever, öğrenmek ister, paylaşmaya can atar, iyi biri olmaya çalışır, hakkı geçenlerden helallik ister vs.