Linux process

Program and Process

  1. Instruction
    We always can use command on terminal and system would take response to you. It is called instruction
  2. Program
    As an engineer, we would write code in program such as .c, .phpand so on. It is a static file, and it is called program.
  3. Process
    Program is executed and running on the system, and it also provides IO for user to interact. It is a dynamic file, and it is called process.

Instruction combine together to a program, and program run on the system to become a process…

How to create process

When system starts, kernel will only create one process to init. Every process has their identified pid, and the init one has pid of 1.
ps
With command of ps, we can see several process running on the system. PID with 1 is init.
After init starts up, it will use fork() to create several child process, and each of them has separated space.
pstree
With command of pstree, we can clearly understand the structure of system process: from the pid=1 process to layers of child process. fork() return two value to process, one is child PID to parent process, and another is 0 to child process itself. In addition, child process can also use PPID to trace their parent PID.

What happen while child termination

When child process terminates, it will:

  1. put exit code which include detail message in kernel
    Usually, normal exit code is 0, and abnormal exit code is bigger than 0.
    In program of C, we also can control the return value of exit code.
    int main(){
    
     int a = 20;
     int b = 1;
     int result = a*b;
     if(result==0){
         fprinf(stderr,"wrong result?\n");
     }
     fprintf(stderr,"right result: %d\n",result);
     exit(EXIT_FAILURE);
    }
    

    If we comment the last line exit(...) and echo $? in command line, we will get printed value of 0. echo $? can print the exit code of last process. Therefore, a normal process will exit with sucess code 0. However, with use of function exit(...), we can control the exit code in program. In the program above, I change the exit code to EXIT_FAILURE, which is 1. So, even the program exit successfully, echo $? still print value of 1 on monitor.

  2. notify their parent process
    Parent process will use wait() to get the termination message of child process which was stored in kernel before and clean them. For more detail of wait, go to the following part: wait and waitpid

Here comes two problems about zombie process and orphan process. If parent process doesn’t use wait() to clean error message which is stored in kernel, and it will be kept in kernel and became zombie process. If parent process is terminated earlier than child process, it make no parent wait for child process to terminate, and it is orphan process which will be wait() by pid1 init process…

errno

In termination part, I have mentioned the exit code of process and error message will be stored in kernel. But, how about the error message of API function in C library? include <errno.h> will give an integer to external variable errno. We can read errno.h to get the reason for function error.

#include <stdio.h>
#include <string.h>
#include <errno.h>

extern int errno;

int main(){
	FILE * fi;
	int errnum;
	fi = fopen("error.txt","rb");
	if(fi == NULL){
		errnum = errno;
		fprintf(stderr,"errno: %d\n",errnum);    // errno: 2
		perror("print by perror");           // print by perror: No such file or directory
		fprintf(stderr,"print by strerror: %s\n",strerror(errnum));  // print by strerror: No .......
	}else{
		fclose(fi);
	}
}

In the program above, we use perror() and strerror instead of errno. These two functions are used to display the test message associated with errno.

wait and waitpid

From the part of termination, we realize that parent process can use wait() to get exit message and clean it. This part I will talk about how to use function wait().

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

int main(){
        pid_t child, process;
        child = fork();
        if(child < 0){
                fprintf(stderr,"fork failure\n");
        }else if(child == 0){
                // is child process
                printf("child pid: %ld\n",(long)(getpid()));
                sleep(5);
        }else{
                // parent process
                process = wait(NULL);
                printf("child pid: %d\n",process);
        }
        exit(0);
}

compile the C program above will get the result:
wait1

  1. pid_t wait(int *status)
    the parameter status of wait function can be used to stored exit information. However, we don’t care about it here, so we use wait(NULL) here instead.
  2. sleep(5)
    In result, the second line of child pid: 7640 show up 5 seconds after the first line. The reason is parent process call function wait() to wait child process finished, and child process execute sleep(5) before it finished.
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main(){
	pid_t child, process;
	int status;
	child = fork();
	if(child < 0){
		printf("fork fail!\n");
	}else if(child == 0){
		printf("child pid is %d",getpid());
		sleep(5);
		exit(10);
	}else{
		process = wait(&status);    // status is pointer
		if(WIFEXITED(status)){      // status is integer
			printf("The exit code is %d\n",WEXITSTATUS(status));
		}else{
			printf("pid %d Abnormally exit\n",process);
		}
	}	
}
// Some others Macro
// WIFSIGNALED(status)!=0    ->  killed by signal WTERMSIG(status)
// WIFSTOPPED(status)!=0     ->  stop by signal WSTOPSIG(status)
// SIFCONTINUED(status)!=0   ->  continue 

Compile the program can get result:
wait2

  1. different type of status need to be cared
    status passed into wait() is pointer, but something into MACRO is integer.
  2. WIFEXITED
    If we use this MACRO, it will return 0 when exit normally. Then we can use WEXITSTATUS(status) to get the exit code. Because of exit(10), we will get 10 on the monitor.
#define _POSIX_SOURCE
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status_ptr, int options);

waitpid provide more way to wait such as pid and some options.
waitpid
parameter pid can target specific child process
waitpid
parameter option
If child process exit normally, waitpid() return value of child pid. If error happens in function, waitpid() will return -1.

Reference