Linux Process understanding and practice (4) wait function processing botnets
Wait background
When a child process exits, the kernel sends a SIGCHLD signal to the parent process. The exit of the child process is an asynchronous event (the child process can be terminated at any time when the parent process is running)
When a child process exits, the kernel changes the child process to a zombie state. This process is called a zombie process. It only retains the smallest kernel data structure, so that the parent process can query the exit state of the child process.
You can use the wait/waitpid function to query the exit status of a child process in the parent process.
#include
#include
pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options);
Wait
Once a process calls wait, it immediately blocks itself. wait automatically analyzes whether a sub-process of the current process has exited. If it finds such a sub-process that has become a zombie, wait will collect information about this sub-process and destroy it completely and return it. If such a sub-process is not found, wait will be blocked until one appears.
The status parameter is used to save some statuses when the collection process exits. It is a pointer to the int type. But if we don't care about how this sub-process died, we just want to destroy it (in fact, in most cases, we will think like this ), we can set this parameter to NULL, as shown below:
Pid = wait (NULL );
If the call succeeds, wait returns the ID of the collected sub-process. If the call process does not have a sub-process, the call fails. In this case, wait returns-1 and errno is set to ECHILD.
Let's use an example to apply the wait call:
int main(){ pid_t pid=fork(); if(pid==-1) ERR_EXIT("fork error!"); else if(pid==0) { printf("This is the child process,ID=:%d\n",getpid()); sleep(5); exit(0); } else { int state,retID; retID= wait(&state); printf("This is the Parent process,returnID=%d,state=%d\n",retID,state); // exit(0); } return 0;}
We can obviously note that there are 5 seconds of waiting before the results are printed in line 1. This is the time we set to let the sub-process sleep, only the sub-process woke up from the sleep, it can exit normally and be captured by the parent process. In fact, no matter how long the sub-process sleeps, the parent process will remain waiting.
Parameter status:
If the value of the status parameter is not NULL, wait will take out the status of the sub-process and store it in it. This is an integer (int ), it indicates whether the sub-process Exits normally or is ended abnormally (a process can also be ended by another process with a signal, which will be introduced in future articles ), and the return value at the normal end, or information about which signal is terminated. Because the information is stored in different binary bits of an integer, It is very troublesome to read it using the conventional method. People have designed a special macro (macro) to complete this work, next, let's take a look at two of the most common ones:
1. The WIFEXITED (status) macro is used to indicate whether the sub-process Exits normally. If yes, it returns a non-zero value.
(Please note that, although the name is the same, the parameter status here is not the only parameter of wait -- the pointer to the integer status, but the integer pointed to by the pointer. Remember not to confuse it .)
2. WEXITSTATUS (status) When WIFEXITED returns a non-zero value, we can use this macro to extract the return value of the sub-process. If the sub-process calls exit (5) to exit, WEXITSTATUS (status) 5 is returned. If the sub-process calls exit (7), WEXITSTATUS (status) returns 7. Note that if the process does not exit normally, that is, if WIFEXITED returns 0, this value is meaningless.
int main(){ pid_t pid=fork(); if(pid<0) ERR_EXIT("fork error!"); else if(pid==0) { printf("This is the child process with pid of %d\n",getpid()); exit(3); } else { int state; pid_t retID=wait(&state); if(WIFEXITED(state)) { printf("The child process %d exit normally.\n",retID); printf("The return code is %d\n",WEXITSTATUS(state)); } else { printf("The child process %d exit abnormally.\n",retID); } } return 0; }
The parent process accurately captures the returned value 3 of the child process and prints it out.
Waitpid
In essence, the functions of the system call waitpid and wait are exactly the same, but the waitpid has two more user-controlled parameters pid and options, this provides us with a more flexible way of programming. The following two parameters are described in detail:
Pid
From the parameter name pid and type pid_t, we can see that what is needed here is a process ID. But when the pid gets different values, it has different meanings here.
When the pid is greater than 0, only the child process whose process ID is equal to the pid is waiting. No matter how many other child processes have ended and exited, as long as the specified child process has not ended, waitpid will keep waiting.
When pid =-1, wait for any sub-process to exit without any restrictions. At this time, waitpid and wait play the same role.
When pid = 0, wait for any sub-process in the same process group. If the sub-process has been added to another process group, waitpid will not ignore it.
Pid <-1, wait for any sub-process in a specified process group, the ID of this process group is equal to the absolute value of pid.
Relationship between wait and waitpid:
static inline pid_t wait(int * wait_stat){return waitpid(-1,wait_stat,0);}
The return value of waitpid is slightly more complex than that of wait. There are three cases:
When the returned result is normal, waitpid returns the ID of the collected sub-process;
If the option WNOHANG (the third parameter) is set, and waitpid in the call finds that no sub-process exited can be collected, 0 is returned;
If an error occurs in the call,-1 is returned. errno is set to a value to indicate the error;
When the sub-process indicated by the pid does not exist or the process exists but is not a sub-process that calls the process, waitpid will return an error, and errno is set to ECHILD;
int main(){ pid_t retID; pid_t pid=fork(); if(pid<0) ERR_EXIT("fork error!"); else if(pid==0) { sleep(10); exit(0); } do{ retID=waitpid(pid,NULL,WNOHANG); if(retID==0) printf("No child exited\n"); sleep(1); }while(retID==0); if(retID==pid) printf("Successfully get child!\n"); else ERR_EXIT("some error occured\n"); return 0; }
After 10 failed attempts, the parent process finally collected the exited child process.