From 505 course lecture
Lecture 2
Process:
exec():
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
..., char * const envp[]);
int execv(const char *path, char *const argv[]); // can pass arguments
//argv is a pointer to an array of pointers to null-terminated character strings.
//A NULL pointer is used to mark the end of the array. Each character string
//pointed to by the array is used to pass an argument to the new process image.
//The first argument, argv[0], is required and must contain the name of the
// executable file for the new process image.
int execve(const char *filename, char *const argv[], char *const envp[]); // can additionally pass environment variables
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
- command Line trick
echo $? // this will show the existing code of child process
Create a child process
v#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(void){
pid_t pid;
char *const parmList[] = {"/bin/ls", "-l","/home",NULL};
if((pid=fork())==-1){
perror("fork error");
} else if (pid == 0){
execv("/bin/ls",parmList);
perror("execv error");
}
}
fork_exec_wait
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main (void ){
//parent forks child process
pid_t pid = fork();
if(pid < 0){
fprintf(stderr, "Fork failed!\n");
exit(1);
} else if (pid == 0){
// fork returns 0 in the child
// child replace itself with /bin/ls
execlp("/bin/ls", "ls", NULL);
} else {
int status;
//parent waits for (any) child to terminate
wait(&status);
printf("Childe returned (%d)\n", status);
}
return 0;
}
Important Note: (from APinUNIXE)
- the fork() function is called once but returns twice! (return value in child is 0 and in parent is child's PID!
- BOTH child and parent continue executing with the instruction that follows the call to fork.
- Parent can have multiple child but child has only one parent!
- The child could get the parent's PID by calling getppid();
- The child is a copy of the parent means: child gets a copy of the parent's data space, heap, and stack!
- But a copy also means they do not share these portions of memory.
- Often , child and parent share the text segment, if it is read-only
// identifiers for every process
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void); // return process ID of calling process
pid_t getppid(void); // returns parent process ID of calling process
pid_t getuid(void); // returns user ID of calling process
pid_t geteuid(void); // returns effective user ID of calling process
gid_t getgid(void); // returns real group ID of calling process
gid_t getegid(void); // returns effective group ID of calling proces
Pipe example:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(void) {
// create a new pipe
int myPipe[2];
// myPipe[0] --> read
// myPipe[1] --> write
pipe(myPipe);
pid_t pid = fork(); //The pid_t data type represents process IDs.
// in GNU C, this is an int
//---
// Noticed that fork() is called once BUT Returned twice!!!!
// * return value for child is 0
// * return value for parent is the child's PID
//---
//child process will inherit both ends
if(pid < 0){
fprintf(stderr, "Fork failed!\n");
exit(1);
} else if (pid == 0) {
// if pid == 0 this is a child process
close(myPipe[0]); // don't need read end;
const char * message = "hellow world";
write(myPipe[1], message, strlen(message) + 1); // +1 for '\0'
printf("child process sent: %s\n", message);
exit(0);
} else {
// this is a parent process
close(myPipe[1]); // don't need write end
char message[200];
read(myPipe[0], message, sizeof(message));
printf("Parent process recieved: %s\n", message);
wait(NULL);
}
return 0;
}
- Important Note: the fork() function is called once but returns twice! (return value in child is 0 and in parent is child's PID!
- BOTH child and parent continue executing with the instruction that follows the call to fork.
- Parent can have multiple child but child has only one parent!
- The child could get the parent's PID by calling getppid();
// identifiers for every process
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void); // return process ID of calling process
pid_t getppid(void); // returns parent process ID of calling process
pid_t getuid(void); // returns user ID of calling process
pid_t geteuid(void); // returns effective user ID of calling process
gid_t getgid(void); // returns real group ID of calling process
gid_t getegid(void); // returns effective group ID of calling process
Signals: when we need asynchronous(异步) communication
Common signals:
- The process can define a event handler for each signal for example: shutdown()
#include <stdio.h>
#include <signal.h>
FILE *infile;
void shutdown(int arg) {
if(infile){
fclose(infile);
}
exit(1);
}
int main(void){
signal(SIGINT, shutdown);
infile = fopen("foo.txt","r");
while(fgets(buf,len,infile)){
//...
}
return 0;
}
Message Queue
- Offers a richer API compared to Signals and pipe
- Identified by name (eg. "/myqueue");
- mq_open: Open or create a queue
- mq_send: Add a new message to the queue
- mq_receive: Get a message from the queue
- mq_close: Close the queue
- mq_unlink: Destroys the queue
- Messages have an associated priority: PriorityQueue
Context Switching
Preemption and scheduling:
- Cooperative multitasking: the process themselves yield the CPU periodically OLD!
- Preemptive multitasking: the kernel preempts processes periodically. Newer!
What does the kernel have to do to switch from on process to another?
- Save the state of old process(CPU registers, program counter, stack pointer, ...) in the PCB of the old process
- switch to the address space of the new process
- Load the state from the PCB of the new Process.
Context switch is expensive:
- Depending on the hardware, certain caches (TLB!) have to be flushed - and there are other overheads.
- Pure overheads: The node doesn't do any 'useful' work during a switch.