30 exec 1 EXECVE2 NAME Linux Programmers Manual

  • Slides: 40
Download presentation
제 30강 : exec() 1

제 30강 : exec() 1

EXECVE(2) NAME Linux Programmer's Manual EXECVE(2) execute program SYNOPSIS #include <unistd. h> Child’s a.

EXECVE(2) NAME Linux Programmer's Manual EXECVE(2) execute program SYNOPSIS #include <unistd. h> Child’s a. out Parent passes arguments to child int execve (const char *filename, char *const argv [], char *const envp[]); DESCRIPTION execve() executes the program pointed to by filename must be either a binary executable, or a script starting with a line of the form "#! interpreter [arg]". …. argv is an array of argument strings passed to the new program. envp is an array of strings, conventionally of the form key=value, which are passed as environment to the new program. (env_var_name, env_var_value) Both, argv and envp must be terminated by null pointer. The argument vector and environment can be accessed by the called program's main function, when it is defined as int main(int argc, char *argv[], char *envp[]). execve() does not return on success, and the text, data, bss, and stack of the calling process are overwritten by that of the program loaded. The program invoked inherits the calling process's PID, and any open file descriptors that are not set to on exec. (childclose program will invoke exit() system call at termination -- compiler puts it) 2

(1) prompt % (2) command % cat ch 1 ch 2> ch 3 parent

(1) prompt % (2) command % cat ch 1 ch 2> ch 3 parent sh command line argument main() { loop: printf (prompt) scanf (eg cat ch 1 ch 2 > ch 12) pid=fork() if (child) exec(“/bin/cat”) else /* parent */ wait() go to top } 3

(1) prompt % (2) command % cat ch 1 ch 2> ch 3 parent

(1) prompt % (2) command % cat ch 1 ch 2> ch 3 parent sh command line argument pa va ren ria t’s bl e main() { loop: printf (prompt) scanf (eg cat ch 1 ch 2 > ch 12) pid=fork() if (child) exec(“/bin/cat”) else /* parent */ wait() go to top } 4

parent sh child sh main() { loop: printf (prompt) scanf (eg cat ch 1

parent sh child sh main() { loop: printf (prompt) scanf (eg cat ch 1 ch 2 > ch 12) pid=fork() if (child) exec(“/bin/cat”) else /* parent */ wait() go to top } 5

parent sh child sh main() { loop: printf (prompt) scanf (eg cat ch 1

parent sh child sh main() { loop: printf (prompt) scanf (eg cat ch 1 ch 2 > ch 12) pid=fork() if (child) exec(“/bin/cat”) else /* parent */ wait() go to top } 6

cat ch 1 ch 2 { sh (parent) main() fork() 2 sh { 1

cat ch 1 ch 2 { sh (parent) main() fork() 2 sh { 1 call newproc() to create a child { copy proc If (PID=fork()) copy image PID=0 rerturn(0) exec () /*child*/ PID<>0 wait() /*parent*/ exec() { overlay wait() swtch() { parent retire proc[0] child arise return(1) 7

fork() sh (parent) main() 2 sh call newproc() If (PID=fork()) copy proc 3* copy

fork() sh (parent) main() 2 sh call newproc() If (PID=fork()) copy proc 3* copy image PID=0 rerturn(0) { 1 exec () /*child*/ PID<>0 wait() /*parent*/ entered newproc() sub-shell (child) main() { If (PID=fork()) PID=0 exec () /*child*/ PID<>0 wait() /*parent*/ exec() Stack: copy parent’s image overlay entered fork() Also copy parent’s ppda Stack: entered newproc() entered fork() wait() swtch() 3* child created (ready) parent retire proc[0] child arise return(1) 8

fork() call newproc() sh (parent) main() sh { 1 If (PID=fork()) PID=0 5 child

fork() call newproc() sh (parent) main() sh { 1 If (PID=fork()) PID=0 5 child (sub-shell) created exec () /*child*/ sub-shell (child) newproc() main() copy proc { copy image rerturn(0) PID<>0 wait() /*parent*/ 4 If (PID=fork()) PID=0 exec () /*child*/ PID<>0 wait() /*parent*/ exec() overlay Stack: entered newproc() entered fork() wait() swtch() 3* child created (ready) parent retire proc[0] child arise return(1) 9

fork() call newproc() sh (parent) main() sh { newproc() 5 1 copy proc sh

fork() call newproc() sh (parent) main() sh { newproc() 5 1 copy proc sh (child) main() { If (PID=fork()) copy image If (PID=fork()) PID=0 rerturn(0) PID=0 exec () /*child*/ PID<>0 wait() /*parent*/ 6 parent wants to rest exec () /*child*/ PID<>0 wait() /*parent*/ exec() overlay Stack: entered newproc() entered fork() wait() 6* swtch() 3* child created (ready) 7* pop return address 12* exec() at 10 is done back to user mode parent retire proc[0] child arise return(1) 10

fork() call newproc() sh (parent) main() sh { 1 newproc() copy proc sh (child)

fork() call newproc() sh (parent) main() sh { 1 newproc() copy proc sh (child) main() { If (PID=fork()) copy image If (PID=fork()) PID=0 rerturn(0) PID=0 exec () /*child*/ PID<>0 wait() /*parent*/ exec() overlay Stack: entered newproc() entered fork() wait() swtch() 3* child created (ready) 7* pop return address 12* exec() at 10 is done back to user mode parent retire proc[0] child arise return(1) Child runs on CPU 11

fork() call newproc() sh (parent) main() sh newproc() sh (child) 8 copy proc {

fork() call newproc() sh (parent) main() sh newproc() sh (child) 8 copy proc { main() { If (PID=fork()) copy image If (PID=fork()) PID=0 rerturn(0)pop & returns PID=0 exec () /*child*/ to fork() PID<>0 wait() /*parent*/ exec () /*child*/ PID<>0 wait() /*parent*/ exec() overlay Stack: entered newproc() entered fork() wait() swtch() 3* child created (ready) 7* pop return address 12* exec() at 10 is done back to user mode parent retire proc[0] child arise return(1) 7* 12

sh (parent) main() sh fork() call newproc() return 9 -1 newproc() finish kernel mode

sh (parent) main() sh fork() call newproc() return 9 -1 newproc() finish kernel mode copy proc { sh (child) main() { return to where? 9 -2 same place as parent If (PID=fork()) copy image If (PID=fork()) PID=0 rerturn(0) PID=0 exec () /*child*/ PID<>0 wait() /*parent*/ exec() overlay wait() swtch() 3* child created (ready) 7* pop return address 12* exec() at 10 is done back to user mode parent retire Stack: entered fork() On return from fork() 9 -3 kernel stack becomes empty proc[0] child arise return(1) 13

fork() call newproc() sh (parent) main() sh newproc() copy proc { If (PID=fork()) copy

fork() call newproc() sh (parent) main() sh newproc() copy proc { If (PID=fork()) copy image PID=0 rerturn(0) exec () /*child*/ PID<>0 wait() /*parent*/ sh (child) main() { Return from fork() PID = = 0 for child If (PID=fork()) PID=0 exec() /*child*/ PID<>0 wait() /*parent*/ exec() overlay Stack wait() swtch() 3* child created (ready) 7* pop return address 12* exec() at 10 is done back to user mode parent retire proc[0] child arise return(1) 14

fork() call newproc() sh (parent) main() sh newproc() copy proc { sh (child) main()

fork() call newproc() sh (parent) main() sh newproc() copy proc { sh (child) main() { If (PID=fork()) copy image If (PID=fork()) PID=0 rerturn(0) PID=0 exec () /*child*/ PID<>0 wait() /*parent*/ exec () /*child*/ 10 PID<>0 wait() /*parent*/ exec() overlay wait() Stack entered exec() kernel stack changes swtch() 3* child created (ready) 7* pop return address 12* exec() at 10 is done back to user mode parent retire proc[0] child arise return(1) 15

cat ch 1 ch 2 call newproc() sh (parent) main() fork() sh newproc() copy

cat ch 1 ch 2 call newproc() sh (parent) main() fork() sh newproc() copy proc { sh (child) main() { If (PID=fork()) copy image If (PID=fork()) PID=0 rerturn(0) PID=0 exec () /*child*/ PID<>0 wait() /*parent*/ cat ch 1 exec( ) /*child*/ 10 PID<>0 wait() /*parent*/ exec() overlay parameters of exec( ) wait() swtch() 3* child created (ready) 7* pop return address 12* exec() at 10 is done back to user mode exec (pathname, argv, envp) parent retire proc[0] child arise return(1) 16

cat ch 1 ch 2 call newproc() sh (parent) main() fork() sh newproc() copy

cat ch 1 ch 2 call newproc() sh (parent) main() fork() sh newproc() copy proc { sh (child) main() { If (PID=fork()) copy image If (PID=fork()) PID=0 rerturn(0) PID=0 exec () /*child*/ PID<>0 wait() /*parent*/ cat ch 1 exec( ) /*child*/ 10 PID<>0 wait() /*parent*/ exec() overlay Stack: entered exec() wait() swtch() 3* child created (ready) 7* pop return address 12* exec() at 10 is done back to user mode 11 parent retire proc[0] cat child arise return(1) 17

cat ch 1 ch 2 call newproc() sh (parent) main() fork() sh main() newproc()

cat ch 1 ch 2 call newproc() sh (parent) main() fork() sh main() newproc() copy proc { If (PID=fork()) copy image PID=0 rerturn(0) exec () /*child*/ cat (child) PID<>0 wait() /*parent*/ { new code cat Stack: empty user stack exec() overlay wait() swtch() 3* child created (ready) 7* pop return address 12* exec() at 10 is done back to user mode 11 parent retire proc[0] cat child arise return(1) 18

PA fork PA PA exec P’A PB (New a. out ) • To create

PA fork PA PA exec P’A PB (New a. out ) • To create a new process: – Allocate & init proc[], ready queue – Allocate & init swappable image --- fork() – load a. out from disk (overlay) – init user stack for main(argv, envp) – Start from main() --- exec() 19

Algorithm of exec() – – – load a. out from disk (overlay) init user

Algorithm of exec() – – – load a. out from disk (overlay) init user stack for main(argv, envp) Start from main() Get pathname “/bin/cat” Get inode Is this file executable? Copy argv, envp from caller to kernel …. (first, allocate kernel buffer, then copy) Copy argv, envp from buf to child’s stack (as passing para to main()). . . Yes. . it is like an IPC. . kernel in between 20

3034 get pathname, map it to inode pointer uchar() (7686) function name alone=pointer to

3034 get pathname, map it to inode pointer uchar() (7686) function name alone=pointer to function, “goto u & get char” returns string (defining pathname) pointed to by u -> u. u_dirp: value of 1 st argument (pathname) namei() (7518) maps: [pathname -> *inode] 3041 Is it executable? access() (6732) check mode permission (r_w_x) -- return(1) on error (6779) 21

prepare to read by readi() readi( ) parameters 6221 • from (file, offset) to

prepare to read by readi() readi( ) parameters 6221 • from (file, offset) to (address, count) ip u_offset u_base u_count [3] Memory [1] Which file? Inode Address? u_base [4] How many? u_count (ip) [2] file offset u_offset 22

3086 prepare to read executable file (6221 readi() needs them) Read first (u_offset =

3086 prepare to read executable file (6221 readi() needs them) Read first (u_offset = 0) 8 bytes (u_count=8) u_base is destination address (6216) set to &u. u_arg[0] i. e. “read into u_arg[0]” read 1 st 8 bytes into u. u_arg[0] (u_base=u_uarg[0]) readi() w 0: file type, w 1/w 2/w 3: sizes of each segments 3095 check file type (pure code - separate text & data? ) 23

a. out format w 0 8 bytes w 1 w 2 w 3 magic

a. out format w 0 8 bytes w 1 w 2 w 3 magic number text size data size bss size symbol table size entry point relo. info. “Magic number” first few bytes of executable file Binary Standard “COFF & ELF” (trend – ELF supports dynamic libraries) Script If first 2 bytes is # ! – pathname of interpreter (from BSD --- to Linux) 24

8 words We just read 8 bytes from a. out and we checked file

8 words We just read 8 bytes from a. out and we checked file type We need to read body of a. out First, need to allocate memory space So find out the size of text, data segment u. u_arg[] magic # W 1 (text size) W 2 (data size) W 3 (bss size) others 3129 adjust memory space according to the finding expand() xalloc() is at 4433 and is for shared code address of user space is now determined 3138 estabur() again (save addresses in struct u) 3141 prepare to read a. out file – read the rest of a. out parameters for readi() 3142 read data segment (i. e. main body of a. out) 25

Passing arguments to child process 26

Passing arguments to child process 26

EXECVE(2) NAME Linux Programmer's Manual EXECVE(2) execute program SYNOPSIS #include <unistd. h> Child’s a.

EXECVE(2) NAME Linux Programmer's Manual EXECVE(2) execute program SYNOPSIS #include <unistd. h> Child’s a. out Parent passes arguments to child int execve (const char *filename, char *const argv [], char *const envp[]); DESCRIPTION execve() executes the program pointed to by filename must be either a binary executable, or a script starting with a line of the form "#! interpreter [arg]". …. argv is an array of argument strings passed to the new program. envp is an array of strings, conventionally of the form key=value, which are passed as environment to the new program. (env_var_name, env_var_value) Both, argv and envp must be terminated by null pointer. The argument vector and environment can be accessed by the called program's main function, when it is defined as int main(int argc, char *argv[], char *envp[]). execve() does not return on success, and the text, data, bss, and stack of the calling process are overwritten by that of the program loaded. The program invoked inherits the calling process's PID, and any open file descriptors that are not set to on exec. (childclose program will invoke exit() system call at termination -- compiler puts it) 27

cat ch 1 ch 2 call newproc() sh (parent) main() sh cat ch 1

cat ch 1 ch 2 call newproc() sh (parent) main() sh cat ch 1 ch 2 { fork() newproc() copy proc If (PID=fork()) copy image PID=0 rerturn(0) exec () /*child*/ PID<>0 wait() /*parent*/ exec() sh (child) main() sh { cat ch 1 ch 2 If (PID=fork()) PID=0 exec( ) /*child*/ 10 PID<>0 wait() /*parent*/ overlay wait() swtch() 3* child created (ready) 7* pop return address 12* exec() at 10 is done back to user mode parent retire proc[0] child arise return(1) 28

cat ch 1 ch 2 call newproc() sh (parent) main() fork() sh cat (child)

cat ch 1 ch 2 call newproc() sh (parent) main() fork() sh cat (child) main() newproc() cat { copy proc { If (PID=fork()) copy image PID=0 rerturn(0) exec () /*child*/ Stack: PID<>0 wait() /*parent*/ exec() overlay Overlay wait() swtch() 3* child created (ready) 7* pop return address 12* exec() at 10 is done back to user mode 11 parent retire proc[0] cat child arise return(1) 29

We just read a. out file from disk, Next, copy argv & envp (1)

We just read a. out file from disk, Next, copy argv & envp (1) Kernel: Parent sh forks child sh user k-stack struct Kernel (2) Child sh calls exec() a. out (0) cat argv user k-stack struct Parent sh copy to kernel buffer copy from kernel buffer (3) exec() <3 -1> copy argv, envp a. out from child-sh’s user var to kernel buffer u--stack (1) user k-stack struct Child sub-sh a. out u--stack (2) argv envp load overlay (3) cat <3 -2> load cat a. out (4) user k-stack struct cat a. out u--stack overwriting shell <3 -3> copy argv, envp from kernel buffer to child’s user stack 30

31

31

3040 bp=getblk() (4921) allocate a kernel buffer (from buffer cache) 3049 Copy from caller

3040 bp=getblk() (4921) allocate a kernel buffer (from buffer cache) 3049 Copy from caller to buffer (3049 3052 3059. . ) Remaining sys call arguments, following pathname (filename, argv, envp, …. ) 3049 cp points to actual kernel buffer (not buffer header) 3052 ap = fuword(); 3058 c =fubyte(ap++); 3059 for ( ) { c = fubyte(ap++); /* byte from parent’s image*/ (*cp++ = c) /* exec() sys call arg into kernel buffer */ until ( c = = 0) at 3067 We must copy (argv, envp) into kernel buffer. BEFORE loading new a. out image from disk. 32 because current user image is being overwritten during loading

Now new image has been read from disk (at 3142) 3153 Copy from buffer

Now new image has been read from disk (at 3142) 3153 Copy from buffer to new user process stack 3153 cp points to actual kernel buffer (not header) 3161 while { 3161 subyte(c++, *cp); /* store byte into user space */ Eventually, the system call arguments are moved from previous user image kernel buffer 33 new image’s user stack

Algorithm of exec() – – – load a. out from disk (overlay) init user

Algorithm of exec() – – – load a. out from disk (overlay) init user stack for main (path, argv, envp) Start from main() Copy argv, envp from sub-shell to kernel (first, allocate kernel buffer, then copy) Copy them from kernel to new a. out’s user stack Read new a. out (Overlay old a. out) (as arguments to main()). . . Yes. . it is like an IPC. . kernel in between 34

1. Makes proc[0] then call newproc() to make proc #1 2. Alloc & copy

1. Makes proc[0] then call newproc() to make proc #1 2. Alloc & copy proc[1] alloc & copy image for proc #1 10. swtch() becomes proc #0 which selects next job retu (sh) 4. Return(0) from newproc() Call sched(). . swtch() saveu() main() proc 0 8. init creates sh 9. swtch() for terminals proc 1 3. Proc #1 is created & is in Ready state sh 7. exec init, i. e. load & run init 6. Copy array to user space & jump 5. retu(), return(1) from newproc() to main sh is created ready queue 11. sh runs (Continued) 35

User types “cat” at shell prompt … Creates child fork() Parent invokes wait() sys

User types “cat” at shell prompt … Creates child fork() Parent invokes wait() sys call 7. Returns from swtch() 1. Calls swtch() 5 End of cat code exit () sys call swtch() sh (parent) 2. scheduler proc #0 cat (child) child-sh is created & is in Ready state User mode Kernel mode 3. within swtch() calls retu (child) child is dispatched 6. scheduler chooses parent sh retu (sh) 4. child returns from swtch() returns from fork() child-sh exec(“bin/cat) 36

Creates child fork() 6 Returns from swtch() 1. Calls swtch() sh (parent) 2. scheduler

Creates child fork() 6 Returns from swtch() 1. Calls swtch() sh (parent) 2. scheduler 4 calls swtch() proc #0 3. cat dispatched 5. scheduler cat (child) Coroutine Calls: sh swtch() User mode Kernel mode cat swtch() sh 37

Creates child cat 6 Returns from swtch() 1. Calls swtch() sh (parent) 2. scheduler

Creates child cat 6 Returns from swtch() 1. Calls swtch() sh (parent) 2. scheduler 4 calls swtch() proc #0 3. cat dispatched 5. scheduler cat (child) sh swtch() User mode Kernel mode cat swtch() sh Retiring OS sched Arising 38

Fork() creates child Later exit() syscalls swtch() wait() sys call which calls swtch() sh

Fork() creates child Later exit() syscalls swtch() wait() sys call which calls swtch() sh (parent) 2. scheduler proc #0 3. Child is dispatched cat (child) sh User mode Kernel mode exec cat child: main(argv ) { finds out argv= cat any fork() exec(cat) pass “any” as argv to Pcat } 39

fork() sh (parent) main() 2 sh { 5 sh (child) newproc() main() copy proc

fork() sh (parent) main() 2 sh { 5 sh (child) newproc() main() copy proc 3* copy image 1 If (PID=fork()) PID=0 call newproc() 9 exec () /*child*/ rerturn(0) PID<>0 wait() /*parent*/ 6 exec() overlay 11 12* { 4 If (PID=fork()) PID=0 exec () /*child*/ 10 PID<>0 wait() /*parent*/ 8 entered newproc() Stack: entered fork() wait() overlay 6* swtch() 3* child created (ready) 7* pop return address 12* exec() at 10 is done back to user mode parent retire new a. out (child) proc[0] main() child arise return(1) 7* 13 exit() cat 40