Diğer cevaplar sadece bir noktada gerçek anlamda çalışan bir NOP düşünüyor - bu oldukça yaygın kullanılıyor, ancak NOP'un tek kullanımı değil.
Yamalı edilebilir kod yazarken olmayan yürütme NOP de oldukça yararlıdır - temelde, sen olacak ped birkaç NOPs ile işlev sonraRET
(ya da benzeri talimat). Yürütülebilir dosyayı yamalamak zorunda olduğunuzda, orijinalden başlayarak RET
ve bu NOP'lardan istediğiniz kadarını (örn. Uzun atlama veya hatta satır içi kod için) kullanarak ve başka biriyle bitirerek işleve kolayca daha fazla kod ekleyebilirsiniz RET
.
Bu kullanım durumunda, noöne hiçbir NOP
zaman yürütmeyi beklemez . Tek nokta, çalıştırılabilir dosyanın yamalanmasına izin vermektir - teorik, yastıklı olmayan bir yürütülebilir dosyada, işlevin kodunu gerçekten değiştirmeniz gerekir (bazen orijinal sınırlara uyuyor olabilir, ancak çoğu zaman yine de bir sıçrama yapmanız gerekir ) - bu özellikle elle yazılmış montaj veya optimize edici bir derleyici göz önüne alındığında çok daha karmaşıktır; bazı önemli kod parçalarına işaret edebilecek sıçramalara ve benzer yapılara saygı göstermelisiniz. Sonuçta, oldukça zor.
Tabii ki, bu eski günlerde, bu küçük ve çevrimiçi gibi yamalar yapmanın yararlı olduğu çok daha yoğun bir şekilde kullanıldı . Bugün, yeniden derlenmiş bir ikili dosyayı dağıtacak ve onunla işleneceksiniz. Yamalama NOP'larını kullanan bazı kullanıcılar var (her zaman değişmez değil NOP
- yürütüyor ya da değil - örneğin, Windows MOV EDI, EDI
çevrimiçi yama için kullanır - bu, sistem yeniden başlatıldığında sistem çalışırken gerçekte bir sistem kitaplığını güncelleyebileceğiniz türdür).
Öyleyse son soru, neden gerçekten hiçbir şey yapmayan bir şey için özel bir talimatınız var?
- Bu gerçek bir talimattır - montajda hata ayıklama veya elle kodlama yaparken önemlidir. Gibi talimatlar
MOV AX, AX
tam olarak aynı şeyi yapacak, ama niyetini çok açık bir şekilde göstermiyor.
- Doldurma - "kod", yalnızca hizalamaya bağlı olan kodun genel performansını artırmak için kullanılır. Asla idam etmek istemedi. Bazı hata ayıklayıcılar, doldurma NOP'larını sökme işlemlerinde gizler.
- Derleyicileri optimize etmek için daha fazla alan sağlar - hala kullanılan desen, birincisi oldukça basit olan ve çok sayıda gereksiz montaj kodu üretirken, iki adım derlemeniz olması, ikincisi ise adres referanslarını temizler, yeniden sarır ve kaldırır yabancı talimatlar. Bu genellikle JIT derlemeli dillerde de görülür - hem .NET'in IL hem de JVM'nin bayt kodu kullanımı
NOP
oldukça fazladır; derlenmiş asıl montaj kodu artık bunlara sahip değil. Bununla birlikte, bunların x86- NOP
s olmadığı belirtilmelidir .
- Çevrimiçi hata ayıklamayı hem okumak için kolaylaştırır (önceden sıfırlanmış bellek her
NOP
şeydir, sökülmeyi çok daha kolay okunur hale getirir) hem de sıcak yama için (Visual Studio'da Düzenleme ve Devam Etmeyi tercih ediyorum: P).
NOP'ları yürütmek için elbette birkaç nokta daha var:
- Performans, elbette - bu yüzden 8085'te değildi, ancak 80486'da bile "hiçbir şey yapmamak" için biraz daha karmaşık bir boru hattı talimatı yürütmesi vardı.
- Görüldüğü gibi
MOV EDI, EDI
, kelimenin tam anlamıyla etkili başka NOP'lar var NOP
. MOV EDI, EDI
x86'da 2 baytlık NOP olarak en iyi performansa sahiptir. İki NOP
s kullandıysanız , yürütmek için iki talimat olurdu.
DÜZENLE:
Aslında, @DmitryGrigoryev ile yapılan tartışma beni biraz daha düşünmeye zorladı ve bence bu soru / cevap için değerli bir katkı, bu yüzden biraz fazladan bit eklememe izin verin:
İlk olarak, açıkçası - neden böyle bir şey yapan bir talimat olsun ki mov ax, ax
? Örneğin, 8086 makine kodu durumuna (386 makine kodundan daha eski) bakalım:
- Opcode ile özel bir NOP talimatı vardır
0x90
. Bu hala birçok insanın mecliste yazdığı zamandır. Bu nedenle, özel bir NOP
talimat olmasa bile NOP
(takma ad / anımsatıcı) anahtar kelimesi yine de yararlı olur ve buna eşlenir.
- Talimatlar gibi
MOV
o zaman ve mekan tasarruf çünkü, aslında birçok farklı opcodes eşleme - örneğin, mov al, 42
"hemen bayt taşımak olduğunu al
çevirir, kayıt" 0xB02A
( 0xB0
, işlemkodu olma 0x2A
"acil" argüman olmak üzere). Bu iki bayt alır.
- Kısayol op kodu yoktur
mov al, al
(çünkü bu aptalca bir şeydir, temelde), bu nedenle mov al, rmb
(rmb "kayıt veya bellek" olmak üzere) aşırı yüklemeyi kullanmanız gerekir. Bu aslında üç bayt alır . ( mov rb, rmb
bunun yerine daha az spesifik kullanılmasına rağmen , yalnızca iki bayt almalıdır mov al, al
- argüman baytı hem kaynağı hem de hedef kaydını belirtmek için kullanılır; şimdi 8086'nın neden sadece 8 kaydı olduğunu biliyorsunuz: D). NOP
Tek baytlık bir talimat olan ile karşılaştırın ! Bu, bellek ve zamandan tasarruf sağlar, çünkü 8086'daki belleği okumak hala oldukça pahalıdır - bu programı bir banttan veya disketten veya elbette bir şeyden yüklemekten bahsetmiyoruz bile.
Peki nereden xchg ax, ax
geliyor? Sadece diğer xhcg
talimatların opcodlarına bakmanız gerekir . Göreceksin 0x86
, 0x87
nihayet ve 0x91
- 0x97
. Yani nop
bunun 0x90
için oldukça iyi bir uyum gibi görünüyor xchg ax, ax
(ki, yine, bir xchg
"aşırı yük" değil - xchg rb, rmb
iki baytta kullanmanız gerekir). Ve aslında, bunun zamanın mikro mimarisinin güzel bir yan etkisi olduğuna eminim - doğru hatırlarsam, 0x90-0x97
"xchg, registerlar üzerinde hareket etmek ax
ve ax
- di
" ( işlenen simetriktir, bu size nop dahil olmak üzere tüm aralığı verdi xchg ax, ax
; siparişin ax, cx, dx, bx, sp, bp, si, di
- bx
sonra olduğunu dx
,ax
; Unutmayın, kayıt adları anımsatıcılar, sıralı adlar değildir - akümülatör, sayaç, veri, taban, yığın işaretçisi, taban işaretçisi, kaynak dizini, hedef dizini). Aynı yaklaşım, örneğin mov someRegister, immediate
küme gibi diğer işlenenler için de kullanılmıştır . Bir bakıma, bunu opcode aslında tam bir bayt değilmiş gibi düşünebilirsiniz - son birkaç bit, "gerçek" işlenen için "bir argüman" dır.
Tüm bunlar, x86'da nop
gerçek bir talimat olarak kabul edilebilir veya olmayabilir. Orijinal mikro mimari, xchg
doğru hatırlamıyorsam bir varyant olarak davrandı , ancak aslında nop
spesifikasyonda adlandırıldı . Ve xchg ax, ax
gerçekten bir talimat olarak mantıklı olmadığından, 8086 tasarımcılarının, 0x90
tamamen "noppy" olan bir şeye doğal olarak eşlediği gerçeğini kullanarak kod çözmede transistörlerde ve yollarda nasıl tasarruf ettiklerini görebilirsiniz.
Öte yandan, i8051 tamamen tasarlanmış-in işlem kodu için vardır nop
- 0x00
. Biraz pratik. Öğretim tasarımı temelde çalışma için yüksek nibble ve işlenen seçilmesi için düşük nibble kullanıyor - örneğin, add a
bir 0x2Y
ve 0xX8
araçlar "0 doğrudan kayıt", bu yüzden 0x28
olduğunu add a, r0
. Silikonda çok tasarruf sağlar :)
CPU tasarımı (derleyici tasarımı ve dil tasarımından bahsetmiyorum) oldukça geniş bir konu olduğu için hala devam edebilirdim, ancak tasarıma oldukça hoş giden birçok farklı bakış açısı gösterdim.