Linux'taki Konular ve Süreçler


253

Son zamanlarda birkaç kişinin Linux'ta iş parçacıkları yerine süreçleri kullanmak neredeyse her zaman daha iyi olduğunu duydum, çünkü Linux süreçleri işlemede çok verimli ve iş parçacıklarıyla ilişkili çok fazla sorun (kilitleme gibi) olduğu için. Ancak, şüpheli, çünkü iş parçacıkları bazı durumlarda oldukça büyük bir performans kazancı verebilir gibi görünüyor.

Benim sorum şu ki, iş parçacıklarının ve süreçlerin her ikisinin de iyi işleyebileceği bir durumla karşılaştığımda, süreçleri veya iş parçacıklarını mı kullanmalıyım? Örneğin, bir web sunucusu yazıyor olsaydım, süreç veya iş parçacığı (ya da bir kombinasyon) kullanmalı mıyım?


Linux 2.4 ile bir fark var mı?
mouviciel

3
Linux 2.4 altında süreçler ve iş parçacıkları arasındaki fark, iş parçacıklarının durumlarının (adres alanı, dosya tanıtıcıları vb.) Genellikle olmayan işlemlerden daha fazla bölümünü paylaşmalarıdır. Linux 2.6 altındaki NPTL, win32 ve Solaris'te "işlemler" gibi "iş parçacığı grupları" vererek bunu biraz daha açık hale getirir.
MarkR

6
Eşzamanlı programlama zordur. Çok yüksek bir performansa ihtiyacınız olmadığı sürece , ödünç alma işleminizdeki en önemli husus sıklıkla hata ayıklamanın zorluğu olacaktır . Süreçler bu konuda çok daha kolay bir çözüm sağlar, çünkü tüm iletişim açıktır (kontrol edilmesi, kaydedilmesi vb.). Bunun aksine, evrelerin paylaşılan hafızası, bir evrenin bir diğerini yanlışlıkla etkileyebileceği yerlerin cıvıltılarını oluşturur.
Lutz Prechelt

1
@LutzPrechelt - Eşzamanlı programlama çok işlemeli olduğu kadar çok iş parçacıklı da olabilir. Eşzamanlı programlamanın neden sadece çok iş parçacıklı olduğunu varsaydığınızı anlamıyorum. Bazı belirli dil sınırlamaları nedeniyle olabilir, ancak genel olarak her ikisi de olabilir.
iankit

2
Lutz, sadece eşzamanlı programlamanın hangisi seçilirse seçilsin - süreç veya iş parçacığı - zor olduğunu, ancak süreçleri kullanan eşzamanlı programlamanın birçok durumda daha kolay hata ayıklamayı kolaylaştırdığını belirtti.
user2692263

Yanıtlar:


322

Linux, işlemler ve iş parçacıkları arasında (çekirdeğe) hiçbir ayrım olmaksızın 1-1 iş parçacığı modeli kullanır - her şey basitçe çalıştırılabilir bir görevdir. *

Linux'ta sistem çağrısı clone, aşağıdakiler arasında yapılandırılabilir bir paylaşım düzeyine sahip bir görevi klonlar:

  • CLONE_FILES: aynı dosya tanımlayıcı tablosunu paylaşma (kopya oluşturmak yerine)
  • CLONE_PARENT: yeni görev ve eski arasında bir ebeveyn-çocuk ilişkisi kurmayın (aksi takdirde çocuğun getppid()= ebeveyninin getpid())
  • CLONE_VM: aynı bellek alanını paylaşma ( COW kopyası oluşturmak yerine )

fork()çağrıları clone(az paylaşımı )ve pthread_create()aramaları clone(en paylaşımı ). **

forkpthread_createtabloları kopyalamak ve bellek için COW eşlemeleri oluşturmak nedeniyle maliyeti biraz daha fazladır , ancak Linux çekirdek geliştiricileri bu maliyetleri en aza indirmeyi denedi (ve başardı).

Aynı bellek alanını ve çeşitli tabloları paylaşıyorlarsa görevler arasında geçiş yapmak, veriler önceden önbelleğe yüklenebileceğinden paylaşılmadıklarından biraz daha ucuz olacaktır. Bununla birlikte, hiçbir şey paylaşılmasa bile görevleri değiştirmek çok hızlıdır - bu, Linux çekirdek geliştiricilerinin sağlamaya çalıştığı (ve sağlamayı başardığı) başka bir şeydir.

Birden çok işlemcili sistemde ise Aslında, değil paylaşım aslında performansa faydalı olabilir: Her görev farklı işlemci üzerinde çalışıyorsa, paylaşılan hafızayı senkronize pahalıdır.


* Basitleştirilmiş. CLONE_THREADsinyal dağıtımının paylaşılmasına neden olur (bu CLONE_SIGHAND, sinyal işleyici tablosunu paylaşır).

** Basitleştirilmiş. Orada her ikisi de mevcut SYS_forkve SYS_clonesyscalls ancak çekirdek, sys_forkve sys_cloneaynı çevresinde hem de çok ince sargı olan do_forkkendi etrafında ince bir sarıcı fonksiyonu copy_process. Evet, terimler process, threadve taskLinux çekirdeği yerine birbirinin yerine kullanılmaktadır ...


6
Bence 1 puan eksik. Web sunucunuz için birden fazla işlem yaparsanız, soketi açmak ve 'iş'i' farklı iş parçacıklarına geçirmek için başka bir işlem yazmanız gerekir. Diş açma, tek bir işlem çoklu dişler, temiz tasarım sunar. Birçok durumda iplik doğaldır ve diğer durumlarda yeni bir süreç doğaldır. Sorun gri bir alana düştüğünde, geçici olarak açıklanan diğer değişimler önem kazanır.
Saurabh

26
@Saurabh Pek değil. Kolayca edebilir socket, bind, listen, forkve sonra birden çok işlem var acceptaynı dinleme soket üzerinde bağlantıları. Bir işlem meşgulse kabul etmeyi durdurabilir ve çekirdek gelen bağlantıları başka bir işleme yönlendirir (kimse dinlemiyorsa, çekirdek listenbiriktirmeye bağlı olarak sıraya girecek veya düşecektir ). İş dağıtımı üzerinde bundan daha fazla kontrole sahip değilsiniz, ancak bu genellikle yeterince iyidir!
ephemient

2
@Bloodcount Linux'taki tüm işlemler / iş parçacıkları, mevcut bir işlemi / iş parçacığını klonlayan aynı mekanizma tarafından oluşturulur. clone()Hangi kaynakların paylaşılacağını belirlemek için bayraklar geçirildi . Bir görev, unshare()daha sonraki herhangi bir zamanda da kaynak sağlayabilir .
ephemient

4
@KarthikBalaguru Çekirdeğin içinde, task_structher görev için bir tane var. Bu genellikle çekirdek kodu boyunca "işlem" olarak adlandırılır, ancak çalıştırılabilir her iş parçacığına karşılık gelir. Yok process_struct; bir grup task_structs kendi thread_grouplisteleriyle birbirine bağlıysa , kullanıcı alanı ile aynı "süreç" tir. Biraz özel "thread" kullanımı vardır, örneğin tüm kardeş thread'lar çatal ve exec üzerinde durdurulur ve sadece "ana" thread görünür ls /proc. /proc/pidListede olsun /procya da olmasın , her konuya erişilebilir .
ephemient

5
@KarthikBalaguru Çekirdek, iş parçacıkları ve süreçler arasındaki davranış sürekliliğini destekler; örneğin, clone(CLONE_THREAD | CLONE_VM | CLONE_SIGHAND))size çalışma dizinini, dosyaları veya kilitleri paylaşmayan yeni bir "iş parçacığı" verirken clone(CLONE_FILES | CLONE_FS | CLONE_IO), bunu yapan bir "işlem" sağlar. Temel sistem klonlayarak görevler yaratır; fork()ve pthread_create()sadece clone()farklı bir şekilde çağıran kütüphane fonksiyonlarıdır (bu cevapta yazdığım gibi).
ephemient

60

Linux (ve aslında Unix) size üçüncü bir seçenek sunar.

Seçenek 1 - Süreçler

Uygulamanızın bir kısmını (veya tüm parçalarını) işleyen bağımsız bir yürütülebilir dosya oluşturun ve her işlem için ayrı olarak çağırın, örneğin program, görevlere yetki vermek için kendi kopyalarını çalıştırır.

Seçenek 2 - Dişler

Tek bir iş parçacığı ile başlayan bağımsız bir yürütülebilir dosya oluşturun ve bazı görevleri yapmak için ek iş parçacıkları oluşturun

Seçenek 3 - çatal

Sadece Linux / Unix altında mevcut, bu biraz farklı. Çatallanmış bir süreç gerçekten kendi adres alanı olan kendi işlemidir - çocuğun ebeveyninin veya kardeşlerinin adres alanını (bir iş parçacığından farklı olarak) etkilemek için yapabileceği (normalde) hiçbir şey yoktur - böylece sağlamlık eklersiniz.

Ancak, bellek sayfaları kopyalanmaz, yazma üzerine kopyalanır, bu nedenle genellikle tahmin edebileceğinizden daha az bellek kullanılır.

İki adımdan oluşan bir web sunucusu programı düşünün:

  1. Yapılandırma ve çalışma zamanı verilerini okuma
  2. Sayfa isteklerini sunma

Eğer iş parçacıkları kullandıysanız, adım 1 bir kez ve adım 2 birden çok iş parçacığında yapılır. "Geleneksel" işlemler kullandıysanız, her işlem için adım 1 ve 2'nin tekrarlanması ve yapılandırmanın ve çalışma zamanı verilerinin depolanması için belleğin çoğaltılması gerekir. Fork () kullandıysanız, adım 1'i bir kez ve sonra fork () 'u kullanarak çalışma zamanı verilerini ve yapılandırmasını dokunulmaz, kopyalanmaz.

Yani gerçekten üç seçenek var.


7
@Qwertie çatallama o kadar da güzel değil, çok sayıda kütüphaneyi ince yollarla (ana süreçte kullanırsanız) bozar. Deneyimli programcıları bile karıştıran beklenmedik davranışlar yaratır.
MarkR

2
@MarkR bazı örnekler verebilir veya kitap kırmanın ve beklenmedik davranışlar yaratmanın bir bağlantısını verebilir misiniz?
Ehtesh Choudhury

18
Bir işlem açık bir mysql bağlantısıyla çatallanırsa, soket iki işlem arasında paylaşıldığından kötü şeyler olur. Yalnızca bir işlem bağlantıyı kullansa bile diğeri bağlantıyı keser.
MarkR

1
fork () sistem çağrısı POSIX tarafından belirtilir (yani herhangi bir Unix sisteminde kullanılabilir), eğer clone () sistem çağrısı olan temel Linux API'sını kullandıysanız, aslında Linux'ta sadece üç taneden daha fazla seçeneğiniz vardır .
Yalan Ryan

2
@MarkR Soketin paylaşımı tasarım gereğidir. Ayrıca, işlemlerden biri soket üzerinde close () öğesini çağırmadan önce linux.die.net/man/2/shutdown komutunu kullanarak soketi kapatabilir.
Lelanthran

53

Bu birçok faktöre bağlıdır. İşlemler dişlerden daha ağırdır ve daha yüksek başlatma ve kapatma maliyetine sahiptir. İşlemler arası iletişim de (IPC), birbirleri arası iletişimden daha zor ve yavaştır.

Bunun tersine, süreçler iş parçacıklarından daha güvenli ve daha güvenlidir, çünkü her işlem kendi sanal adres alanında çalışır. Bir işlem çökerse veya arabellek taşması varsa, başka bir işlemi etkilemez, bir iş parçacığı çökerse, işlemdeki diğer tüm iş parçacıklarını alır ve bir iş parçacığının arabellek taşması varsa açılır. tüm dişlerde bir güvenlik deliği.

Bu nedenle, uygulamanızın modülleri çoğunlukla az iletişim ile bağımsız olarak çalışabiliyorsa, başlatma ve kapatma maliyetlerini karşılayabiliyorsanız muhtemelen işlemleri kullanmalısınız. IPC'nin performans isabeti minimum olacak ve hatalara ve güvenlik deliklerine karşı biraz daha güvenli olacaksınız. Çok sayıda paylaşılan veri (karmaşık veri yapıları gibi) elde edebileceğiniz veya alabileceğiniz her performansa ihtiyacınız varsa, iş parçacıklarıyla gidin.


9
Adam'ın cevabı yönetici brifingi olarak işe yarayacaktır. Daha fazla ayrıntı için MarkR ve geçici açıklama iyi açıklamalar sağlar. Örneklerle çok ayrıntılı bir açıklama cs.cf.ac.uk/Dave/C/node29.html adresinde bulunabilir, ancak bölümler halinde biraz tarihli gibi görünmektedir.
CyberFonic

2
CyberFonic, Windows için geçerlidir. Ephemient'in dediği gibi Linux süreçleri daha ağır değil. Linux altında, iş parçacıkları (futex'ler, paylaşılan bellek, borular, IPC) arasındaki iletişim için mevcut tüm mekanizmalar da işlemler için kullanılabilir ve aynı hızda çalışır.
Russell Stuart

IPC'yi kullanmak daha zordur, ancak biri "paylaşılan bellek" kullanıyorsa ne olur?
abhiarora

11

Diğerleri bu hususları tartıştı.

Belki de önemli fark, Windows işlemlerinde iş parçacıklarına göre ağır ve pahalıdır ve Linux'ta fark çok daha küçüktür, bu nedenle denklem farklı bir noktada dengeler.


9

Bir zamanlar Unix vardı ve bu iyi eski Unix'te süreçler için çok fazla yük vardı, bu yüzden bazı akıllı insanların yaptığı, aynı adres alanını üst süreçle paylaşacak olan iş parçacıkları oluşturmaktı ve sadece daha az bir bağlama ihtiyaçları vardı bağlam anahtarını daha verimli hale getirir.

Çağdaş bir Linux'ta (2.6.x), bir işlemin bağlam anahtarı arasında bir iş parçacığına kıyasla performansta çok fazla fark yoktur (iş parçacığı için yalnızca MMU şeyler eklenir). Paylaşılan adres alanı ile ilgili bir sorun var, yani bir iş parçacığında hatalı bir işaretçi üst işlemin belleğini veya aynı adres alanı içindeki başka bir iş parçacığını bozabilir.

Bir işlem MMU tarafından korunur, bu nedenle hatalı bir işaretçi sadece bir sinyale 11 neden olur ve bozulmaya neden olmaz.

Genel olarak süreçleri kullanırım (Linux'ta çok fazla bağlam anahtarı yükü değil, MMU'ya bağlı bellek koruması), ancak gerçek zamanlı bir zamanlayıcı sınıfına ihtiyacım olursa pthreads, hep birlikte farklı bir fincan çay.

Neden Linux'ta iş parçacıklarının bu kadar büyük bir performans kazancı olduğunu düşünüyorsunuz? Bunun için herhangi bir veri var mı, yoksa sadece bir efsane mi?


1
Evet, verilerim var. 100.000 işlem oluşturan bir test ve 100.000 iş parçacığı oluşturan bir test çalıştırdım. İş parçacığı sürümü yaklaşık 9 kat daha hızlı çalıştı (işlemler için 17,38 saniye, iş parçacıkları için 1,93 saniye). Şimdi bu sadece oluşturma süresini test ediyor, ancak kısa ömürlü görevler için oluşturma süresi anahtar olabilir.
user17918

4
@ user17918 - Yukarıda belirtilen zamanlamaları hesaplamak için kullandığınız kodu paylaşmanız mümkün mü ..
codingfreak

bir büyük farklı, süreçleri ile çekirdek her işlem için sayfa tablosu oluşturmak ve theads sadece bir sayfa tabloları kullanın, bu yüzden normal olduğunu düşünüyorum iş parçacıkları daha hızlı sonra süreçleri
c4f4t0r

Bakmanın bir başka basit yolu TCB, PCB'den oldukça küçüktür ve bu nedenle PCB'yi içeren işlem bağlamı anahtarının, ipliklerin değiştirilmesinden biraz daha fazla zaman alacağı açıktır.
Karthik Balaguru

5

Görevleriniz ne kadar sıkı bağlı?

Birbirlerinden bağımsız olarak yaşayabilirlerse, süreçleri kullanın. Birbirlerine güveniyorlarsa, iplik kullanın. Bu şekilde, diğer görevlerin işleyişine müdahale etmeden kötü bir işlemi öldürebilir ve yeniden başlatabilirsiniz.


4

Konuları daha da karmaşık hale getirmek için, yerel iş parçacığı deposu ve Unix paylaşılan belleği gibi bir şey var .

Yerel iş parçacığı deposu, her iş parçacığının ayrı bir global nesne örneğine sahip olmasını sağlar. Kullandığım tek zaman, RTOS'da çalışan uygulama kodu için linux / windows üzerinde bir öykünme ortamı oluştururken oldu. RTOS'ta her görev kendi adres alanına sahip bir işlemdi, öykünme ortamında her görev bir iş parçacığıydı (paylaşılan adres alanına sahip). Singleton gibi şeyler için TLS kullanarak, 'gerçek' RTOS ortamında olduğu gibi her bir iş parçacığı için ayrı bir örnek elde edebildik.

Paylaşılan bellek, (açık bir şekilde), birden çok işlemin aynı belleğe erişmesinin, ancak işlemleri düzgün bir şekilde senkronize etme maliyetinin / riskinin olmasının getirdiği performans avantajlarını sağlayabilir. Bunu yapmanın bir yolu, bir işlemin paylaşılan bellekte bir veri yapısı oluşturması ve daha sonra bu yapıya geleneksel süreçler arası iletişim (adlandırılmış bir boru gibi) yoluyla bir tanıtıcı göndermesidir.


1
Bazı istatistik toplama için iş parçacığı yerel depolama kullandım, son kez bir dişli ağlar programı yazıyordum: her iş parçacığı kendi sayaçlarına yazdı, kilit gerekli değildi ve yalnızca iletilen her iş parçacığı istatistiklerini genel toplamlara birleştirdiğinde. Ama evet, TLS çok yaygın olarak kullanılmıyor veya gerekli değil. Diğer yandan paylaşılan bellek ... Verileri verimli bir şekilde göndermeye ek olarak, POSIX semaforlarını süreçler arasında paylaşılan belleğe yerleştirerek de paylaşabilirsiniz. Oldukça şaşırtıcı.
ephemient

4

LINUX ile yaptığım son çalışmalarda kütüphanelerin farkında olmak bir şey var. İş parçacığı kullanıyorsanız, iş parçacıkları arasında kullanabileceğiniz kitaplıkların iş parçacığı açısından güvenli olduğundan emin olun. Bu beni birkaç kez yaktı. Özellikle libxml2 kutudan çıktığı gibi güvenli değildir. İş parçacığı kasası ile derlenebilir, ancak yetenek yüklemesi ile elde ettiğiniz bu değildir.


3

Duyduklarınıza katılıyorum. Kümemizi ( xhplve benzerlerini) kıyasladığımızda, iş parçacıkları üzerindeki işlemlerde her zaman önemli ölçüde daha iyi performans elde ederiz.</anecdote>


3

İş parçacığı / süreç arasındaki karar, onu ne için kullanacağınıza bağlıdır. Bir işlemin avantajlarından biri, bir PID'ye sahip olması ve ebeveyni de sonlandırmadan öldürülebilmesidir.

Bir web sunucusunun gerçek dünya örneği için, apache 1.3 yalnızca birden çok işlemi desteklemek için kullanılır, ancak 2.0'da ikisi arasında geçiş yapabilmeniz için bir soyutlama eklediler . Yorumlar , işlemlerin daha sağlam olduğunu kabul ediyor gibi görünüyor , ancak iş parçacıkları biraz daha iyi performans verebilir (süreçlerin performansının emildiği ve yalnızca iş parçacıklarını kullanmak istediğiniz pencereler hariç).


2

Çoğu durumda iş parçacıkları yerine işlemleri tercih ediyorum. iş parçacıkları nispeten daha küçük bir göreve sahipseniz (işlem yükü >> her bölünmüş görev birimi tarafından harcanan zaman) ve bunlar arasında bellek paylaşımına ihtiyaç duyduğunuzda yararlı olabilir. Geniş bir dizi düşünün. Ayrıca (offtopik), CPU kullanımınız yüzde 100 veya ona yakınsa, çoklu kullanım veya işlemeden faydalanmayacağını unutmayın. (aslında kötüleşecek)


Ne faydası demek istiyorsun? GUI iş parçacığında ağır hesaplamalar yapmaya ne dersiniz? CPU'nun nasıl yüklendiğine bakılmaksızın, bunları paralel iş parçacığına taşımak, kullanıcı deneyiminden çok daha iyi olacaktır.
olegst

2

Konular -> Konular bir bellek alanını paylaşır, CPU'nun bir soyutlamasıdır, hafiftir. İşlemler -> İşlemler kendi bellek alanlarına sahiptir, bir bilgisayarın soyutlamasıdır. Görevi paralel hale getirmek için bir CPU soyutlamanız gerekir. Bununla birlikte, bir iş parçacığı üzerinde bir işlem kullanmanın avantajları güvenlik, istikrar bir iş parçacığı işlemden daha az bellek kullanır ve daha az gecikme sağlar. Web açısından örnek olarak chrome ve firefox verilebilir. Chrome durumunda, her sekme yeni bir işlemdir, bu nedenle kromun bellek kullanımı firefox'tan daha yüksektir, sağlanan güvenlik ve kararlılık firefox'tan daha iyidir. Her sekme yeni bir işlem olduğundan, burada krom tarafından sağlanan güvenlik daha iyidir, farklı sekme belirli bir işlemin bellek alanına göz atamaz.


2

Bence herkes sorunuza yanıt olarak harika bir iş çıkardı. Çekirdek bağlamında önceki yanıtların bazılarını açıklığa kavuşturmak ve özetlemek için Linux'ta iş parçacığına karşı süreç hakkında daha fazla bilgi ekliyorum. Yani, cevabım Linux'ta çekirdeğe özel kod ile ilgili. Linux Çekirdeği belgelerine göre, işlemden farklı olarak paylaşılan sanal adres alanını kullanması dışında, iş parçacığı ve işlem arasında açık bir ayrım yoktur . Ayrıca, Linux Çekirdeği genel olarak süreç ve iş parçacığı için "görev" terimini kullandığını unutmayın.

"İşlemleri veya iş parçacıklarını uygulayan hiçbir iç yapı yoktur, bunun yerine görev adı verilen soyut bir zamanlama birimini tanımlayan bir yapı task_struct vardır"

Ayrıca Linus Torvalds'a göre, iş parçacığına karşı işlem hakkında hiç düşünmemelisiniz ve çok sınırlayıcı olduğu ve tek fark "adres alanını üst öğeden ayırın" veya paylaşılan adres alanı açısından COE veya Yürütme Bağlamı olduğu için. Aslında burada bir web sunucusu örneği kullanıyor ( burada okumayı şiddetle tavsiye ediyor).

Linux çekirdeği belgelerine tam kredi


-3

Kaynakları paylaşmanız gerekiyorsa, gerçekten iş parçacıkları kullanmalısınız.

Ayrıca, iş parçacıkları arasındaki bağlam anahtarlarının işlemler arasındaki bağlam anahtarlarından çok daha ucuz olduğunu düşünün.

Bunu yapmak için iyi bir nedeniniz yoksa (güvenlik, kanıtlanmış performans testleri vb.) Açıkça ayrı süreçlerle devam etmek için hiçbir neden göremiyorum.


3
Düzenlenecek temsilcim var, ama tam olarak aynı fikirde değilim. Linux'taki işlemler arasındaki bağlam geçişleri, neredeyse iş parçacıkları arasındaki bağlam geçişleri kadar ucuzdur.
ephemient
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.