Linux Basic Tutorial: 9. Linux Process Management (2)

We talked about fork creating child processes earlier, so in this issue we will continue to talk about how to debug and insert other processes, special processes, and how to exit processes after creating processes;

Similarly, we wrote a C language program, but there are two processes in this program. When we debug, we will only select one process for debugging, so we need to learn multi-process debugging;

1. It also uses gdb but the difference is the following settings:

set detach-on-fork off/on #设置是否分离线程调试
set follow-fork-mode parent #设置父子进程调试
info inferiors #打印调试信息,当前调试的进程以及可调式进程
inferior id #切换要调试的进程
detach inferiors id #分离调试

For example, we wrote a parent-child process code:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{

	pid_t pid;
	int i;
	int num=0;
	pid=fork();
	
	for(i=0;i < 2;i++)
	{	
		if(pid==-1)
		{
			perror("fork error");
			exit(1);
		}
		else if(pid>0)
		{
			num+=2004;
			printf("花有重开日,人无再少年,id为:%d\n",getpid());
			printf("增加后的num:%d\n",num);
		}
		else if(pid==0)
		{
			num+=2004;
                	printf("花有重开日,人无再少年,id为:%d\n",getpid());
			printf("增加后的num:%d\n",num);
			exit(0);
		}
	}
	return 0;
}

We first use the compilation with debugging information to compile, and then enter the debugging. This file is called pipe.c so:

 

 You can see that after the compilation is successful, we use the gdb command and start debugging. Here we need to set it according to the above command:

I set up separate thread debugging, and debug the parent process, and then need to breakpoint when printing:

 ​​

 Here is line 23, I hit a breakpoint here, and then start debugging:

 It can be seen that the program stops here. If there is a loop, we will still stop here when we click Next, but if we also break the point in the Forbidden City, will it enter the sub-process debugging?

 Here my subprocess breakpoint is at 29, so let's hit a breakpoint and start debugging:

 It can be seen that the content of line 29 has been printed without entering the child process after the second pass, so we can know that the current debugger is debugging the parent process. If we add this line of code, it will switch to the child process for debugging :

Obviously we type:

set follow-fork-mode parent

The program switches to process debugging;

So many process debugging, I will stop here, and leave other functions to explore by yourself;

2. exec function family

There must be a reason to say that this is a function family rather than a function, because exec represents six functions:

execl(文件路径名,新进程名,NULL), #在程序中插入一个新进程,这个进程会运行参数1 
execlp(文件名,新进程名,NULL),  #与execl的作用一致,但是文件路径名会自动查找,不需要我们自己写好路径
execle(), 
execv(), 
execvp(), 
execve()

Here I will only explain the first two and others are actually the second function called in the end. Let me first talk about the difference between the first and the second. The first one will not fill the path by itself, but the second one will find the order by itself;

So what is their function? Just insert a process:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{

	pid_t pid;
	int i;
	int num=0;
	pid=fork();
	
	for(i=0;i < 2;i++)
	{	
		if(pid==-1)
		{
			perror("fork error");
			exit(1);
		}
		else if(pid>0)
		{
			num+=10;
			printf("花有重开日,人无再少年,id为:%d\n",getpid());
			printf("增加后的父进程num:%d\n",num);
		}
		else if(pid==0)
		{
			num+=10;
                	printf("花有重开日,人无再少年,id为:%d\n",getpid());

			execl("sy","sy",NULL);
			printf("增加后的子进程num:%d\n",num);

		}
	}
	return 0;
}

Compared with the previous one, this program has one more line of code:

execl("sy","sy",NULL);

The sy here is actually an executable file written by me, after compilation, its source code is:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{

	printf("fighting ,物联网2003! \n");
}

Then run it for him:

 The originally executed sub-process jumps to the sy file for execution after reaching this line of code, and the code of the sub-process after this function will not be executed again; then this function ends here, and execlp is almost used;

3. Process exit

There are two common process exits, one is exit(), and the other is _exit(), so what is the difference between the two?

The former is an exit that does not refresh the cache, and the latter is an exit that refreshes the cache:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
​
        printf("hello world!\n");
        prinft("hi");
        exit();
        return 0;
}

You can see that when using exit, both sentences will be printed, that is, both are executed; but if we change to _exit(), it will be different:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
​
        printf("hello world!\n");
        prinft("hi");
        _exit(0);
        return 0;
}

4. Special process

Here we will explain two processes, one is a zombie process and the other is an orphan process . Zombie processes should not appear in server maintenance, because they will occupy resources, and if there are too many threads, they will crash the computer, so we need to prevent them from It is generated, and the orphan process is a child process without a parent process. Because the parent-child process runs asynchronously, there is a situation where the parent process ends early, and no one closes the child process, so it keeps running:

int main()
{
​
        pid_t pid;
        int i;
        int num=0;
        pid=fork();
​
        for(i=0;i < 2;i++)
        {
                if(pid==-1)
                {
                        perror("fork error");
                        exit(1);
                }
                else if(pid>0)
                {
                        printf("parent pid:%d\n",getpid());
                }
                else if(pid==0)
                {
                        sleep(2);
​
                        printf("child pid:%d\n",getpid());
                }
        }
        return 0;
}

Here we use an operation, that is, after creating the child process, we let it sleep for 2 seconds, and then the parent process will exit. After it exits, the child process becomes an orphan process. The result is that the child process has been running without Recycle, and we can see from the window that the original operation interface is not displayed;

A zombie process means that the child process has ended, but the parent process has been executing without recycling. We can see it with the following code:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{

        pid_t pid;
        int i;
        int num=0;
        pid=fork();

                if(pid==-1)
                {
                        perror("fork error");
                        exit(1);
                }
                else if(pid>0)
                {
			while(1)
			{

				printf("parent pid:%d\n",getpid());
				sleep(3);
			}
                }
                else if(pid==0)
                {
			sleep(2);

			printf("child pid:%d\n",getpid());
                }
        return 0;
}

Here we let the child process end before the parent thread wakes up, so we can see a z+ in the picture, which means that the zombie process is still running; at this time, we can end the zombie process by killing the parent process up;

So how do we avoid the generation of zombie processes? Linux gives the wait() function:

pid_t wait(int *wstatus);
​
 pid_t waitpid(pid_t pid, int *wstatus, int options);
 
 #include<sys/wait.h>
​

The wait function will block when the parent process is running. If a zombie process is found in the child process, it will be executed, and then it will be recycled, and the id of the process will be returned. If not, it will return -1, and then it will exit normally. It’s ok; the parameters here are used to save the state information when the process exits, because we only want to deal with zombie processes, so we generally set the parameters to null; the use of wait needs to include a header file sys/wait.h

  while(1)
            {
​
                printf("parent pid:%d\n",getpid());
                sleep(3);
                ret=wait(NULL);
                if(ret==-1)
                {
                    exit(0);
                }
                else if(ret>0)
                {
                    printf("处理了一个僵尸进程!%d\n",ret);
                }
            }
  
We add this code in the while loop of the parent process, and the result will be completely different:

wAAACH5BAEKAAAAAAAAAABAAEAAAICRAEAOw== edit

The parent process, which would have been executed forever, exited with the generation of the zombie process, because the call of the wait function was processed when the zombie process was generated and the number 2099 was printed, and then when there was no zombie process Execute exit(0);

The waitpid function can be said to be an upgraded version of wait. It has three parameters, and the parameter pid can be set to -1. Then its function is the same as that of wait. Its distinctive feature is that it will not block threads, that is, run in the parent process The child process that is processed at the time;

Guess you like

Origin blog.csdn.net/aiwanchengxu/article/details/127940113