Programación del sistema Linux C (10) operación básica de gestión de subprocesos

1 Descripción general del hilo

1.1 Introducción a hilos

En el entorno Linux, el llamado hilo es un proceso ligero. El sistema operativo asigna recursos en unidades de procesos y utiliza múltiples procesos pequeños para completar diferentes tareas simultáneamente en un espacio de ejecución. Estos pequeños procesos se denominan hilos. En el hilo del mismo proceso, hay recursos compartidos y recursos privados, como sigue:

  1. Recursos compartidos: espacio de direcciones, variables globales, archivos abiertos, procesos secundarios, alarmas, señales y programas de servicio de señales, información contable
  2. Recursos privados: contadores de programas, registros, pilas, palabras de estado.

1.2 Modelo de implementación de subprocesos del sistema operativo moderno

El modelo de subprocesos de los sistemas operativos modernos se basa en el modelo de subprocesamiento en modo de usuario anterior y el modelo de subprocesamiento en modo de núcleo, o una combinación de ambos. El sistema de ejecución en modo de usuario es responsable del cambio de hilos internos en el proceso cuando no está bloqueado; el sistema operativo en modo kernel es responsable del cambio de hilos bloqueados. Hay menos hilos en modo kernel y más hilos en modo usuario. Cada hilo en modo kernel sirve a uno o más hilos en modo usuario. Al asignar subprocesos, generalmente configure los subprocesos que necesitan realizar operaciones de bloqueo como subprocesos en modo kernel, y configure los subprocesos que no realizarán operaciones de bloqueo como subprocesos en modo de usuario.

1.3 Ventajas de multihilo

Aumentar la concurrencia del programa, mejorando así la eficiencia operativa del programa.

1.4 La programación de subprocesos debe prestar atención a

  1. Este conjunto de funciones pthread es inútil con el manual man, porque es una biblioteca pthread separada.
  2. Las funciones relacionadas con pthread están en el archivo de encabezado pthread.h.
  3. En general, se producirán errores durante la compilación directa. Debe agregar la biblioteca -lpthread.    

2 identificador de hilo

Cada subproceso tiene su propia ID, que está representada por el tipo de datos pthread_t, que es esencialmente un entero sin signo, es decir, typedef unsigned int pthread_t; por razones de portabilidad, el tipo pthread_t no puede ser equivalente a un entero sin signo Operación tipo.

2.1 función pthread_self

Use la función pthread_self para obtener la ID de un hilo en Linux

pthread_t pthread_self(void);
函数返回本线程的线程ID。

2.2 función pthread_equal

Utilice la función pthread_equal para comparar si dos ID de subproceso son iguales en Linux

int pthread_equal(pthread_t tid1,pthread_t tid2);
参数tid1\tid2:表示将要比较的量个线程ID号。
如果两个值相同,返回0,不相等则返回非零值。

Función de creación de 3 hilos pthread_creat

Utilice la función pthread_creat para crear un hilo en Linux:

int pthread_create (pthread_t * restrict tidp , pthread_attr_t *restrict \
attr,void *(*start_routine) (void *),void *restrict arg);

参数tidp:pthread_t类型的指针,一个值结果参数。
参数attr:线程的属性,不想指定特定属性的时候置为NULL。
参数start_rtn:一个函数指针(返回值是一个指向void型的指针,参数也是一个指向void型的指针),创建的线程要从该函数的起始位置处开始执行,函数返回该线程就停止了。
参数arg:一个void型的指针,是函数start_rtn的参数,在线程开始执行时,该参数由内核传递给线程。
函数执行成功返回0,失败返回错误编号。这种规律适合于所有的线程系列函数。

Nota:

  1. Los subprocesos y procesos en Linux tienen los mismos derechos de programación.El orden de programación de un subproceso y otro subproceso es completamente impredecible, lo que depende del algoritmo de programación del núcleo.
  2. Si desea pasar varios parámetros a la función pthread_creat, debe organizar todos los parámetros en una estructura y luego pasar la dirección de la estructura como parámetro al nuevo hilo.
  3. Las funciones de la serie de subprocesos generalmente no establecen el valor de errno, sino que devuelven el número de error. Debido a que los subprocesos pueden acceder libremente a las variables de entorno del proceso, cuando varios subprocesos cometen errores, el valor de errno se sobrescribirá varias veces, y el proceso solo puede verificar la causa del último subproceso de error.         

Terminación de 4 hilos

4.1 Hay 3 formas de terminar el hilo

  1. La ejecución de la función de subproceso finaliza: utilice el subproceso creado por pthread_creat para ejecutar una función y salga del subproceso si finaliza la ejecución de la función. Similar al retorno de la función principal.
  2. Cancelado por otro hilo: similar a ser asesinado entre procesos.
  3. El hilo sale solo: similar a llamar a la función de salida.

4.2 función pthread_exit

Use la función pthread_exit para terminar el hilo en Linux:

void pthread_exit(void *rval_ptr);
参数rval_ptr:一个指向void型的指针,该指针指向的区域存储退出信息,该信息类似于传递给新线程的参数,将多个信息组织成一个结构体。
函数无返回值。

Hay dos tipos de información final para un hilo:

  1. El área señalada por el puntero devuelto por la función del cuerpo del hilo. Obtenga el valor de retorno de la función del cuerpo del hilo.
  2. El área señalada por la función pthread_exit. Obtenga la información de salida establecida por la función pthread_exit.

4.3 función pthread_join

Use pthread_join para acceder a la información final de un hilo específico en Linux:

int pthread_join(pthread_t tid,void **rval_ptr);
参数tid:需要取得结束信息的线程。(如果该线程正在运行,则等待,直到该线程结束执行为止;如果指定线程的ID和调用线程的ID不属于同一进程,则出错)。
参数rval_ptr:一个指向void型的指针(因为要在内核中改变它的值)
//如果线程由于线程体函数返回/调用pthread_exit函数退出,则参数指向的是退出信息的首地址。
//如果其他线程由于被其他线程取消而退出则该参数被置为PTHREAD_CANCELED常量。
//如果不关心返回值,则将参数置为NULL。这时仅仅等待线程执行结束,不获得线程退出信息。
函数执行成功返回0,失败返回错误号。

El modelo general de uso de hilos es:

pthread_create();          //创建第1个线程
pthread_create();          //创建第2个线程
...
pthread_create();          //创建第n个线程
...
...
pthread_join();          //得到第1个线程的退出信息
pthread_join();          //得到第2个线程的退出信息
...
pthread_join();          //得到第n个线程的退出信息

4.4 Cómo salir del mensaje correctamente

Una vez que el subproceso termina de ejecutarse, el núcleo de Linux solo guarda la primera dirección del área de memoria donde se almacena la información de salida y no almacena la información de salida en el núcleo.

4.5 Cancelar la ejecución de un hilo pthread_cancel

Un hilo puede ser cancelado por otro hilo. Use la función pthread_cancel para cancelar otro hilo en Linux:

int pthread_cancel(pthread_t tid);
参数tid:要取消线程的线程ID。
函数执行成功返回0,失败返回错误编号。

Llamar a la función pthread_cancel es equivalente a llamar al hilo cancelado: pthread_exit (PTHREAD_CANCELED);

4.6 Función de limpieza de hilos pthread_cleanup_push / pthread_cleanup_pop

Use pthread_cleanup_push / pthread_cleanup_pop para lograr el final de la limpieza de hilos en Linux:

void pthread_cleanup_push(void (*rtn)(void *),void *arg);          //设置清理程序    
void pthread_cleanup_pop(int execute);                              //执行清理程序
参数rtn: 处理程序入口地址。
参数arg: 传递给处理函数的参数。
参数execute:
0:不执行清理程序。但是将栈顶的清理程序记录出栈。
非0:执行栈顶清理程序。执行后栈顶的清理程序记录出栈。    
函数无返回值。

Nota: La función pthread_cleanup_push se ejecutará en 3 situaciones:

  1. Llama a la función pthread_exit.
  2. Cancelado por otros hilos.
  3. Al llamar a la función pthread_cleanup_pop con un parámetro de valor distinto de cero.

pthread_push y pthread_pop deben usarse en pares, de lo contrario se producirá un error. Debido a que pthread_cleanup_push () y pthread_cleanup_pop () se implementan en modo macro, esta es la definición de macro en pthread.h:

#define pthread_cleanup_push(routine,arg) \
{
     struct _pthread_cleanup_buffer _buffer; \
     _pthread_cleanup_push (&_buffer, (routine), (arg));
#define pthread_cleanup_pop(execute) \
     _pthread_cleanup_pop (&_buffer, (execute)); \
    }

Se puede ver que pthread_cleanup_push () lleva un "{", mientras que pthread_cleanup_pop () lleva un "}". El orden de establecer el procedimiento de limpieza y ejecutar el procedimiento de limpieza es al revés.

4.7 Separación de subprocesos función pthread_detach

Use la función pthread_detach en Linux para lograr la separación dinámica de hilos:

int pthread_detach(pthread tid);
参数tid:要使之处于分离状态的线程ID。

Esta función es establecer el estado del subproceso secundario en separado, luego todos los recursos se liberarán automáticamente después de que se ejecute el subproceso.


5 Hilo sincronización_mutexes pthread_mutex _ ***

Un mutex es un tipo de bloqueo, que se agrega al acceder a un recurso compartido y se libera al final, de modo que solo un subproceso está en la zona crítica en cualquier momento. Cualquiera que desee ingresar a la sección crítica probará el bloqueo. Si el bloqueo ya está sujeto por un hilo, el hilo de prueba se bloqueará hasta que se libere el bloqueo, y el hilo repite el proceso anterior.

5.1 Inicializando y destruyendo el mutex

En el entorno de Linux, el tipo de datos pthread_mutex_t se usa para representar el mutex, y debe inicializarse cuando se usa. Cuando no esté en uso, destrúyalos.

pthread_mutex_init:锁的初始化
int pthread_mutex_init(pthread_mutex_t *mutex, const
pthread_mutexattr_t *mutexattr)
pthread_mutex_destroy:锁的销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数mutex:互斥锁的指针,一种特殊的类型参数。
参数mutexattr:锁的属性,缺省值为NULL。
函数执行成功返回0,失败返回错误号。

Hay dos formas de crear un mutex: estático y dinámico. POSIX define una macro PTHREAD_MUTEX_INITIALIZER para inicializar estáticamente el mutex, de la siguiente manera:

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
/*(在 LinuxThreads 实 现 中 , pthread_mutex_t 是 一 个 结 构 ,\
 而PTHREAD_MUTEX_INITIALIZER 则是一个结构常量)*/

La forma dinámica es utilizar la función pthread_mutex_init () para inicializar el mutex. La API se define de la siguiente manera:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) 

Mutexattr se usa para especificar el atributo del mutex.Si es NULL, se usa el atributo predeterminado.

5.2 Obteniendo y liberando el mutex

Bloquéelo cuando esté en uso. Desbloquee la cerradura al final de su operación. Las funciones involucradas son:

//pthread_mutex_lock:加锁操作
int pthread_mutex_lock(pthread_mutex_t *mutex);
//pthread_mutex_trylock:加锁操作
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//pthread_mutex_unlock:解锁操作
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数mutex:互斥锁的指针,一种特殊的类型参数。
函数执行成功返回0,失败返回错误号。

La diferencia entre las funciones pthread_mutex_lock y pthread_mutex_trylock:

  1. Si un subproceso adquiere el bloqueo solicitado por el primero, se bloqueará hasta que se libere el mutex.
  2. Si el bloqueo solicitado por este último es adquirido por un subproceso, no se bloqueará e inmediatamente devolverá un número de error EBUSY, que indica que el bloqueo aplicado está ocupado.

6 Bloqueo de sincronización de subprocesos_read-write pthread_rwlock _ ***

Un bloqueo mutex tiene solo un hilo a la vez para operar el bloqueo, y otros hilos se bloquean porque no pueden obtener el bloqueo. La intención original de crear una operación de subprocesos múltiples es ejecutar tareas simultáneamente, pero debido al bloqueo de mutex, la operación del subproceso se vuelve serial y la eficiencia del programa se verá reducida. Durante la ejecución del programa, si hay muchos más hilos leyendo un recurso compartido que escribiendo hilos, el uso de tales bloqueos de lectura-escritura puede aumentar en gran medida la concurrencia de los hilos, mejorando así la eficiencia operativa de los hilos.

6.1 Inicializando y destruyendo bloqueos de lectura-escritura

El entorno de Linux utiliza el tipo de datos pthread_rwlock_t para indicar el tipo de bloqueo de lectura-escritura, que debe inicializarse cuando se usa. Cuando no esté en uso, destrúyalos.

//pthread_rwlock_init:初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);  
//pthread_rwlock_destroy:销毁读写锁  
int pthread_rwlock_destroy(pthread_rwlock_t *restrict rwlock);
参数rwlock:读写锁的指针。
参数attr:读写锁的属性,缺省值为NULL。
函数执行成功返回0,失败返回错误号。

6.2 Obtención y liberación del bloqueo de lectura-escritura

//pthread_rwlock_rdlock:设定读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);      
//pthread_rwlock_wrlock:设定写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);   
参数rwlock:读写锁的指针。
函数执行成功返回0,失败返回错误号。

La implementación puede limitar el número de bloqueos en el modo de lectura del bloqueo de lectura-escritura, por lo que debemos verificar el valor de retorno para determinar si es exitoso. Las otras dos funciones devolverán un error, pero mientras nuestro diseño de bloqueo sea apropiado, no podemos verificarlo. Las funciones sin bloqueo son:

//pthread_rwlock_rdlock:设定读锁    
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);  
//pthread_rwlock_wrlock:设定写锁    
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
参数rwlock:读写锁的指针。
函数执行成功返回0,失败返回错误号。
//当锁成功获取时,返回0,否则返回EBUSY。
//这两个函数可以避免死锁。如果针对未初始化的读写锁调用进行读写操作,则结果是不确定的。

pthread_rwlock_unlock: liberar el bloqueo de lectura-escritura (usado para liberar el bloqueo contenido en el objeto de bloqueo de lectura-escritura al que hace referencia rwlock).

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);    
参数rwlock:读写锁的指针。
函数执行成功返回0,失败返回错误号。

7 Variable de condición de sincronización de subprocesos pthread_cond _ ***

7.1 Inicialización y destrucción de variables de condición

El tipo de datos de la variable de condición es pthread_cond_t, y debe inicializarse antes de su uso.

//pthread_cond_init:条件变量初始化
int pthread_cond_init(pthread_cond_t *restrict cond,pthread_condattr_t *restrict attr);
//pthread_cond_destroy:条件变量销毁
int pthread_cond_destroy(pthread_cond_t *cond);
参数cond:条件变量,一个特殊的参数类型。
参数attr:条件变量的属性,缺省值为NULL。    
函数执行成功则返回0, 出错则返回错误编号。

Dos formas de inicializar variables de condición:

  1. Estático: la constante PTHREAD_COND_INITIALIZER puede asignarse a una variable de condición asignada estáticamente.
  2. Dinámico: la función pthread_cond_init es borrar el espacio de memoria de la variable de condición dinámica con pthread_cond_destroy antes de liberarla.

7.2 Condiciones de espera para variables de condición

La función de condición de espera espera a que la condición se vuelva verdadera, el mutex pasado a pthread_cond_wait protege la condición, y la persona que llama pasa el mutex bloqueado a la función. La función coloca el hilo de llamada en la lista de hilos de espera y luego desbloquea el mutex. Estas dos operaciones son atómicas. Esto cierra el canal de tiempo entre la comprobación de la condición y el subproceso se pone en suspensión y espera a que la condición cambie, para que el subproceso no pierda ningún cambio en la condición. Cuando pthread_cond_wait regresa, el mutex se bloquea nuevamente.

//pthread_cond_wait:阻塞等待
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restric mutex);
//pthread_cond_timewait:超时等待
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout);
参数cond:条件变量,一个特殊的参数类型。
参数mutex:等待的指定的锁,一般在其他线程内。
参数timeout:等待的时间。
函数执行成功则返回0, 出错则返回错误编号。

La diferencia entre las dos funciones: las dos funcionan de manera similar, excepto que la función pthread_cond_timedwait tiene un tiempo de espera adicional que especifica el período de tiempo de espera.

7.3 Condiciones de notificación para variables de condición

Las dos funciones siguientes se utilizan para notificar al hilo que se ha cumplido la condición. Llamar a estas dos funciones también se llama enviar una señal al hilo o condición. Debe tenerse en cuenta que la señal debe enviarse al hilo después de cambiar el estado de la condición.

pthread_cond_signal:
int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_broadcast:
int pthread_cond_broadcast(pthread_cond_t *cond);
参数cond:条件变量,一个特殊的参数类型。
成功则返回0, 出错则返回错误编号。

La diferencia entre las dos funciones:

  1. pthread_cond_signal es despertar un subproceso que espera esta condición.
  2. pthread_cond_broadcast es para despertar todos los hilos que esperan esta condición.
     
Publicado 289 artículos originales · elogiados 47 · 30,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/vviccc/article/details/105165155
Recomendado
Clasificación