Kabuğumda bir dosya çalıştırdığımda tam olarak ne olur?


32

Bu yüzden, bunu iyi anladığımı sanıyordum, ama sadece bir test yaptım (birine karşı çıktığım bir sohbete yanıt olarak) ve anlayışımın hatalı olduğunu öğrendim ...

Kabuğumda bir dosyayı çalıştırdığımda tam olarak ne oluyor? Demek istediğim, eğer: kabuğuma ./somefile some argumentsyazıp geri dönüş tuşuna somefilebasarsam (ve cwd'de var ve okuma + yürütme izinlerini açtım somefile), sonra başlık altında ne olur?

Cevap olduğunu düşündüm :

  1. Kabuk bir çağrı yapar execve yolu geçereksomefile
  2. Çekirdek , işlemcinin kaldırabileceği bir format olup olmadığını belirlemek için dosyanın sihirli sayısını inceler somefileve bakar.
  3. Sihirli sayı, dosyanın işlemcinin yürütebileceği bir formatta olduğunu gösterirse,
    1. yeni bir süreç yaratılır (süreç tablosuna bir giriş ile)
    2. somefilehafızaya okunur / haritalanır. Bir yığın oluşturuldu ve yürütme kod giriş noktası atlar olduğu somefileile, ARGVparametreler bir dizi başlatılır: (a char**, ["some","arguments"])
  4. Sihirli sayı ise bir mesele daha sonra exec()yukarıdaki gibi yeni bir işlem olarak çoğaltılır, ancak yürütülebilir kullanılan shebang tarafından başvurulan yorumlayıcı (örneğin bir /bin/bashya da /bin/perl) ve somefilegeçirilirSTDIN
  5. Dosyanın geçerli bir sihirli numarası yoksa, "geçersiz dosya (hatalı sihirli sayı): Yürütme biçimi hatası" gibi bir hata oluşur.

Ancak birileri bana dosya düz metin ise, kabuk komutları yürütmeye çalıştığını söyledi (yazmışım gibi bash somefile). Buna inanmadım ama denedim ve doğruydu. Bu yüzden burada gerçekte ne olduğu hakkında bazı yanlış düşüncelerim var ve mekaniği anlamak istiyorum.

Kabuğumda bir dosya çalıştırdığımda tam olarak ne olur? (kadar ayrıntılı olarak makul ...)


Kaynak koduna tam bir anlayış derinliği için bakmak için mükemmel bir alternatif yoktur .
Wildcard

1
@Wildcard şu anda ne yapıyorum, aslında :-) Yapabilirsem, kendi soruma cevap vereceğim
Josh

1
source somefileAncak, yeni bir sürecin izinsiz olarak bırakılmasından çok farklı ./somefile.
saat

@ thrig evet, katılıyorum. Ancak ./somefile, somefileeğer dosyanın sihirli bir numarası yoksa, bash'ın komutları yerine getirmesine neden olacağını düşünmemiştim . Sadece bir hata göstereceğini düşündüm ve bunun yerine etkili bir şekilde görünüyorsource somefile
Josh

Tekrar yanlış yapıyorum, eğer somefilebir metin dosyası ise, çalıştırmayı denediğimde yeni bir kabuk üretildiğini onaylayabilirim . Bir dosyayı echo $$vs kaynak yürütürsem, farklı davranır.
Josh,

Yanıtlar:


31

Linux'ta "programların nasıl çalıştırıldığının" kesin cevabı, LWN.net'te şaşırtıcı biçimde yeterince, Programların nasıl çalıştırıldığı ve Programların nasıl çalıştırıldığı başlıklı makale çiftidir . ELF ikili dosyaları . İlk makale komut dosyalarını kısaca açıklamaktadır. (Kesin olarak söylemek gerekirse, kesin cevap kaynak kodundadır, ancak bu makalelerin okunması ve kaynak koduna bağlantılar verilmesi daha kolaydır.)

Küçük bir deney, onu hemen hemen doğru anladığınızı ve basit bir komut listesi içeren bir dosyanın çalıştırılmasının, bir shebang olmadan, kabuk tarafından ele alınması gerektiğini göstermektedir. Execve (2) manpage bir test programı için kaynak kodunu içeren execve; Bunu kabuk olmadan ne olacağını görmek için kullanacağız. İlk önce testscr1, içeren bir test komut dosyası yazın.

#!/bin/sh

pstree

ve testscr2yalnızca birini içeren

pstree

Her ikisini de çalıştırılabilir duruma getirin ve her ikisinin de kabuktan çalıştırıldığını doğrulayın:

chmod u+x testscr[12]
./testscr1 | less
./testscr2 | less

Şimdi execve(onu geçerli dizinde oluşturduğunuzu varsayarak) kullanarak tekrar deneyin :

./execve ./testscr1
./execve ./testscr2

testscr1hala çalışıyor ama testscr2üretiyor

execve: Exec format error

Bu, kabuğun testscr2farklı işlediğini gösterir . Ancak betiği kendisi işlemez, yine /bin/shde bunu yapmak için kullanır ; Bu boru doğrulanabilir testscr2için less:

./testscr2 | less -ppstree

Sistemimde alıyorum

    |-gnome-terminal--+-4*[zsh]
    |                 |-zsh-+-less
    |                 |     `-sh---pstree

Gördüğünüz gibi , koştuğum komut dosyasını çalıştırmak için kullandığım ve zshbaşladığım lesskabuk ve ikinci bir kabuk var sh( dashsistemimde) pstree. Gelen zshbu tarafından işlendiğini zexecvede Src/exec.c: kabuk kullanımlara execve(2)komutu çalıştırmak için denemek için, ve başarısız olursa, buna göre (çekirdek da yapmış olan) o işlemin gerçekleştirilmesi suretiyle shebang olup olmadığını görmek için dosyayı okur ve eğer dosyadan shsıfır bayt okumadığı sürece dosyayı çalıştırmayı denemez:

        for (t0 = 0; t0 != ct; t0++)
            if (!execvebuf[t0])
                break;
        if (t0 == ct) {
            argv[-1] = "sh";
            winch_unblock();
            execve("/bin/sh", argv - 1, newenvp);
        }

bashexecute_cmd.cyararlı bir yorumla ( taliezin tarafından belirtildiği gibi ) uygulanan aynı davranışa sahiptir :

Bir yerde bir disk dosyasında umutla tanımlanmış olan basit bir komutu yürütün.

  1. fork ()
  2. boruları bağlamak
  3. komuta bakmak
  4. yönlendirmeleri yapmak
  5. execve ()
  6. Eğer execvebaşarısız dosya çalıştırılabilir mod kümesi vardır olmadığına bakın. Öyleyse ve bir dizin değilse, içeriğini kabuk betiği olarak yürütün.

POSIX olarak bilinen işlevler kümesi tanımlar fonksiyonları sarma, bu işlevi de sağlamakta; bkz muru Ayrıntılar için 'cevabını. Linux'ta en azından bu işlevler çekirdek tarafından değil, C kütüphanesi tarafından uygulanır.exec(3)execve(2)


Bu fantastik ve aradığım detaylara sahip, teşekkür ederim!
Josh

12

Bu kısmen execkullanılan özel aile işlevine bağlıdır . execveOlarak, Stephen KITT ayrıntılı olarak göstermiştir, sadece uygun bir shebang ile başlayan doğru ikili formattaki dosyaları veya komut dosyaları çalışır.

Ancak , execlpve execvpbir adım daha ileri gidin: shebang doğru değilse, dosya ile yürütülür /bin/shLinux üzerinde. Kimden man 3 exec:

Special semantics for execlp() and execvp()
   The execlp(), execvp(), and execvpe() functions duplicate the actions
   of the shell in searching for an executable file if the specified
   filename does not contain a slash (/) character.
   …

   If the header of a file isn't recognized (the attempted execve(2)
   failed with the error ENOEXEC), these functions will execute the
   shell (/bin/sh) with the path of the file as its first argument.  (If
   this attempt fails, no further searching is done.)

Bu biraz POSIX tarafından destekleniyor (benimki vurgusu):

Standart geliştiriciler tarafından not edilen potansiyel bir karışıklık kaynağı, bir işlem imaj dosyasının içeriğinin, exec işlev ailesinin davranışını nasıl etkilediği ile ilgilidir. Aşağıdakiler, gerçekleştirilen işlemlerin bir açıklamasıdır:

  1. İşlem görüntü dosyası bu sistem için geçerli bir çalıştırılabilir (çalıştırılabilir ve geçerli ve uygun yetkilere sahip bir biçimde) ise, sistem dosyayı çalıştırır.

  2. İşlem görüntüsü dosyasının uygun yetkileri varsa ve bu sistem için geçerli olmayan ancak bu sistem için geçerli olmayan (başka bir mimari için tanınan bir ikili) biçiminde ise, bu bir hatadır ve errno [EINVAL] olarak ayarlanır (daha sonra [EINVAL]) üzerinde.

  3. İşlem görüntüsü dosyasının uygun ayrıcalıkları varsa, ancak başka şekilde tanınmıyorsa:

    1. Bu execlp () veya execvp () çağrısıysa, işlem görüntüsü dosyasının bir kabuk betiği olduğunu varsayarak bir komut yorumlayıcısı çağırırlar.

    2. Bu execlp () veya execvp () çağrısı değilse, bir hata oluşur ve hata [ENOEXEC] olarak ayarlanır.

Bu, komut yorumlayıcısının nasıl elde edildiğini belirlemez, ancak bir hatanın verilmesi gerektiğini belirtmez. Bu nedenle, Linux aygıtlarının bu tür dosyaların çalıştırılmasına izin verdiğini tahmin ediyorum /bin/sh(ya da bu zaten yaygın bir uygulamaydı ve onlar da bunu takip ediyorlardı).

FWIW, FreeBSD manpageexec(3) de benzer davranışlardan bahseder:

 Some of these functions have special semantics.

 The functions execlp(), execvp(), and execvP() will duplicate the actions
 of the shell in searching for an executable file if the specified file
 name does not contain a slash ``/'' character. 
 …
 If the header of a file is not recognized (the attempted execve()
 returned ENOEXEC), these functions will execute the shell with the path
 of the file as its first argument.  (If this attempt fails, no further
 searching is done.)

Bununla birlikte, AFAICT, çevre üzerinde daha hassas kontrol sağlamak için yaygın olarak kullanılan kabuk kullanımı execlpveya execvpdoğrudan değildir. Hepsi aynı mantığı kullanarak uygularlar execve.


3
Ben de Linux üzerinde en azından o eklersiniz, execl, execlp, execle, execv, execvpve execvpetüm ön uçları olan execvesistem çağrısı; eski C kütüphanesi tarafından sağlanır, çekirdek sadece execve(ve execveatbugünlerde) hakkında bilir .
Stephen Kitt

@StephenKitt Bu, man7.org'un 2. bölümünde bu fonksiyonlar için neden bir manpage bulamadığımı açıklıyor.
muru

6

Bu bashdosyadaki kaynaktan bir yorum olarak Stephen Kitt'in cevabına bir ek olabilir execute_cmd.c:

Bir yerde bir disk dosyasında umutla tanımlanmış olan basit bir komutu yürütün.

1. fork ()
2. connect pipes
3. look up the command
4. do redirections
5. execve ()
6. If the execve failed, see if the file has executable mode set.  

Eğer öyleyse ve bu bir dizin değilse, içeriğini kabuk betiği olarak yürütün.


0

Bu bir kabuk komut dosyası olarak işletilirse, bu edilmektedir değil (örneğin, idam dosyasında belirlenen değişkenler dışında etkilemez) kaynaklı. Muhtemelen sisli geçmişten, bir kabuk ve bir çalıştırılabilir format varken, görkemli . Çalıştırılabilir değil, kabuk betiği olmalıdır.


2
Sorumu yanlış anladınız. Detaylı olarak ne olur ? En azından, bir shebang için çeklerin ne olduğunu anlamalıyım, o exec()mu yoksa kabuk mu? Çok daha fazla dahili istiyorum
Josh
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.