Linux'ta, bir diskten blokları okuması gerektiğinde bir işlemin durumuna ne olur? Engellendi mi? Öyleyse, yürütülecek başka bir süreç nasıl seçilir?
Yanıtlar:
Bir dosya tanımlayıcının dönüşünü beklerken read()
veya write()
geri dönerken, işlem "D" veya "Disk Uykusu" olarak bilinen özel bir uyku türüne konur. Bu özeldir, çünkü böyle bir durumdayken süreç durdurulamaz veya kesintiye uğratılamaz. İoctl () 'den dönüşü bekleyen bir işlem de bu şekilde uykuya alınır.
Bunun bir istisnası, bir dosyanın (bir terminal veya başka bir karakter aygıtı gibi) O_NONBLOCK
modda açıldığında , bir aygıtın (bir modem gibi) başlatılması için zamana ihtiyaç duyacağı varsayıldığında geçmesidir. Ancak sorunuzda cihazları engellemeyi belirttiniz. Ayrıca, ioctl()
engellemesiz modda açılan bir fd'de engelleme olasılığı olan bir şeyi hiç denemedim (en azından bilerek değil).
Başka bir sürecin nasıl seçileceği, tamamen kullandığınız zamanlayıcıya ve diğer işlemlerin ağırlıklarını o zamanlayıcı içinde değiştirmek için neler yapmış olabileceğine bağlıdır.
Belirli koşullar altındaki bazı kullanıcı alanı programlarının, yeniden başlatılıncaya kadar sonsuza kadar bu durumda kaldığı bilinmektedir. Bunlar tipik olarak diğer "zombiler" ile gruplandırılır, ancak teknik olarak geçersiz olmadıkları için terim doğru olmaz.
Bir işlemin bir diskten veri alması gerektiğinde, işlemin tamamlanması uzun zaman alabileceği için diğer işlemlerin çalışmasına izin vermek için CPU üzerinde çalışmayı etkin bir şekilde durdurur - bir disk için en az 5 ms arama süresi yaygındır ve 5 ms 10 milyondur CPU döngüleri, program açısından sonsuzluk!
Programcı bakış açısından ("kullanıcı alanında" da denir), buna engelleme sistem çağrısı denir . Eğer ararsanız write(2)
(aynı isimli sistem çağrısı etrafında ince libc sarıcı olan), sizin süreç tam o sınır durmaz; çekirdekte sistem çağrı kodunu çalıştırmaya devam eder. Çoğu zaman belirli bir disk denetleyici sürücüsüne kadar gider (dosya adı → dosya sistemi / VFS → blok aygıtı → aygıt sürücüsü), burada diskteki bir bloğu getirme komutunun uygun donanıma gönderildiği çok önemlidir. çoğu zaman hızlı işlem.
SONRA süreç uyku durumuna alınır (çekirdek alanında engellemeye uyku denir - çekirdek açısından hiçbir şey 'engellenmez'). Donanım nihayet uygun veriyi aldıktan sonra uyandırılacak, ardından işlem çalıştırılabilir olarak işaretlenecek ve planlanacaktır. Sonunda, planlayıcı işlemi çalıştıracaktır.
Son olarak, kullanıcı alanında, engelleyen sistem çağrısı uygun durum ve verilerle geri döner ve program akışı devam eder.
Çoğu G / Ç sistem çağrısını engellemesiz modda çağırmak mümkündür (bkz O_NONBLOCK
. open(2)
Ve içinde fcntl(2)
). Bu durumda, sistem çağrıları hemen geri döner ve sadece disk işlemini göndererek rapor verir. Programcı daha sonra işlemin başarılı olup olmadığını açıkça kontrol etmeli ve sonucunu (örn select(2)
. İle) almalıdır . Buna eşzamansız veya olay tabanlı programlama denir.
D durumundan bahseden ( TASK_UNINTERRUPTIBLE
Linux durum adlarında anılan ) buradaki yanıtların çoğu yanlıştır. D devlet sadece bu kod yolu bir çekirdek alan kodu yolu tetiklenir özel uyku modu olduğunu kesilemez çok sadece engelleyeceğini de beklentisi ile (o programa çok karmaşık olacağından), kısa zaman. Çoğu "D durumunun" aslında görünmez olduğuna inanıyorum; çok kısa ömürlüdürler ve 'top' gibi örnekleme araçlarıyla gözlenemezler.
D durumunda birkaç durumda öldürülemez süreçlerle karşılaşabilirsiniz. NFS bununla ünlü ve ben birçok kez karşılaştım. Her zaman yerel disklere ve hızlı hata tespiti (SATA'da bir hata zaman aşımı yaklaşık birkaç 100 ms olacaktır) ile her zaman yerel disklere ulaştığını varsayan bazı VFS kod yolları ile gerçekte ağdan veri alan NFS arasında anlamsal bir çatışma olduğunu düşünüyorum. daha esnektir ve yavaş iyileşme gösterir (300 saniyelik bir TCP zaman aşımı yaygındır). Durumla Linux 2.6.25'te sunulan harika çözüm için bu makaleyi okuyun TASK_KILLABLE
. Bu çağdan önce, çekirdek iş parçacığına bir SIGKILL göndererek NFS işlem istemcilere sinyal gönderebileceğiniz rpciod
, ancak bu çirkin numarayı unuttuğunuz bir hack vardı . ...
/proc/stat
midir?
G / Ç gerçekleştiren bir işlem , CPU'ya programı yürütmeye geri dönmesini söyleyen bir donanım kesintisi olana kadar CPU'yu serbest bırakan D durumuna (kesintisiz uyku) konulacaktır . man ps
Diğer işlem durumları için bakın .
Çekirdeğinize bağlı olarak, yürütülmeye hazır bir süreç akışını izleyen bir süreç planlayıcı vardır . Bir zamanlama algoritmasıyla birlikte çekirdeğe hangi işlemin hangi CPU'ya atanacağını söyler. Dikkate alınması gereken çekirdek süreçleri ve kullanıcı süreçleri vardır. Her işleme, kullanmasına izin verilen CPU zamanının bir parçası olan bir zaman dilimi tahsis edilir. Süreç tüm zaman dilimini kullandığında, süresi dolmuş olarak işaretlenir ve programlama algoritmasında daha düşük öncelik verilir.
In 2.6 çekirdeği , bir var O (1) zaman karmaşıklığı zamanlayıcısı çalıştırmakta kadar kaç süreçler, bu sabit zamanda CPU atar bu nedenle olursa olsun,. 2.6'da ön hazırlık ve CPU yük dengeleme getirdiği için bu daha karmaşıktır. Her durumda, verimlidir ve siz G / Ç'yi beklerken CPU'lar boşta kalmayacaktır.
Başkaları tarafından daha önce açıklandığı gibi, "D" durumundaki (kesintisiz uyku) işlemler, ps işleminin askıda kalmasından sorumludur. Bana RedHat 6.x ve otomatik olarak bağlanmış NFS ev dizinleri ile birçok kez oldu.
İşlemleri D durumunda listelemek için aşağıdaki komutları kullanabilirsiniz:
cd /proc
for i in [0-9]*;do echo -n "$i :";cat $i/status |grep ^State;done|grep D
İşlemin geçerli dizinini ve sorunlu takılı NFS diskini öğrenmek için aşağıdaki örneğe benzer bir komut kullanabilirsiniz (31134'ü uyku işlem numarasıyla değiştirin):
# ls -l /proc/31134/cwd
lrwxrwxrwx 1 pippo users 0 Aug 2 16:25 /proc/31134/cwd -> /auto/pippo
İlgili bağlanmış nfs dosya sistemine -f (kuvvet) anahtarıyla umount komutunu vermenin uyku sürecini uyandırdığını buldum:
umount -f /auto/pippo
dosya sistemi meşgul olduğu için kaldırılmadı, ancak ilgili işlem uyandı ve sorunu yeniden başlatmadan çözebildim.
İşleminizin tek bir iş parçacığı olduğunu ve G / Ç'yi bloke ettiğinizi varsayarsak, işleminiz G / Ç'nin tamamlanmasını beklemeyi engeller. Çekirdek, aynı zamanda kaliteye, önceliğe, son çalıştırma süresine vb. Dayalı olarak çalışacak başka bir işlem seçecektir. Çalıştırılabilir başka süreç yoksa, çekirdek herhangi bir işlem yapmayacaktır; bunun yerine donanıma makinenin boşta olduğunu söyler (bu da daha düşük güç tüketimine neden olur).
G / Ç'nin tamamlanmasını bekleyen işlemler tipik olarak D durumunda, örn., ps
Ve top
.
Evet, görev read () sistem çağrısında engellenir. Hazır olan başka bir görev çalışır veya başka hiçbir görev hazır değilse, boş görev (o CPU için) çalışır.
Normal, bloke edici bir disk okuması, görevin "D" durumuna girmesine neden olur (diğerlerinin de belirttiği gibi). Bu tür görevler, CPU'yu tüketmeseler bile yük ortalamasına katkıda bulunur.
Bazı diğer IO türleri, özellikle tty'ler ve ağ, tamamen aynı şekilde davranmaz - işlem "S" durumunda sona erer ve kesintiye uğrayabilir ve yük ortalamasına göre sayılmaz.
Evet, GÇ'yi bekleyen görevler engellenir ve diğer görevler yürütülür. Bir sonraki görevin seçilmesi, Linux zamanlayıcı tarafından yapılır .
Genellikle süreç engellenir. Okuma işlemi, engellemesiz olarak işaretlenmiş bir dosya tanımlayıcı üzerindeyse veya süreç eşzamansız GÇ kullanıyorsa, engellenmez. Ayrıca, işlemin engellenmemiş başka iş parçacıkları varsa, çalışmaya devam edebilirler.
Bundan sonra hangi sürecin çalışacağına ilişkin karar , çekirdekteki zamanlayıcıya bağlıdır .