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.

Thread

results matching ""

    No results matching ""