UC growth path 9

Posted by kapishi on Fri, 04 Mar 2022 05:06:14 +0100

Review:
UC growth path 1
UC growth path 2
UC growth path 3
UC growth path 4
UC growth path 5
UC growth path 6
UC growth path 7
UC growth path 8

1, Registration of last words function

#include <stdlib.h>
int on_exit(void (*function)(int , void *), void *arg);
//Function: register the last words function for the process
//parameter
//Function: the entry address of the last words function
//arg: second parameter passed to function
//Return value: 0 for success and non-0 for failure

eg:

#include <stdio.h>
#include <stdlib.h>

//Last words function
void doit(int status, void* arg)
{
    printf("n=%d\targ=%s\n", status, (char *)arg);
    return;
}
int main(void)
{
    //Register legacy function with process
    on_exit(doit, "hello");
    getchar();
    return 0;
}


Note: three points for attention of last words function:

  • 1. The order of calling is opposite to that of registration;
  • 2. The same function is called once it is registered
  • 3. The child process inherits the legacy function of the parent process
#include <stdio.h>
#include <stdlib.h>

//Last words function
void doit(int status, void* arg)
{
    printf("status=%d\targ=%s\n", status, (char *)arg);
    return;
}

void dou(int status, void* arg)
{
    printf("stutus%d\targ=%s\n", status, (char *)arg);
    return;
}

int main(void)
{
    //Register legacy function with process
    on_exit(doit, "hello");
    on_exit(doit, "hello");
    on_exit(dou, "world");
    getchar();
    return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

//Last words function
void doit(int status, void* arg)
{
    printf("status=%d\targ=%s\n", status, (char *)arg);
    printf("pid=%d\n", getpid());
    return;
}

void dou(int status, void* arg)
{
    printf("stutus%d\targ=%s\n", status, (char *)arg);
    printf("pid=%d\n", getpid());
    return;
}

int main(void)
{
    //Register legacy function with process
    on_exit(doit, "hello");
    on_exit(dou, "world");
    //on_exit(doit, "hello");
    pid_t pfork = fork();
    if(pfork==-1){
        perror("fork");
        return -1;
    }
    if(pfork==0){
        exit(0);
    }
    else{

    }
    getchar();
    return 0;
}

2, Recycling of process resources

  • The process occupies certain resources during execution. When the process terminates, the resources need to be released to the system, and the parent process is responsible for recycling the resources of the child process.
  • The system provides wait(2) family functions for recycling process resources
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
//Function: wait for the end of the sub process. After the end of the sub process, recycle the resources of the sub process
//parameter
//status: 
//1. Disabled (status): check whether the child process terminates normally, and the normal termination returns true.
//2.WEXITSTATUS(status): it is used when the child process terminates normally, that is, it is used when the disabled (status) returns true to detect the exit status of the child process.
//3. Wifsignled (status): returns true if the subprocess is terminated by a signal
//4.WTERMSIG(status): used only when wifsignled (status) is true. It returns the number of the signal that terminates the child process
//Return value: successfully returns the pid of the terminated child process; Error returned - 1

pid_t waitpid(pid_t pid, int *status, int options);
//Function: wait for the end of the specified sub process. After the end of the sub process, recycle the resources of the sub process
//parameter
//pid: you can specify a specific pid
//pid value: 1) < - 1: the absolute value of pid is the group id of the child process to wait
//2) - 1: wait for any child process
//3) 0: wait for child processes. The group id of the child process waiting for these processes must be consistent with the group id of the parent process
//4) > 0: wait for the specified process id

//Status: the same as the status of wait()
//options: 1.WNOHANG: if no child process terminates, return immediately; 2.0 is blocking
//Return value: Success: returns the pid of the terminated child process. If WNOHANG is specified and no child process is terminated, return 0; Error returned - 1

waitpid(-1, &status, 0);
  • Process group: there are multiple processes in the process group
  • Zombie process: after the child process exits, the system resources are not recovered by the parent process, which is a zombie process
    eg: zomdie.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int main(void)
{
    //Create child process
    pid_t pid=fork();
    if(pid==-1){
        perror("fork");
        return -1; 
    }   
    if(pid==0){//Code executed by child process
        printf("child processing...%d\n", getpid());
        exit(0);//Quit now
    }   
    else{//Code executed by the parent process
        getchar();
    }   
    return 0;
}

  • Orphan process: the parent process is terminated, and the parent process of the child process is changed to init process
    eg: orphan.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int main(void)
{
    //Create child process
    pid_t pid=fork();
    if(pid==-1){
        perror("fork");
        return -1; 
    }   
    if(pid==0){//Code executed by child process
        printf("child processing...%d\n", getppid());
        sleep(2);//Ensure that the parent process has terminated
        printf("child processing...%d\n", getppid());
    }   
    else{//Code executed by the parent process
        sleep(1);//Parent process sleep 1s
    }   
    return 0;
}


eg: wait.c use wait(2)

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(void)
{
    //Create child process
    pid_t pid=fork();
    if(pid==-1){
        perror("fork");
        return -1;
    }
    if(pid==0){//Code executed by child process
        printf("child processing...%d\n", getpid());
        getchar();
        exit(0);
    }
    else{//Code executed by the parent process
        wait(NULL);
        printf("parent processing...%d\n", getpid());
    }
    return 0;
}

eg:waitt.c. detect the cause of sub process termination

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(void)
{
    int status;
    //Create child process
    pid_t pid=fork();
    if(pid==-1){
        perror("fork");
        return -1;
    }
    if(pid==0){//Code executed by child process
        printf("child processing...%d\n", getpid());
        //getchar();
        exit(0);
    }
    else{//Code executed by the parent process
        wait(&status);//Wait for the subprocess to terminate and store the push state of the subprocess in the s variable space
        printf("parent processing...%d\n", getpid());
        //Check whether the subprocess terminates normally
        if(WIFEXITED(status))
            printf("exit code %d\n", WEXITSTATUS(status));
    }
    return 0;
}

eg: process terminated by signal

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(void)
{
    int status;
    //Create child process
    pid_t pid=fork();
    if(pid==-1){
        perror("fork");
        return -1;
    }
    if(pid==0){//Code executed by child process
        printf("child processing...%d\n", getpid());
        getchar();
        exit(0);
    }
    else{//Code executed by the parent process
        wait(&status);//Wait for the subprocess to terminate and store the push state of the subprocess in the s variable space
        printf("parent processing...%d\n", getpid());
        //Check whether the subprocess terminates normally
        if(WIFEXITED(status))
            printf("exit code %d\n", WEXITSTATUS(status));
        //Detect whether the subprocess is terminated by a signal
        if(WIFSIGNALED(status))
            printf("signal num %d\n", WTERMSIG(status));
    }
    return 0;
}

Supplementary: how to send a signal to the specified process?
kill - signal number pid

eg: waitpid.c illustrate waitpid(2)

  • Verify blocking
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(void)
{
    //int status;
    //Create child process
    pid_t pid=fork();
    if(pid==-1){
        perror("fork");
        return -1; 
    }   
    if(pid==0){//Code executed by child process
        printf("child processing...%d\n", getpid());
        getchar();
        exit(0);
    }   
    else{//Code executed by the parent process
        waitpid(-1, NULL, 0);//Equivalent to wait(NULL);
        printf("parent processing...%d\n", getpid());
    }   
    return 0;
}

  • Verify non blocking
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(void)
{
    //int status;
    //Create child process
    pid_t pid=fork();
    if(pid==-1){
        perror("fork");
        return -1; 
    }   
    if(pid==0){//Code executed by child process
        printf("child processing...%d\n", getpid());
        getchar();
        exit(0);
    }   
    else{//Parent code of the executing process
        int w=waitpid(-1, NULL, WNOHANG);//Non blocking. When no child process terminates, it returns 0
        printf("parent processing...%d\n", getpid());
        printf("w=%d\n",w);
    }   
    return 0;
}

3, Replace the image of the process

  • fork copies the PCB of the parent process. The process has its own 4G space, but at this time, the program code executed by the child process and the parent process is exactly the same. The child process repeatedly executes the parent process without development.
  • Sub process development and innovation are needed, and the image of the process belongs to a part of PCB. Load the specified program image in the space of the child process and replace the image inherited from the parent process. The child process has its own space and has its own image of the executing program.
  • The system provides the following function to complete the image of the update process: execve(2)
#include <unistd.h>
int execve(const char *filename, char *const argv[],
                  char *const envp[]);
//Function: Execute Program
//parameter
//filename: Specifies the file name of the program to be executed
//argv: list of strings. By convention, the first string is filename
//envp: pass environment variables to the process through this parameter. The form is key=value
//Return value: success is not returned; Failure returns - 1, and errno is set
  • In addition to the execve(2) function, the system encapsulates this function and provides many library functions to replace the image of the process. These functions belong to the execl(3) family
#include <unistd.h>

extern char **environ;
int execl(const char *path, const char *arg, ...
                       /* (char  *) NULL */);
int execlp(const char *file, const char *arg, ...
                       /* (char  *) NULL */);
int execle(const char *path, const char *arg, ...
                       /*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
 int execvpe(const char *file, char *const argv[],
                       char *const envp[]);
  • The above functions are composed of exec and l, v, p and e:
    • l: argv[0], argv[1],…, NULL. Is to list every argv [] array
    • v: vector argv. Just give the address of argv [] array, which is opposite to the letter l
    • p: Refers to the environment variable PATH. If there is this letter in the name of the function, you only need to specify the name of the executable file. The executable program can find the file under the PATH specified by the PATH environment variable. If the function name does not have this letter, you need to specify the PATH of the executable file.
    • e: Environment variables. The name of the function does not contain this character. It inherits the environment variable from the parent process by default. If there is this character, use the specified environment variable to override the environment variable inherited from the parent process.

Execute commands: PS - O, PID, PPID, PGRP, session, comm

day09$ps -o pid,ppid,pgrp,session,comm
   PID   PPID   PGRP   SESS COMMAND
  2429   2424   2429   2429 bash
  2914   2429   2914   2429 ps

eg: use the functions of exec family to execute the above commands PS - O, PID, PPID, PGRP, session, comm
execvp.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

int main(void)
{
    char *const p_argv[]={"ps", "-o", "pid,ppid,pgrp,session,comm",NULL};

    //Create child process
    pid_t pid = fork();
    if(pid==-1){
        perror("fork");
        return -1;
    }
    if(pid==0){//Code executed by child process
        //Replace the process image inherited by the parent process with the image of ps command
        execvp("ps", p_argv);
        //The following code will be executed only when the execp() function above makes an error
        perror("execl");
        exit(1);
    }
    else{//Code executed by the parent process
        wait(NULL);//Blocking waits for the end of the child process
    }
    return 0;
}


execv.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

int main(void)
{
    char *const p_argv[]={"ps", "-o", "pid,ppid,pgrp,session,comm",NULL};

    //Create child process
    pid_t pid = fork();
    if(pid==-1){
        perror("fork");
        return -1; 
    }   
    if(pid==0){//Code executed by child process
        //Replace the process image inherited by the parent process with the image of ps command
        //execvp("ps", p_argv);
        execv("/bin/ps", p_argv);
        //The following code will be executed only when the execp() function above makes an error
        perror("execl");
        exit(1);
    }   
    else{//Code executed by the parent process
        wait(NULL);//Blocking waits for the end of the child process
    }   
    return 0;
}


execlp.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

int main(void)
{
    //char *const p_argv[]={"ps", "-o", "pid,ppid,pgrp,session,comm",NULL};

    //Create child process
    pid_t pid = fork();
    if(pid==-1){
        perror("fork");
        return -1; 
    }   
    if(pid==0){//Code executed by child process
        //Replace the process image inherited by the parent process with the image of ps command
        execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,comm",NULL);
        //The following code will be executed only when the execp() function above makes an error
        perror("execl");
        exit(1);
    }   
    else{//Code executed by the parent process
        wait(NULL);//Blocking waits for the end of the child process
    }   
    return 0;
}

execl.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

int main(void)
{
    //char *const p_argv[]={"ps", "-o", "pid,ppid,pgrp,session,comm",NULL};

    //Create child process
    pid_t pid = fork();
    if(pid==-1){
        perror("fork");
        return -1; 
    }   
    if(pid==0){//Code executed by child process
        //Replace the process image inherited by the parent process with the image of ps command
        execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,comm",NULL);
        //The following code will be executed only when the execp() function above makes an error
        perror("execl");
        exit(1);
    }   
    else{//Code executed by the parent process
        wait(NULL);//Blocking waits for the end of the child process
    }   
    return 0;
}

  • The function of exec family is used to replace the image of the process.
    What happens when executing a.out? bash first creates a child process through fork(2), and then execl(2) changes the image of the process to a.out.
    eg: verify that the exec function is the image of the replacement process
#include <unistd.h>

int main(void)
{
    execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,comm", NULL);
    return 0;
}

  • bash executes linux commands, which are divided into internal commands and external commands
    • External commands are independent executable programs, and bash is also an independent executable program. bash and bash are not the same commands. These commands are external commands.
    • Internal commands are just part of bash. Bash does not need to fork when executing these programs.
  • So how to check whether a command is an internal command or an external command? Use the command type command, such as type ls

Topics: C Multithreading Operating System