Harap jelaskan fungsi exec () dan keluarganya


98

Apa exec()fungsi dan keluarganya? Mengapa fungsi ini digunakan dan bagaimana cara kerjanya?

Tolong ada yang menjelaskan fungsi-fungsi ini.


4
Cobalah untuk membaca Stevens lagi, & klarifikasi apa yang tidak Anda mengerti.
vlabrecque

Jawaban:


245

Sederhananya, di UNIX, Anda memiliki konsep proses dan program. Proses adalah lingkungan tempat program dijalankan.

Ide sederhana di balik "model eksekusi" UNIX adalah bahwa ada dua operasi yang dapat Anda lakukan.

Yang pertama adalah fork(), yang membuat proses baru yang berisi duplikat (kebanyakan) dari program saat ini, termasuk statusnya. Ada beberapa perbedaan antara kedua proses yang memungkinkan mereka untuk mengetahui mana yang merupakan induk dan mana yang merupakan anaknya.

Yang kedua adalah exec(), yang menggantikan program dalam proses saat ini dengan program baru.

Dari dua operasi sederhana tersebut, seluruh model eksekusi UNIX dapat dibangun.


Untuk menambahkan beberapa detail di atas:

Penggunaan fork()dan exec()contoh semangat UNIX menyediakan cara yang sangat sederhana untuk memulai proses baru.

The fork()panggilan membuat duplikat dekat dari proses saat ini, identik di hampir setiap jalan (tidak semuanya disalin, misalnya, batas sumber daya di beberapa implementasi, tetapi gagasan adalah untuk membuat sedekat salinan mungkin). Hanya satu panggilan proses fork()tetapi dua proses kembali dari panggilan itu - terdengar aneh tetapi sangat elegan

Proses baru (disebut anak) mendapatkan ID proses (PID) yang berbeda dan memiliki PID dari proses lama (induk) sebagai PID induknya (PPID).

Karena kedua proses sekarang menjalankan kode yang persis sama, mereka harus dapat membedakan mana - kode kembalian dari fork()memberikan informasi ini - anak mendapat 0, induk mendapatkan PID anak (jika fork()gagal, tidak anak dibuat dan orang tua mendapat kode kesalahan).

Dengan cara itu, orang tua mengetahui PID anak dan dapat berkomunikasi dengannya, mematikannya, menunggu, dan seterusnya (anak selalu dapat menemukan proses induknya dengan panggilan ke getppid()).

The exec()panggilan menggantikan isi saat seluruh proses dengan program baru. Ini memuat program ke dalam ruang proses saat ini dan menjalankannya dari titik masuk.

Jadi, fork()dan exec()sering digunakan secara berurutan untuk menjalankan program baru sebagai anak dari proses saat ini. Kerang biasanya melakukan ini setiap kali Anda mencoba menjalankan program seperti find- garpu shell, kemudian anak memuat findprogram ke dalam memori, menyiapkan semua argumen baris perintah, I / O standar, dan sebagainya.

Tapi mereka tidak harus digunakan bersama. Sangat dapat diterima untuk sebuah program untuk memanggil fork()tanpa berikut exec()jika, misalnya, program tersebut berisi kode induk dan anak (Anda perlu berhati-hati dengan apa yang Anda lakukan, setiap implementasi mungkin memiliki batasan).

Ini digunakan cukup banyak (dan masih) untuk daemon yang hanya mendengarkan pada port TCP dan menyalin salinan dirinya sendiri untuk memproses permintaan tertentu sementara induk kembali mendengarkan. Untuk situasi ini, program berisi kode induk dan anak.

Demikian pula, program yang tahu bahwa mereka telah selesai dan hanya ingin menjalankan program lain tidak perlu fork(), exec()dan kemudian wait()/waitpid()untuk anak. Mereka hanya dapat memuat anak langsung ke ruang proses mereka saat ini dengan exec().

Beberapa implementasi UNIX telah dioptimalkan fork()yang menggunakan apa yang mereka sebut copy-on-write. Ini adalah trik untuk menunda penyalinan ruang proses fork()sampai program mencoba mengubah sesuatu di ruang itu. Ini berguna untuk program-program yang hanya menggunakan fork()dan bukan exec()karena mereka tidak perlu menyalin seluruh ruang proses. Di Linux, fork()hanya membuat salinan dari tabel halaman dan struktur tugas baru, exec()akan melakukan pekerjaan kasar "memisahkan" memori dari dua proses.

Jika exec ini disebut berikut fork(dan ini adalah apa yang terjadi kebanyakan), yang menyebabkan tulis ke ruang proses dan kemudian disalin untuk proses anak, sebelum modifikasi diperbolehkan.

Linux juga memiliki vfork(), bahkan lebih dioptimalkan, yang berbagi hampir semua hal antara dua proses. Karena itu, ada batasan tertentu tentang apa yang bisa dilakukan anak, dan orang tua berhenti sampai anak memanggil exec()atau _exit().

Induk harus dihentikan (dan anak tidak diizinkan untuk kembali dari fungsi saat ini) karena kedua proses tersebut bahkan berbagi tumpukan yang sama. Ini sedikit lebih efisien untuk kasus penggunaan klasik yang fork()diikuti segera oleh exec().

Perhatikan bahwa ada seluruh keluarga execpanggilan ( execl, execle, execvedan sebagainya), tetapi execdalam konteks di sini berarti salah satu dari mereka.

Diagram berikut menggambarkan fork/execoperasi umum di mana bashshell digunakan untuk membuat daftar direktori dengan lsperintah:

+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | ls     |
    V                  +--------+
+--------+                 |
| pid=7  |                 | exits
| ppid=4 | <---------------+
| bash   |
+--------+
    |
    | continues
    V

12
Terima kasih atas penjelasannya yang terperinci :)
Faizan

2
Terima kasih atas referensi shell dengan program find. Persis apa yang perlu saya pahami.
Pengguna

Mengapa executilitas digunakan untuk mengarahkan IO proses saat ini? Bagaimana kasus "null", menjalankan exec tanpa argumen, digunakan untuk konvensi ini?
Ray

@Ray, saya selalu menganggapnya sebagai perpanjangan alami. Jika Anda menganggap execsebagai cara untuk mengganti program saat ini (shell) dalam proses ini dengan yang lain, maka tidak menentukan program lain untuk menggantikannya bisa berarti Anda tidak ingin menggantinya.
paxdiablo

Saya mengerti apa yang Anda maksud jika dengan "ekstensi alami" yang Anda maksudkan adalah sesuatu di sepanjang garis "pertumbuhan organik". Tampaknya pengalihan akan ditambahkan untuk mendukung fungsi penggantian program, dan saya dapat melihat perilaku ini tetap dalam kasus degenerasi execyang dipanggil tanpa program. Tetapi agak aneh dalam skenario ini karena kegunaan asli pengalihan untuk program baru - program yang sebenarnya akan di- executed - menghilang dan Anda memiliki artefak yang berguna, mengarahkan ulang program saat ini - yang tidak sedang di- executed atau dimulai up dengan cara apapun - sebagai gantinya.
Ray

36

Fungsi dalam keluarga exec () memiliki perilaku berbeda:

  • l: argumen diteruskan sebagai daftar string ke main ()
  • v: argumen dilewatkan sebagai larik string ke main ()
  • p: path / s untuk mencari program baru yang sedang berjalan
  • e: lingkungan dapat ditentukan oleh pemanggil

Anda dapat mencampurnya, oleh karena itu Anda memiliki:

  • int execl (const char * path, const char * arg, ...);
  • int execlp (file * const char, const char * arg, ...);
  • int execle (const char * path, const char * arg, ..., char * const envp []);
  • int execv (const char * path, char * const argv []);
  • int execvp (file * const char, char * const argv []);
  • int execvpe (file * const char, char * const argv [], char * const envp []);

Untuk semuanya, argumen awal adalah nama file yang akan dieksekusi.

Untuk informasi lebih lanjut, baca halaman manual exec (3) :

man 3 exec  # if you are running a UNIX system

1
Menariknya, Anda melewatkan execve()daftar Anda, yang ditentukan oleh POSIX, dan Anda menambahkan execvpe(), yang tidak ditentukan oleh POSIX (kebanyakan karena alasan preseden historis; ini melengkapi serangkaian fungsi). Jika tidak, penjelasan bermanfaat tentang konvensi penamaan untuk keluarga - tambahan yang berguna untuk paxdiablo ' jawaban yang menjelaskan lebih lanjut tentang cara kerja fungsi.
Jonathan Leffler

Dan, dalam pembelaan Anda, saya melihat bahwa halaman manual Linux untuk execvpe()(et al) tidak mencantumkan execve(); ia memiliki halaman manualnya sendiri yang terpisah (setidaknya di Ubuntu 16.04 LTS) - perbedaannya adalah bahwa exec()fungsi keluarga lainnya tercantum di bagian 3 (fungsi) sedangkan execve()yang tercantum di bagian 2 (panggilan sistem). Pada dasarnya, semua fungsi lain dalam keluarga diimplementasikan dalam istilah panggilan ke execve().
Jonathan Leffler

18

The execkeluarga fungsi membuat proses Anda mengeksekusi program yang berbeda, menggantikan program lama itu berjalan. Yaitu, jika Anda menelepon

execl("/bin/ls", "ls", NULL);

kemudian lsprogram dijalankan dengan id proses, direktori kerja saat ini dan pengguna / grup (hak akses) dari proses yang dipanggil execl. Setelah itu, program aslinya tidak lagi berjalan.

Untuk memulai proses baru, forkpanggilan sistem digunakan. Untuk menjalankan program tanpa mengganti asli, Anda perlu fork, kemudian exec.


Terima kasih, itu sangat membantu. Saat ini saya melakukan proyek yang mengharuskan kami menggunakan exec () dan deskripsi Anda memperkuat pemahaman saya.
TwilightSparkleTheGeek

7

apa fungsi exec dan keluarganya.

The execfungsi keluarga adalah semua fungsi yang digunakan untuk mengeksekusi file, seperti execl, execlp, execle, execv, dan execvp.Mereka semua frontends untuk execvedan memberikan metode yang berbeda dari menyebutnya.

mengapa fungsi ini digunakan

Fungsi Exec digunakan ketika Anda ingin menjalankan (meluncurkan) file (program).

dan bagaimana cara kerjanya.

Mereka bekerja dengan menimpa gambar proses saat ini dengan gambar yang Anda luncurkan. Mereka mengganti (dengan mengakhiri) proses yang sedang berjalan (yang disebut perintah exec) dengan proses baru yang telah diluncurkan.

Untuk lebih jelasnya: lihat tautan ini .


7

execsering digunakan dalam hubungannya dengan fork, yang saya lihat yang juga Anda tanyakan, jadi saya akan membahas hal ini dengan mengingatnya.

execmengubah proses saat ini menjadi program lain. Jika Anda pernah menonton Doctor Who, maka ini seperti saat dia beregenerasi - tubuh lamanya diganti dengan tubuh baru.

Cara ini terjadi dengan program Anda dan execadalah bahwa banyak sumber daya yang diperiksa kernel OS untuk melihat apakah file yang Anda berikan execsebagai argumen program (argumen pertama) dapat dieksekusi oleh pengguna saat ini (id pengguna proses melakukan execpanggilan) dan jika demikian itu menggantikan pemetaan memori virtual dari proses saat ini dengan memori virtual proses baru dan menyalin argvdan envpdata yang diteruskan dalam execpanggilan ke area peta memori virtual baru ini. Beberapa hal lain juga dapat terjadi di sini, tetapi file yang dibuka untuk program yang dipanggil execakan tetap terbuka untuk program baru dan mereka akan berbagi ID proses yang sama, tetapi program yang dipanggil execakan berhenti (kecuali jika exec gagal).

Alasan bahwa hal ini dilakukan dengan cara ini adalah bahwa dengan memisahkan menjalankan sebuah baru Program menjadi dua langkah seperti ini Anda dapat melakukan beberapa hal antara dua langkah. Hal yang paling umum dilakukan adalah memastikan bahwa program baru memiliki file tertentu yang dibuka sebagai deskriptor file tertentu. (ingat di sini bahwa deskriptor file tidak sama FILE *, tetapi merupakan intnilai yang diketahui kernel). Dengan melakukan ini, Anda dapat:

int X = open("./output_file.txt", O_WRONLY);

pid_t fk = fork();
if (!fk) { /* in child */
    dup2(X, 1); /* fd 1 is standard output,
                   so this makes standard out refer to the same file as X  */
    close(X);

    /* I'm using execl here rather than exec because
       it's easier to type the arguments. */
    execl("/bin/echo", "/bin/echo", "hello world");
    _exit(127); /* should not get here */
} else if (fk == -1) {
    /* An error happened and you should do something about it. */
    perror("fork"); /* print an error message */
}
close(X); /* The parent doesn't need this anymore */

Ini menyelesaikan lari:

/bin/echo "hello world" > ./output_file.txt

dari shell perintah.


5

Ketika sebuah proses menggunakan fork (), itu membuat salinan duplikatnya sendiri dan duplikat ini menjadi anak dari proses tersebut. Fork () diimplementasikan menggunakan system call clone () di linux yang mengembalikan dua kali dari kernel.

  • Nilai bukan nol (ID Proses anak) dikembalikan ke induk.
  • Nilai nol dikembalikan ke anak.
  • Jika anak tidak berhasil dibuat karena masalah seperti memori rendah, -1 dikembalikan ke fork ().

Mari kita pahami ini dengan sebuah contoh:

pid = fork(); 
// Both child and parent will now start execution from here.
if(pid < 0) {
    //child was not created successfully
    return 1;
}
else if(pid == 0) {
    // This is the child process
    // Child process code goes here
}
else {
    // Parent process code goes here
}
printf("This is code common to parent and child");

Dalam contoh, kita berasumsi bahwa exec () tidak digunakan di dalam proses anak.

Tetapi orang tua dan anak berbeda dalam beberapa atribut PCB (blok kontrol proses). Ini adalah:

  1. PID - Anak dan orang tua memiliki ID Proses yang berbeda.
  2. Sinyal Tertunda - Anak tidak mewarisi sinyal tertunda dari Orang Tua. Ini akan kosong untuk proses anak saat dibuat.
  3. Kunci Memori - Anak tidak mewarisi kunci memori orang tuanya. Kunci memori adalah kunci yang dapat digunakan untuk mengunci area memori dan kemudian area memori ini tidak dapat ditukar ke disk.
  4. Record Locks - Anak tidak mewarisi kunci record orang tuanya. Kunci rekaman dikaitkan dengan blok file atau seluruh file.
  5. Pemanfaatan sumber daya proses dan waktu CPU yang dikonsumsi disetel ke nol untuk anak.
  6. Anak itu juga tidak mewarisi timer dari orang tuanya.

Tapi bagaimana dengan ingatan anak? Apakah address space baru dibuat untuk anak?

Jawabannya tidak. Setelah fork (), orang tua dan anak berbagi ruang alamat memori induk. Di linux, ruang alamat ini dibagi menjadi beberapa halaman. Hanya ketika anak tersebut menulis ke salah satu halaman memori induk, duplikat dari halaman itu dibuat untuk anak tersebut. Ini juga dikenal sebagai salin saat menulis (Salin halaman induk hanya saat anak menulis padanya).

Mari kita pahami copy on write dengan sebuah contoh.

int x = 2;
pid = fork();
if(pid == 0) {
    x = 10;
    // child is changing the value of x or writing to a page
    // One of the parent stack page will contain this local               variable. That page will be duplicated for child and it will store the value 10 in x in duplicated page.  
}
else {
    x = 4;
}

Tapi mengapa copy on write diperlukan?

Pembuatan proses yang umum terjadi melalui kombinasi fork () - exec (). Pertama mari kita pahami apa yang dilakukan exec ().

Grup fungsi Exec () menggantikan ruang alamat anak dengan program baru. Setelah exec () dipanggil di dalam anak, ruang alamat terpisah akan dibuat untuk anak tersebut yang sama sekali berbeda dari induknya.

Jika tidak ada salinan pada mekanisme tulis yang terkait dengan fork (), halaman duplikat akan dibuat untuk anak tersebut dan semua data akan disalin ke halaman anak. Mengalokasikan memori baru dan menyalin data adalah proses yang sangat mahal (membutuhkan waktu prosesor dan sumber daya sistem lainnya). Kita juga tahu bahwa dalam banyak kasus, anak akan memanggil exec () dan itu akan mengganti memori anak dengan program baru. Jadi copy pertama yang kami buat akan sia-sia jika copy on write tidak ada.

pid = fork();
if(pid == 0) {
    execlp("/bin/ls","ls",NULL);
    printf("will this line be printed"); // Think about it
    // A new memory space will be created for the child and that   memory will contain the "/bin/ls" program(text section), it's stack, data section and heap section
else {
    wait(NULL);
    // parent is waiting for the child. Once child terminates, parent will get its exit status and can then continue
}
return 1; // Both child and parent will exit with status code 1.

Mengapa orang tua menunggu proses anak?

  1. Orang tua dapat menetapkan tugas ke anaknya dan menunggu sampai tugas itu selesai. Kemudian bisa melakukan beberapa pekerjaan lain.
  2. Setelah anak itu berakhir, semua sumber daya yang terkait dengan anak dibebaskan kecuali untuk blok kontrol proses. Sekarang, anak itu dalam keadaan zombie. Menggunakan wait (), orang tua dapat menanyakan tentang status anak dan kemudian meminta kernel untuk membebaskan PCB. Jika orang tua tidak menggunakan menunggu, anak tersebut akan tetap berada dalam status zombie.

Mengapa panggilan sistem exec () diperlukan?

Tidak perlu menggunakan exec () dengan fork (). Jika kode yang akan dieksekusi oleh anak berada dalam program yang diasosiasikan dengan induk, exec () tidak diperlukan.

Tetapi pikirkan kasus ketika anak harus menjalankan banyak program. Mari kita ambil contoh program shell. Ini mendukung beberapa perintah seperti find, mv, cp, date dll. Apakah benar untuk memasukkan kode program yang terkait dengan perintah ini dalam satu program atau meminta anak memuat program ini ke dalam memori saat diperlukan?

Itu semua tergantung pada kasus penggunaan Anda. Anda memiliki server web yang diberi masukan x yang mengembalikan 2 ^ x ke klien. Untuk setiap permintaan, server web membuat anak baru dan memintanya untuk menghitung. Apakah Anda akan menulis program terpisah untuk menghitung ini dan menggunakan exec ()? Atau Anda hanya akan menulis kode komputasi di dalam program induk?

Biasanya, pembuatan proses melibatkan kombinasi panggilan fork (), exec (), wait () dan exit ().


4

The exec(3,3p)fungsi menggantikan proses saat ini dengan yang lain. Artinya, proses saat ini berhenti , dan proses lain berjalan sebagai gantinya, mengambil alih beberapa sumber daya yang dimiliki program asli.


6
Tidak terlalu. Ini menggantikan gambar proses saat ini dengan gambar proses baru. Prosesnya adalah proses yang sama dengan pid yang sama, lingkungan yang sama, dan tabel deskriptor file yang sama. Yang berubah adalah seluruh memori virtual dan status CPU.
JeremyP

@JeremyP "Deskriptor file yang sama" penting di sini, ini menjelaskan cara kerja pengalihan di shell! Saya bingung bagaimana cara kerja pengalihan jika eksekutif menimpa semuanya! Terima kasih
FUD
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.