Salir del proceso y esperar en Linux (requerido para principiantes)

contenido

1. Creación de un proceso

2. Copia en escritura

3. Terminación del proceso

4. Proceso en espera


1. Creación de un proceso

Ya he hablado sobre cómo se usa el tenedor en el blog de gestión de procesos. Para más detalles, puedes ir al blog anterior. ¿Qué debe hacer el sistema operativo cuando se crea un proceso?

1. Tabla de páginas de PCB (bloque de control de proceso) y mm_struct

2. Copiar parte de los datos del proceso padre al proceso hijo (compartir código y datos, siempre que no se produzca ninguna modificación)

3. Coloque el PCB del proceso secundario en la cola de permisos para la programación de la CPU.

Veamos el código a continuación:

  1#include<iostream>  
  2 using namespace std;
  3 #include<sys/types.h>
  4 #include<unistd.h>
  5 int main()
  6 {
  7   if(fork()==0)
  8   {
  9     cout<<"I am a child pid:"<<getpid()<<"ppid:"<<getppid()<<endl;
 10   }
 11   else 
 12   {
 13     cout<<"I am father pid:"<<getpid()<<"ppid:"<<getppid()<<endl;
 14     sleep(1);
 15   }                                                                                                                                                         
 16                                                                                                                                     
 17                                                                                                                                     
 18                                                                                                                                     
 19   return  0;                                                                                                                        
 20 }                                                                                                                                   
~                                                 

 ¿La bifurcación tiene éxito cada vez que se crea ? La respuesta es no. Cuando hay demasiados procesos en el sistema, es imposible crear procesos secundarios

O el número real de usuarios supera el límite, etc.

2. Copia en escritura

La premisa de que el código y los datos de los procesos padre e hijo se comparten en Linux se basa en la condición de que no se produzca ninguna modificación. Cuando cualquier parte intenta modificarlo, se produce una copia en escritura y el proceso hijo y el padre cada proceso tiene una copia privada.

 La copia en escritura que ocurre solo cambia la relación de mapeo de la tabla de páginas en la parte que intenta modificarla, y tiene permiso de escritura después de que ocurre la copia en escritura. ¿Y por qué copy-on-write?

1. Reducir el desperdicio de espacio Cuando los procesos padre e hijo no modifican el código, los procesos padre e hijo pueden compartir una parte del espacio, lo que reduce el desperdicio de espacio.

2. Mantener la independencia entre procesos . Aunque se comparte el código padre-hijo, una de las partes intenta modificar los datos compartidos. Si no se produce la copia por escritura, no se puede mantener la independencia entre los procesos. Con la copia on-write party Las modificaciones de los datos correspondientes no afectarán a la otra parte.

3. Terminación del proceso

Hay tres situaciones en las que un proceso sale:

1. Después de que se ejecuta el código, el resultado es correcto (la lógica es correcta, salga normalmente)
2. Después de que se ejecuta el código, el resultado es incorrecto (hay un problema con la lógica del código, el resultado es incorrecto)
3. El código termina anormalmente

Cómo comprobar el código de salida de un proceso:

echo $? puede ver el código de salida del proceso, el ejemplo es el siguiente:

​
  1#include<iostream>  
  2 using namespace std;
  3 #include<sys/types.h>
  4 #include<unistd.h>
  5 int main()
  6 {
  7   if(fork()==0)
  8   {
  9     cout<<"I am a child pid:"<<getpid()<<"ppid:"<<getppid()<<endl;
 10   }
 11   else 
 12   {
 13     cout<<"I am father pid:"<<getpid()<<"ppid:"<<getppid()<<endl;
 14     sleep(1);
 15   }                                                                                                                                                         
 16                                                                                                                                     
 17                                                                                                                                     
 18                                                                                                                                     
 19   return  0;                                                                                                                        
 20 }                                                                                                                                   
~                                                 

​

Tome el código anterior como ejemplo: ejecutamos este programa:

 Cuando estamos aprendiendo el lenguaje C, el valor de retorno de la función principal devuelve 0, y el valor de retorno de la función principal es en realidad el código de salida del proceso. Generalmente 0 significa normal, distinto de cero significa error.

¿Solo podemos finalizar el proceso con retorno 0 en main? Si quiero finalizar un proceso en una función, entonces no podemos usar return porque ejecutar return en una función normal significa que se ejecuta la función, no el final del proceso. Si queremos terminar el proceso dentro de una función normal, podemos usar exit o _exit.

 _salida

 Vamos a demostrar con un trozo de código:

  1.#include<iostream>
  2 using namespace std;
  3 #include<sys/types.h>
  4 #include<unistd.h>
  5 void func(int b,int a)
  6 {
  7    if(a==0)
  8    {
  9      cout<<"非法数据"<<endl;
 10      exit(1);                                                                                                                                               
 11    }
 12    cout<<b/a<<endl;
 13 }
 14 int main()
 15 {
 16   int a=0;
 17   int b=10;
 18   func(b,a);
 19   cout<<"hello word"<<endl;
 20   return 0;
 21 
 22 }

Si la salida puede eliminar el proceso, no se imprimirá helloword. Ejecutemos este programa:

 Encontramos que helloword no se imprime y el código de salida del proceso es exactamente el parámetro pasado para salir.

_salida

La diferencia entre _exit y exit es que exit vacía el búfer y _exit no vacía el búfer.

Del mismo modo, usamos el código anterior:

#include<stdlib.h>
  2 #include<iostream>
  3 using namespace std;
  4 #include<sys/types.h>
  5 #include<unistd.h>
  6 void func(int b,int a)
  7 {
  8    if(a==0)
  9    {
 10      cout<<"非法数据";                                                                                                                                      
 11      exit(1);                                                                                          
 12    }                                                                                                   
 13    cout<<b/a<<endl;   
 14 }                     
 15 int main()            
 16 {                     
 17   int a=0;            
 18   int b=10;           
 19   func(b,a);          
 20   cout<<"hello word"<<endl;
 21   return 0;           
 22                       
 23 }                     

está funcionando:

 Encontramos que la sentencia de datos ilegales puede ser emitida. ¿Qué pasa si usamos _exit?

 En este punto, encontramos que la entrada ilegal no se imprimió.

Terminación anormal:

1. Use ctr+c para terminar el proceso de primer plano

2. Use el comando kill para generar una señal de terminación

4. Proceso en espera

1. La necesidad de proceso en espera:

1. El proceso hijo sale.Si el proceso padre no lee la información de salida del proceso hijo, puede causar el problema del 'proceso zombi' y causar fugas de memoria.
2. Una vez que el proceso se convierte en zombie, será invulnerable, y el kill -9 de "matar sin parpadear" no puede hacer nada, porque nadie puede matar un proceso muerto.
3. Necesitamos saber cómo se completa la tarea asignada por el proceso padre al proceso hijo. Por ejemplo, si el proceso secundario se completa, el resultado es correcto o incorrecto, o si sale normalmente.
4. El proceso padre recicla los recursos del proceso hijo y obtiene la información de salida del proceso hijo a través del método de proceso en espera.

2. El método de proceso en espera

Cómo espera el proceso:

1.esperar

2.esperar

Primero echemos un vistazo a la espera:

Valor de retorno de la función:

Hay dos casos para el valor de retorno: 1. Esperando el éxito para devolver el id del proceso en espera, 2. Esperando el fracaso para devolver -1, es decir, se devuelve el id del proceso en espera si tiene éxito, y -1 se devuelve si falla.

Parámetros: el estado es un parámetro de salida. En el lenguaje c, si queremos obtener el valor en la función, debemos pasar la dirección y desreferenciarla en la función para que podamos obtener algunos valores en la función fuera de la función. Con stauts podemos obtener el estado de salida del proceso de espera. Por supuesto, si no le importa esto, puede establecerlo en NULL.

Ejemplo:

#include<stdlib.h>                                                                                                                                          
  2 #include<iostream>
  3 using namespace std;
  4 #include<sys/types.h>
  5 #include<unistd.h>
  6 #include<wait.h>
  7 int main()
  8 {
  9   pid_t id=fork();
 10   if(id<0)
 11   {
 12     cerr<<"fokr fail"<<endl;
 13   }
 14   else if(id==0)
 15   {
 16     //子进程
 17     int cnt=10;
 18      while(cnt--)
 19      {
 20        cout<<"I am a child pid: "<<getpid()<<"ppid:"<<getppid()<<endl;
 21        sleep(1);
 22      }
 23      exit(1);
 24   }
 25   //父进程
 26   cout<<"father process begin"<<endl;
 27    sleep(5);
 28    pid_t Id=wait(0);//不关系子进程的退出状态
 29    cout<<"parent process wait finish"<<endl;
 30    if(Id<0)
 31    {
 32      cout<<"parent wait fail"<<endl;
 33    }
 34    else
 35    {
 36        cout<<"wait success"<<endl;
 37    }
 38 
 39 }

A continuación usamos un script para monitorear el proceso:

 while :; do ps axj | head -1 && ps ajx | grep test; sleep 2; echo "#############################################"; done

 El proceso hijo sale en un estado zombie.

El proceso principal espera al proceso secundario y, después de reciclar los recursos del proceso secundario, el proceso secundario también cambia del estado z al estado x.

2.esperar

ilustrar:

1. Valor de retorno: espere el éxito para devolver la identificación del proceso secundario que espera el éxito y devuelva -1 para el fracaso. El valor devuelto es el mismo que esperar.Si se establece WNOHANG y waitpid no encuentra ningún proceso secundario finalizado, devuelve 0.

2. El primer parámetro del parámetro es el proceso que desea esperar, cuando pid = -1, es lo mismo que esperar cualquier proceso, cuando pid es mayor que 0, espera el mismo proceso que pid.

3. El segundo parámetro: el parámetro de salida, si no le importa el estado de salida del proceso de espera, puede establecerlo en NULL. WIFEXITD (estado) es verdadero si el proceso secundario regresa normalmente (generalmente se usa para determinar si el proceso finaliza normalmente). WEXITSTATUS(status): si WIFEXITD(status) no es cero, se puede utilizar para extraer el código de salida del proceso secundario (consulte el código de salida del proceso secundario).

4. La tercera opción de parámetro: si el proceso secundario especificado por pid no finaliza, el valor de retorno de waitpid es 0. Si finaliza normalmente, devuelve la identificación del proceso secundario (para la espera basada en bloqueo para el acceso de sondeo) 0 representa el bloqueo de la espera, el proceso principal no hace nada durante la espera.

Del mismo modo, cambiamos la espera anterior a waitpid:

#include<stdlib.h>
  2 #include<iostream>
  3 using namespace std;
  4 #include<sys/types.h>
  5 #include<unistd.h>
  6 #include<wait.h>
  7 int main()
  8 {
  9   pid_t id=fork();
 10   if(id<0)
 11   {
 12     cerr<<"fokr fail"<<endl;
 13   }
 14   else if(id==0)
 15   {
 16     //子进程
 17     int cnt=5;
 18      while(cnt--)
 19      {
 20        cout<<"I am a child pid: "<<getpid()<<"ppid:"<<getppid()<<endl;
 21        sleep(1);
 22      }
 23      exit(10);
 24   }
 25   //父进程
 26   
 27   cout<<"father process begin"<<endl;
 28     pid_t Id=waitpid(-1,NULL,0);//等待任意一个子进程                                                                                                        
 29    cout<<"parent process wait finish"<<endl;                                                  
 30    if(Id<0)                                                                                   
 31    {                                                                                          
 32      cout<<"parent wait fail"<<endl;                                                          
 33    }                                                                                          
 34    else                                                                                       
 35    {                                                                                          
 36        cout<<"wait success"<<endl;                                                            
 37    }                                                                                          
 38                                                                                               
 39 }                                      

 Ejecuta el programa:

 3. Obtenga el estado del proceso secundario:

Tanto wait como waitpid tienen un parámetro de salida, que se puede establecer en NULL si no nos importa: hablemos de para qué sirve: los 32 bits de estado son altos y 16 bits no se estudian, solo los 16 bits bajos son estudiados poco

 Si muere por una señal:

 A partir de la figura, podemos saber que los 7 bits inferiores representan la señal de terminación, el indicador core_dump del octavo bit y los ocho bits superiores representan el código de salida (el código de salida es significativo cuando el proceso finaliza normalmente). Sacamos el valor de los 7 bits inferiores, si es 0 significa que el proceso sale normalmente, y el valor de los 8 bits superiores es el código de salida del proceso, de igual forma si es 0 significa salida normalmente. Si los 7 bits inferiores no son 0, significa que el proceso es anormal. El código de salida no tiene sentido en este punto.

Ejemplo:

 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<iostream>
  4 using namespace std;
  5 #include<sys/types.h>
  6 #include<unistd.h>
  7 #include<wait.h>
  8 int main()
  9 {
 10   pid_t id=fork();
 11   if(id<0)
 12   {
 13     cerr<<"fokr fail"<<endl;
 14   }
 15   else if(id==0)
 16   {
 17     //子进程
 18     int cnt=5;
 19      while(cnt--)
 20      {
 21        cout<<"I am a child pid: "<<getpid()<<"ppid:"<<getppid()<<endl;
 22        sleep(1);
 23      }
 24      exit(10);
 25   }
 26   //父进程
 27 
 28 
 29    int status=0;
 30   pid_t Id=waitpid(-1,&status,0 );//等待任意一个子进程默认行为阻塞等待
 31 
 32    if(Id>0)
 33    {//子进程退出了waitpid也成功了
 34     printf("father wait: %d ,sucess,status:exit code:%d,status exit signal:%d\n",Id,((status)>>8)&0xFF,status&0x7F);
 35    }                                                                                                                                                        
 36    else if(Id==0)
 37    {
 38        
 39         cout<<"Do my things"<<endl;
 40    }
 41    else if(Id<0)
 NORMAL ?? test.cpp                                                                                        ? main() ? cpp ?  ?  utf-8 ??  ?  72%   35/48 :  4 
                                             

4. Espera con bloqueo y espera sin bloqueo

1. Bloqueo y espera: el proceso padre ha estado esperando al proceso hijo y el proceso hijo no sale, esperaré y no haré nada más.

2. La esencia del bloqueo: la PCB del proceso principal cambia del estado R al estado S, de la cola en ejecución a la cola de espera, esperando el proceso secundario d.

3. La esencia de esperar el final: el proceso principal cambia de la cola de espera a la cola en ejecución, y la PCB del proceso principal también cambia del estado S al estado R.

#include<stdlib.h>
  2 #include<iostream>
  3 using namespace std;
  4 #include<sys/types.h>                                                                                                                                       
  5 #include<unistd.h>
  6 #include<wait.h>
  7 int main()
  8 {
  9   pid_t id=fork();
 10   if(id<0)
 11   {
 12     cerr<<"fokr fail"<<endl;
 13   }
 14   else if(id==0)
 15   {
 16     //子进程
 17     int cnt=5;
 18      while(cnt--)
 19      {
 20        cout<<"I am a child pid: "<<getpid()<<"ppid:"<<getppid()<<endl;
 21        sleep(1);
 22      }
 23      exit(10);
 24   }
 25   //父进程
 26   
 27   cout<<"father process begin"<<endl;
 28   int status=0;
 29     pid_t Id=waitpid(-1,&status,0);//等待任意一个子进程默认行为阻塞等待
 30    cout<<"parent process wait finish"<<endl;
 31    if(Id>0)
 32    {
 33     cout<<"wait sucess"<<endl;
 34    }                    
 35    else if(Id<0)        
 36    {                    
 37      cout<<"parent wait fail"<<endl;
 38    }                    
 39                         

espera sin bloqueo

Espera sin bloqueo: el proceso principal verifica constantemente el estado de salida del proceso secundario y puede hacer lo suyo durante la detección del proceso secundario.

#include<stdio.h>
  2 #include<stdlib.h>
  3 #include<iostream>
  4 using namespace std;
  5 #include<sys/types.h>
  6 #include<unistd.h>
  7 #include<wait.h>
  8 int main()
  9 {
 10   pid_t id=fork();
 11   if(id<0)
 12   {
 13     cerr<<"fokr fail"<<endl;
 14   }
 15   else if(id==0)
 16   {
 17     //子进程
 18     int cnt=5;
 19      while(cnt--)
 20      {
 21        cout<<"I am a child pid: "<<getpid()<<"ppid:"<<getppid()<<endl;
 22        sleep(1);
 23      }
 24      exit(10);
 25   }
 26   //父进程
 27 
 28   while(1){
 29    int status=0;
 30   pid_t Id=waitpid(-1,&status,WNOHANG );//等待任意一个子进程默认行为阻塞等待
 31 
 32    if(Id>0)
 33    {//子进程退出了wait也成功了
 34     printf("father wait: %d ,sucess,status:exit code:%d,status exit signal:%d\n",Id,((status)>>8)&0xFF,status&0x7F);                                        
 35     break;
 36    }
 37    else if(Id==0)
 38    {
 39          //子进程还没有退出,但是waitpid是成功的,需要父进程重复等待
 40         cout<<"Do my things"<<endl;
 41    }
 NORMAL ?? test.cpp                                                                                        ? main() ? cpp ?  ?  utf-8 ??  ?  69%   34/49 :  5 
                                              

El resultado es el siguiente

 

Supongo que te gusta

Origin blog.csdn.net/qq_56999918/article/details/124027916
Recomendado
Clasificación