Download presentation
Presentation is loading. Please wait.
Published byいつや なかじゅく Modified over 5 years ago
1
CS 201 Computer Systems Programming Chapter 8 “Unix fork(), execve()”
Herbert G. Mayer, PSU CS Status 7/15/2014
2
Syllabus Process, Thread, Hyperthread Unix Process Interrupt
Command ps Background Process Command fork() fork() Sample Command execve() execve() Sample References
3
Process – SW View [Bryant, 16] “A process is the operating system’s abstraction of a running program.” [Silberschatz, 10] “A process can be thought of as a program in execution …” [Silberschatz, 90] “Informally, a process is a program in execution. … A process is more than the program code. It also includes the current activity, as represented by the value of the program counter and the contents of the processor’s registers.” [Tanenbaum, 72] “A process is just an executing program, including the current values of the program counter, registers, and variables.”
4
Thread – SW View [Bryant, 947] “A thread is a logical flow that runs in the context of a process. Each thread has its own thread context, including a unique integer thread ID, stack, stack pointer, program counter, general-purpose registers, and condition codes. All threads running in a process share the entire virtual address space of that process.” [Silberschatz, 103] “A thread, sometimes called a lightweight process, is a basic unit of CPU utilization, and consists of a program counter, a register set, and a stack space. It shares with peer threads its code section, data section, and operating-system resources such as open files and signals.” [Tanenbaum, 81] “A thread has a program counter that keeps track of which instruction to execute next. It has registers, which hold its current working variables. It has a stack, which contains the execution history, with one frame for each procedure called, but not yet returned from. ... A thread must execute in some process”
5
Thread Creation Who makes threads?
Programmer understands which portions of C program are dependent-, and which ones are independent of other parts of the same program –which when executed is a process MS and Intel compiler provide tools to support this analysis Some C and C++ compilers provide directives Directives can be hidden as comments, to be transparent to other compilers See lit ref [9] for Intel’s Parallel Studio, helping programmer eliminated threading errors in C and C++ programs Or see Intel’s [10] [12] to help programmer “to thread” SW Or Microsoft Multi-Threading support for existing C and C++ source code [11]
6
Hyperthread – HW View A processor may be single-core (UP), or have multiple cores, the latter meaning: all processor resources are replicated; e.g. Intel Core 2 Duo Or a processor may be single-core, hyperthreaded, e.g. Intel Pentium 4e. Hyperthreaded means that only CPU registers and APIC are replicated, but not ALU units, such as integer unit, floating-point unit, branch unit, caches, etc. Or a processor may be multi-core, hyperthreaded, in which case each of several real cores has a hyperthread twin (or more), sharing the real core’s ALU with all hyperthreads, but each hyperthread has own register + APIC; e.g. Intel Core i7 Hyperthreading is an old idea, proposed decades ago by Digital Equipment Corp. (DEC), implemented first in silicon by Intel in 2002 on Xeon® server + Pentium® 4 desktop CPUs Hyperthread is an overloaded term, referring to the reduced Silicon core, as well as the active SW thread executing on it Should be named: Hypothread since it is a thread-subset, but Hyperthread IS THE accepted technical term
7
Hyperthread – Per Intel Website
Ability of processor to run concurrent threads quickly Some microprocessor hardware replication that creates the illusion to SW of Dual Processor (DP) Yet such HW with some resource replication is NOT a true dual-core silicon implementation The execution unit is still shared between multiple threads Effect of Hyperthreading on Xeon® Processor: Average CPU utilization increases to ~50%, down from ~35% for a typical uni-processor Up to ~30% performance gain for some applications with the same processor frequency Hyperthreading Technology Results: 1. More performance with enabled applications 2. Better responsiveness with existing applications
8
Hyperthread – Per Intel Website
Almost two Logical Processors Architecture state (registers) and APIC* replicated Shares execution units, caches, branch prediction, control logic and buses Processor Execution Resource Adv. Programmable Interrupt Control Architecture State On-Die Caches *APIC: Advanced Programmable Interrupt Controller. Handles interrupts sent to a specified logical processor System Bus
9
Hyperthread Hyperthreaded core replicates in silicon all CPU resources essential to switching fast from one thread to another Those replicated resources are registers and the APIC. ALU units are not replicated on a hyperthread core SW hyperthreads are also threads, but execute concurrently on HW hyperthread cores; switch is efficient due to available registers; ALU-sharing is still necessary: Only a single ALU! This costs ~5% more HW (silicon) than a single core, but can gain up to 30% performance improvement; great ROI!
10
Delta Between Process & Thread
Process is an OS-centric view of a running program. Due to the meaning of “running”, all machine resources are necessary to execute a process A thread is a subset of a process, not necessarily a proper subset One purpose for threading a process is to allow continued execution of that one process, even when some part of it has to wait; e.g. one thread waits for an IO operation to finish, but another thread can continue, being independent of the IO result Purpose for having threads execute a process is to speed up overall execution. Possible, if multiple threads of the same process are sufficiently data- independent to progress concurrently; never simultaneously on a single core! A threaded process can –sometimes-- execute faster on a single core unit due to reduced waits
11
Delta Between Thread & Hyperthread
A hyperthreaded core almost creates illusion of a multi-core CPU, though there is only 1 complete CPU Enables concurrent execution on a uni-processor, when one process thread stalls and another thread is ready; switch to the other thread is cheap, since the other register set already has proper state –except the first time around But hyperthreading (or threading) per se never allows parallel execution; only a multi-core architecture does A System Programmer must know: On MP OS not yet tuned for hyperthreading: best disable hyperthread scheduling; else under the right (i.e. wrong) circumstances performance degradation can result: This is the case, when the “next core to be scheduled” happens to be regularly the hyperthreaded subset-core, while leaving other real cores idle, though a real core could work instead of the hyperthread
12
Unix Process Under Unix, a process is an instance of running a program. If you execute the ancient editor ed and your colleague does too, then there are 2 --very similar– different processes running All user a.out programs and all Unix commands, when running, become processes; commands ll and g++ my_prog.cpp create 2 different processes Processes are visible via ps command, and even that command is a process in its own right Issue the command ps, for process status, and you see your current processes, plus the ps processes Issue ps –a, and you see a very detailed list, including sleeping processes Issue the command man ps for your education
13
Unix Process When a command is issued, Unix starts new process, suspends the current OS process, generally the C-shell, until the new child process completes Exception: background processes with & Unix identifies every process by a Process Identification Number (PID) assigned at initiation See getpid() function calls below Unix is a timesharing system, which grants each process time-slices Allocation of time-slices has to be fair, so that one long process cannot make other, shorter ones, wait for extended periods (no starvation!) Also, time-slicing has to be efficient, lest too much processing time migrates into overhead, like a typical state government
14
Interrupt Def: Program interrupt is a transparent, non- scheduled change in execution flow with specific cause outside the program, treated by an interrupt handler, ending up at the original program again Is unpredictable: Programmer does not know that, when, why, where interrupt happens Is unexpected: Programmer does not know when interrupt happens, or whether it happens Cannot be pinpointed (i.e. coded): Programmer does not know where such an interrupt happens Cause is known, passed to interrupt handler by HW, yet the SW program (i.e. process) does not know a-priori that it will happen; can be some external event like power-outage, or time-slice consumption (timer interrupt), numeric error
15
Interrupt After handling, execution continues at place after the interrupt Challenge: if interrupt happens during execution of some long instruction, say move of a large portion of memory by a byte-move instruction Challenge caused by general need of handling interrupt swiftly, more swiftly than the time needed for some long machine instructions! Interrupt handled in a way that the program never knows it was interrupted: transparent Except that execution ends up slower than expected; slower than if the interrupt had not happened Note: x86 INT instruction is not an interrupt! Though it is named a “software interrupt” instruction; is it totally predictable, locatable!
16
Process-Related Unix Commands
17
Command ps Command ps without any option shows all processes with the same controlling terminal and same user id as the invoker of the ps command Complex command with numerous options! PID identifies the process, TT the controlling terminal, S the state, and TIME the CPU time consumed for that process [process] ps PID TT S TIME COMMAND 8687 pts/33 O 0:00 ps -- O running 19212 pts/33 S 0:00 –csh -- S sleeping
18
Command ps The command ps –a prints information about all active processes; can result in a long list, e.g.: ps –a | more PID TT S TIME COMMAND Z 0:00 Z 0:00 523 console S 0:00 /usr/lib/saf/ttymon -g -d /dev/console -l console -m ld 19668 pts/1 S 0:00 -tcsh 19691 pts/1 S 0:00 tcsh 19705 pts/1 S 0:07 pine 22394 pts/2 S 0:00 -bash 22412 pts/2 S 0:06 screen . . .
19
Command kill To abort, AKA kill, any process in Unix whose PID is known, issue the command kill with argument -9, e.g., the -9 meaning “death” , see below: kill – Which in this particular case was the instructor’s remote shell log-in to PSU’s computer, and as a result he had to log in again from home Process status Z means “zombie”, typically a child process that has terminated, but the parent process still needs to know its termination status Killing a Z process has no effect; good so, else parent would never know status of terminated child; see [8] for detail
20
Background Process & Some processes may run forever
Others in your environment may run for a limited time but for quite long What if you do not wish to wait for long process completion before continuing with other work? Possible in Unix with background processes initiated by the & command modifier, such as: run_big & Which executes the long running program run_big in the background; making progress whenever CPU cycles are available Your real interactive work may proceeded in parallel –on a UP implied here: but concurrently!
21
Background Process & What happens if this program generates output, such as messages to stderr? These would be interspersed with other output generated concurrently, causing confusion! To avoid mixing of messages, stderr can be redirected to a separate file using >& g++ big_program.cpp >& my_errors & . . . does accomplish that It redirects via >& all output from stderr to a new file, named: my_errors . . . and then runs in the background, caused by & So neither the messages nor the (long running) background process hold you up
22
Command fork() Unix fork() creates a new process by cloning the current process issuing the fork() command Returns 0 to child, and returns child’s PID to parent Peculiar about a child processes: it shares all attributes with the forking parent process, except the process id AKA PID, the parent PID AKA PPID, locks and a few attributes preventing infinite spawning! To compile the fork() command in your C program, #include <unistd.h> Code and data space of child and parent process are shared for reading only; when the child needs to write (stack, heap) it receives its own copy with the new modifications; AKA copy-on-write Spawning a new process via fork() creates a second exit() action; i.e. both parent and child need to exit eventually
23
Command fork() Confusing about fork(): it creates one new process by cloning once, but exiting twice! Child can inquire about its own PID via getpid( ) Child processes created via fork() do not repeat their own fork() thus preventing infinite spawning!! But child processes do execute other fork() instructions, if also present in the parent program Important: child and parent run concurrently If parent and child had 2 processors, they could both run in parallel, but the relative speeds shall be arbitrary! Make no assumptions about their speeds!! Hence on a UP these 2 executions are arbitrarily interleaved E.g. outputs can be arbitrarily mixed, though strictly sequential for parent and strictly sequential for the child A sample shown next:
24
fork() Sample1 in C #include <unistd.h> #define DEAD -1
// no shared global data, not shared data on heap! void fork_sample1() { // fork_sample1 int local = 1; // just some data to track: which process? pid_t pid = fork(); // from now on: 2 processes switch ( pid ) { case 0: // this is thread through child process printf( “++local value in child = %d\n", ++local ); break; case DEAD: // this is en error process, dead, zombie? printf( “<><> local in dead process = %d\n", local ); default: // clearly thread through parent process printf( ”parent pid = %d, --local = %d\n", pid, --local ); } //end switch // strange? switch statement has multiple clauses executed! By design! printf( "Ending process %d\n", pid ); } //end fork_sample1
25
fork() Sample1 Output [process] a.out parent pid = 22853, --local = 0
Ending process 22853 ++local value in child = 2 Ending process 0 [process] Note that local is 2, and not 1 in child process, and it happens to be executed after parent; arbitrarily! So you infer: child has its own copy. It does not share local with parent; else it would be 1, since parent decreased local to 0 Students: Are other outputs possible? How many?
26
fork() Sample2 in C++ #define DEAD -1 // define DEAD as before
#include <unistd> // make fork() available // Bryant & O'Halleron's "Computer Systems", chapter 8 void fork_sample2() { // fork_sample2 switch ( fork() ) { case 0: // child process cout << 'C'; break; case DEAD: cout << " <><> DEAD process?"; default: // parent process cout << 'P'; } //end switch // in all cases, indicate end by emitting 'E' cout << 'E'; } //end fork_sample2
27
fork() Sample2 Output [process] a.out
PECE[process] note no carriage return: no endl! Would CEPE be possible? Would EECP be possible? Would CPEE be possible? Would PECE be possible? Would ECPE be possible?
28
fork() Sample3 in C++ #include <iostream.h>
#include <unistd.h> int main( void ) { // main int number = 0; // Note “number” on stack if ( 0 == fork() ) { cout << "PID: " << getpid() << ” child process number = ” << ++number << endl; } //end if << " exiting with number = “ << --number << endl; return 0; } //end main // how many output lines, students? // Which will be the different values for “number”?
29
fork() Sample3 Output [process] a.out
PID: 5587 exiting with number = -1 PID: 5588 child process number = 1 PID: 5588 exiting with number = 0
30
fork() Sample4 –Getting Interesting
#include <iostream.h> #include <unistd.h> int main() { // main int number = 100; // Note “number” on stack if ( 0 == fork() ) { // first child creation cout << "PID: " << getpid() << " 1st child, number = “ << ++number << endl; } //end if if ( 0 == fork() ) { // second child creation cout << "PID: " << getpid() << " 2nd child. PPID: “ << getppid() << ". ++number = “ << " exiting with number = “ << --number << endl; return 0; } //end main
31
fork() Sample4 Output [process] a.out
PID: 4432 exiting with number = 99 PID: st child, number = 101 PID: nd child. PPID: number = 101 PID: 4434 exiting with number = 100 PID: 4433 exiting with number = 100 [process] PID: nd child. PPID: number = 102 PID: 4435 exiting with number = 101 had to enter CR-LF [process]
32
fork() Sample4 Variation
#include <iostream.h> #include <unistd.h> int main() { // main int number = 100; // Note “number” on stack if ( 0 == fork() ) { // first child creation cout << "PID: " << getpid() << " 1st child, number = “ << ++number << endl; } //end if if ( 0 == fork() ) { // second child creation cout << "PID: " << getpid() << " 2nd child. PPID: “ << getppid() << ". ++number = “ return 0; // Which program output now? << " exiting with number = “ << --number << endl; return 0; } //end main
33
fork() Sample5 –Interesting
#include <iostream.h> #include <unistd.h> void fork_sample() { // fork_sample pid_t pid1 = fork(); pid_t pid2 = fork(); pid_t pid3 = fork(); cout << " pid1 = " << pid1 << " pid2 = " << pid2 << " pid3 = " << pid3 << endl; } //end fork_sample int main() { // main fork_sample(); exit( 0 ); } //end main
34
fork() Sample5 Output [process] a.out
pid1 = pid2 = pid3 = 24585 pid1 = pid2 = pid3 = 0 pid1 = pid2 = 0 pid3 = 24587 pid1 = 0 pid2 = pid3 = 24588 [process] pid1 = pid2 = 0 pid3 = 0 pid1 = 0 pid2 = pid3 = 0 pid1 = 0 pid2 = 0 pid3 = 0 pid1 = 0 pid2 = 0 pid3 = 24589 had to enter CR-LF [process]
35
fork() Sample6 #include <iostream.h> #include <unistd.h>
void fork_sample() { // fork_sample pid_t pid1 = fork(); // 1 pid_t pid2 = fork(); // 2 pid_t pid3 = fork(); // 3 pid_t pid4 = fork(); // 4 printf( "I am %5d, parent= %5d, pid1= %5d, pid2= %5d, pid3= %5d, pid4= %5d\n”, getpid(), getppid(), pid1, pid2, pid3, pid4 ); } //end fork_sample int main() { // main fork_sample(); exit ( 0 ); } //end main
36
fork() Sample6 Output I am 22255, parent= 21528, pid1= 22256, pid2= 22257, pid3= 22258, pid4= 22260 I am 22260, parent= 22255, pid1= 22256, pid2= 22257, pid3= 22258, pid4= 0 I am 22256, parent= 22255, pid1= 0, pid2= 22259, pid3= 22261, pid4= 22264 I am 22258, parent= 22255, pid1= 22256, pid2= 22257, pid3= 0, pid4= 22265 I am 22264, parent= 22256, pid1= 0, pid2= 22259, pid3= 22261, pid4= 0 I am 22257, parent= 22255, pid1= 22256, pid2= 0, pid3= 22263, pid4= 22268 I am 22261, parent= 22256, pid1= 0, pid2= 22259, pid3= 0, pid4= 22266 I am 22265, parent= 22258, pid1= 22256, pid2= 22257, pid3= 0, pid4= 0 I am 22259, parent= 22256, pid1= 0, pid2= 0, pid3= 22262, pid4= 22267 I am 22266, parent= 22261, pid1= 0, pid2= 22259, pid3= 0, pid4= 0 I am 22263, parent= 22257, pid1= 22256, pid2= 0, pid3= 0, pid4= 22270 I am 22268, parent= 22257, pid1= 22256, pid2= 0, pid3= 22263, pid4= 0 I am 22269, parent= 22262, pid1= 0, pid2= 0, pid3= 0, pid4= 0 I am 22270, parent= 22263, pid1= 22256, pid2= 0, pid3= 0, pid4= 0 I am 22267, parent= 22259, pid1= 0, pid2= 0, pid3= 22262, pid4= 0 I am 22262, parent= 22259, pid1= 0, pid2= 0, pid3= 0, pid4= 22269
37
fork() Sample6 Graph Shell Process Parent Process
59 67 62 69 57 68 63 70 55 56 58 64 28 60 66 65 61 Shell Process Parent Process 4 children of Parent
38
Command execve() So why does the fork() command exist, if all it does is: make a copy of the current process? fork() is just the first step of creating new processes, including the execution of any Unix commands, but also user programs, such as a.out Therefore, code and data space have to be replaced to spawn off a new, separate process Unix command execve() accomplishes this A clever resource saving: pages in memory are not duplicated for new process; only modified pages are: AKA copy-on-write Side-effect of any fork() command is eventual execution of two returns or exits, namely of parent and child process; correspondingly, execve() command creates no new return or exit
39
execve() Sample void fork_exec_sample( char * argv[], char * envp[] )
pid_t pid = fork(); // parent + child exist now if ( 0 == pid ) { // strange order of: 0 == . . . // child process cout << "run 'herb.o' program." << endl; // must yield complete path; giving relative path also OK if ( execve( "/u/herb/progs/process/herb.o", argv, envp ) < 0 ) { cout << "Aborting " << endl; exit( 0 ); } //end if cout << "Created process " << pid << endl; } //end fork_exec_sample int main( int argc, char * argv[], char * envp[] ) { // main fork_exec_sample( argv, envp ); return 0; } //end main
40
execve() Sample, uses herb.o
// new program = process to be initiated by execve() // source named: herb.cpp #include <stdio.h> int main() { // main printf( "<> SUCCESS <> You executed Herb\n" ); return 0; // exits main() } //end main Compile this, and rename: a.out --> herb.o Move herb.o into directory /u/herb/progs/process [process] a.out Created process students: why printed only once? run 'herb.o' program. [process] <> SUCCESS <> You executed Herb
41
References IBM literature about Unix 8: speakingunix8/ Computer Systems, a Programmer’s Perspective, Bryant and O’Halleron, Prentice Hall © 2011, 2nd ed. Modern Operating Systems, Andrew S. Tanenbaum, Prentice Hall © 2011, second ed. Operating System Concepts, Silberschatz and Galvin, Addison Wesley © 1998, fifth ed. Posix Thread: Thread, Hyperthread, Process: Intel Hyperthreading: ng_technology.pdf Zombie process: Eliminate threading errors: us/sites/default/files/eliminate-threading-errors_studioxe-evalguide.pdf Intel Timebase Utility for multi-threading and concurrency: MS multi-threading: us/library/vstudio/172d2hhw.aspx Threading help: with-intel-compilers Threading instructions from Intel: guide-for-developing-multithreaded-applications
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.