Tutorial CSI 3310 Dewan Tanvir Ahmed SITE Uof
- Slides: 33
Tutorial: CSI 3310 Dewan Tanvir Ahmed SITE, Uof. O 1
Today’s Objective q Go through the last lab q Play with q q exec() fork() Dup 2 pipe 2
Process 1. A process in Unix terms is an instance of an executing program. 2. Each process incorporates program code, the data values within program variables, and values held in hardware registers, program stack etc. 3. Associated with each process is a process control block (PCB) which stores all the information related to the process. 1. fork. . Creates a new process. 2. exec. . Overlays the memory space of the process with a new program. 3. wait. . synchronizes processes. 4. exit. . Terminates a process 3
fork() • fork() creates a new child process • Points to note – the OS copies the current program into the new process, – resets the program pointer to the start of the new program (child fork location), and – both processes continue execution independently as two separate processes • The child gets its own copy of the parent’s: – – data segments heap segment stack segment file descriptors 4
fork() Return Values • • • fork() is the one Unix function that is called once but returns twice!!: If fork() returns 0: – you’re in the new child process If fork() returns > 1 (i. e. , the pid of the new child process) – you’re back in the parent process 5
fork() 6
Waiting on Our Children • Unlike life, parents should always hang around for their children’s lives (runtimes) to end, that is to say: – Parent processes should always wait for their child processes to end • When a child process dies, a SIGCHLD signal is sent to the parent as notification • The SIGCHLD signal’s default disposition is to ignore the signal • A parent can find out the exit status of a child process by calling one of the wait() functions 7
Problem Children: Orphans and Zombies • If a child process exits before it’s parent has called wait(), it would be inefficient to keep the entire child process around, since all the parent is going to want to know about is the exit status: – A zombie is a child process that has exited before it’s parent’s has called wait() for the child’s exit status – A zombie holds nothing but the child’s exit status (held in the program control block) – Modern Unix systems have init (pid == 1) adopt zombies after their parents die, so that zombies do not hang around forever as they used to, in case the parent never did get around to calling wait 8
Points to note #include <sys/types. h> #include <unistd. h> #include <stdio. h> int main(void){ pid_t fork_pid; FILE *my_file; my_file=fopen("MY_FORK_FILE. txt", "w"); fprintf(my_file, " Hello from main n"); fork_pid=fork(); fprintf(my_file, " After fork %dn", fork_pid); return 0; } Hello from main After fork 0 Hello from main After fork 14818 The first thing you should note is that the output from the child process appears in the same file as the parent process. One characteristic of fork is that all file descriptors that are open in the parent are duplicated in the child. 9
Exec • The only way in which – a program is executed in Unix is for an existing program to issue an exec system call. – The exec calls overlays the memory space of the calling process with a new program. – When used together, fork and exec provides the means for the programmer to start another program, yielding multitasking. 10
Exec(). . 11
The exec() Functions: Out with the old, in with the new • The exec() functions all replace the current program running within the process with another program • There are two families of exec() functions, – the “l” family (list), and – the “v” family (vector) • What is the return value of an exec() call? 12
The execl. . . functions • int execl(const char * path, const char * arg 0, . . . ); – executes the command at path, – passing it the environment as a list: arg 0. . . argn – thus, the execl family breaks down argv into its individual constituents, and then passes them as a list to the execl? • int execlp(const char * path, const char * arg 0, . . . ); – same as execl, but – uses $PATH resolution for locating the program in path, thus an absolute pathname is not necessary • int execle(const char * path, const char * arg 0, . . . char * envp[]); – allows you to specifically set the new program’s environment, which replaces the default current program’s environment 13
The execv. . . functions • int execv(const char * path, char *const argv[]); – executes the command at path, – passing it the environment contained in a single argv[] vector • int execvp(const char * path, char *const argv[]); – same as execv, – but uses $PATH resolution for locating the program in path • int execve(const char * path, char *const argv[], char * const envp[]); – note that this is the only system call of the lot 14
File Descriptor • Contains – reference to system file structure • Denoted by small integer • • – passed to system calls to specify which process-to-file connection should be affected Created by open( ), dup 2( ), pipe( ), socket( ) Removed by close( ) Duplicated by fork() Retained across exec() (unless FD_CLOEXEC) 15
Standard File Descriptors • Each process normally has file descriptors for standard input, standard output, standard error • Denoted by – STDIN_FILENO – STDOUT_FILENO – STDERR_FILENO • Older programs use 0, 1, 2 • Provide unbuffered access to data in files via – read( ), write( ), lseek( ) • Buffered FILE structures stdin (scanf), stdout (printf), stderr are layered on unbuffered files 16
Inter Process Communication (IPC) • For processes to cooperate in performing a task, they need to share data. • The fundamentals of network programming lies in processes' ability to share data among themselves. • Unix allows concurrent processes to communicate by using a variety of IPC methods – – – Signals message queues shared memory semaphores and sockets. 17
Pipe • Pipe is called with a two integer array • hold the file descriptors associated with the pipe – the first element in the array (p[0]) opens the pipe for reading – while the other file descriptor (p[1]) opens the pipe for writing. • Once created we can use pipes to communicate with the process itself or with any child process. 18
Concept… Stage 1 The original process A (the shell for example) creates a pipe, receiving descriptors on each end. Stage 2 Process A forks twice, creating child processes B and C. The children inherit the parent's open descriptors, including those on the two ends of the pipe, so at this stage, all three processes have descriptors on both ends of the pipe. 19
Concept… Stage 3 Each process closes the descriptors it doesn't need. B closes the downstream end, C closes the upstream end, and A closes both. Note that B and C inherit their own copies of A's descriptors, so that closing a descriptor in the parent doesn't affect the child's descriptors. 20
Concept… Stage 4 Processes B and C exec() the programs they need to run. Again, the descriptors remain intact across the exec() call. B can then write to the upstream end of the pipe, and C can read from the downstream end. Inter Process communication!!! 21
Pipe • Note: A buffer is a system maintained data structure accessible to both ends of the pipe. • The writer writes on to the buffer while the reader reads from it. • How do we access the pipe? – Obviously the array is not going to exist after the exec(), so how do we access the pipe? – Before the exec(), we have to reassign the standard output of the process to the pipe. – We can do this using a system call to duplicate file descriptors called “dup 2()” 22
dup 2() • dup 2(target, new) • The file descriptor new will point to the same file as target after the call. • If our target is the standard output stream of the process, then we will redirect the standard output to the pipe. • The standard output will survive the exec() call. 23
close() • The close() system call is used to close pipes and files. • We need to close() the open pipes in the child process before we exec() the new process. • If not, the open pipes may be destroyed by the exec() call, also destroying our newly created connection. 24
Example: ls > temp Create a subprocess from program “ls”; redirect standard output of “ls” into file named “temp” Unix shell notation: $ ls > temp int main(int argc, char *argv[]) { pid = fork(); if (pid == 0) { fd = open("temp", O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU); dup 2(fd, STDOUT_FILENO); if (execl(“/bin/ls”, “ls”, NULL) == -1) perror("execl"); } else { close(fd); wait(&status); } } 25
Example – Execl/Execlp #include <unistd. h> #include <stdio. h> int main(void){ int status; if (fork()){ // Parent: Do you know why? } else { // Child printf(" HELLO from Child n"); status=execl("/bin/ls", "-l", "-t", '