Red de conocimiento informático - Problemas con los teléfonos móviles - Código fuente de la API de promoción de Sem

Código fuente de la API de promoción de Sem

Semaphore enfatiza la sincronización entre subprocesos (o procesos): "Semaphore se usa para la sincronización de múltiples subprocesos y múltiples tareas. Cuando un subproceso completa una acción, se lo informará a otros subprocesos a través del semáforo, y otros subprocesos realizarán algunas acciones (cuando todos estén en sem_wait, se bloqueará allí). Cuando el semáforo es un semáforo de un solo valor, también se puede completar el acceso mutuamente exclusivo a los recursos.

Semáforo con nombre: se puede utilizar para la interacción entre diferentes procesos o múltiples subprocesos.

Crear un semáforo con nombre abierto

sem_t *sem_open(const char *name, int of lag);

sem_t *sem_open( const). char *name, int oflag, mode_t mode, unsigned int value);

Devuelve con éxito el puntero del semáforo; no puede devolver sem_FAILED, establece errnoname en el nombre de la ruta del archivo, pero no se puede escribir como /tmp/a. .sem En el formulario, debido a que en Linux, sem está en el directorio /dev/shm y puede escribirse como "/mysem" o "mysem", los archivos creados son todos "/dev/shm/sem.mysem", y el modo se establece en 00.

Cierra el semáforo, que se llamará automáticamente cuando finalice el proceso

int SEM_close(SEM_t * SEM);

Devuelve 0 correctamente. No se puede volver a -1, establece el número de error.

Elimina el semáforo inmediatamente y lo destruye cuando todos los demás procesos lo cierran.

int SEM_unlink. * nombre);

Espera el semáforo, prueba el valor del semáforo, si su valor es menor o igual a 0, espera (bloquea una vez que su valor sea mayor que 0, comienza desde 1); Réstalo y regresa

int SEM_wait(SEM_t*SEM);

int SEM_try wait(SEM_t*SEM);

Vuelve a 0 con éxito; en -1, establezca el número de error.

Cuando el valor del semáforo es 0, SEM_trywait regresa inmediatamente y errno se establece en EAGAIN. Si la señal lo interrumpe, sem_wait regresará antes y establecerá errno en. EINTR.

Emite un semáforo, incrementa su valor en 1 y activa el proceso o hilo que espera el semáforo

int SEM_ post(SEM _ t * SEM);

Devuelve 0 si tiene éxito; si falla, devuelve -1 y su valor no cambiará. errno está configurado. Esta función es segura para señales asincrónicas y su semáforo anónimo se puede llamar en el controlador de señales para exclusión mutua y sincronización entre subprocesos en el cuerpo del proceso. Se utilizan las siguientes API (semáforos anónimos, semáforos basados ​​en memoria).

(1), sem_init

Función: se utiliza para crear un semáforo e inicializar el valor del semáforo.

Archivo de encabezado:

Prototipo de función: int SEM_init (SEM_t * SEM, int p compartido, valor int sin signo);

Función entrante de valor: sem: semáforo. Pshared: Determina si el semáforo se puede compartir entre varios procesos. Actualmente, LINUX no ha implementado el intercambio de información entre procesos, por lo que este valor sólo puede ser 0.

(2)Otras funciones.

int SEM_wait(SEM_t*SEM);

int SEM_try esperar(SEM_t*SEM);

int SEM_post (SEM_t*SEM);

int SEM_getvalue(SEM_t*SEM);

int SEM_destroy(SEM_t*SEM);

Función: sem_wait y SEM_try wait son equivalentes a operaciones P, las cuales pueden reducir el valor del semáforo por uno. La diferencia entre ellos es que si el valor del semáforo es menor que cero, sem_wait bloqueará el proceso y sem_wait regresará inmediatamente. Sem_post es equivalente a la operación V, suma 1 al valor del semáforo y envía una señal de activación al proceso (o subproceso) en espera.

Sem_getvalue obtiene el valor del semáforo.

Señal de destrucción de Sem_destroy.

Si un semáforo basado en memoria se sincroniza entre diferentes procesos, entonces el semáforo debe almacenarse en el área de memoria compartida, lo que significa que mientras exista el área de memoria compartida, el semáforo existirá.

Los bloqueos mutex (también llamados bloqueos mutex) enfatizan la exclusión mutua del acceso a recursos: los bloqueos mutex se utilizan en la exclusión mutua de múltiples subprocesos y múltiples tareas. Un hilo ocupa un determinado recurso, por lo que otros hilos no pueden acceder a él. Este recurso puede ser utilizado por otros hilos hasta que este hilo lo desbloquee. Por ejemplo, el acceso a una variable global a veces está bloqueado y cuando se completa la operación, se desbloquea. A veces se utilizan cerraduras y semáforos al mismo tiempo. "

En otras palabras, un semáforo no es necesariamente un concepto de bloquear un recurso, sino un concepto de proceso. Por ejemplo, hay dos subprocesos A y B. El subproceso B debe esperar a que el subproceso A complete un determinada tarea, y luego puede realizar sus propios pasos posteriores. Esta tarea no necesariamente necesita bloquear un recurso, pero también puede realizar algunos cálculos o procesamiento de datos. La exclusión mutua de subprocesos es el concepto de "bloquear un recurso" y otros subprocesos. No se pueden operar los datos protegidos. En algunos casos, los dos son intercambiables.

En Linux, el tipo de datos mutuamente excluyentes es pthread_mutex_T, que debe inicializarse antes de usarlo:

Para mutex asignados estáticamente, puede configurarlo en PTHREAD_MUTEX_INITIALIZER o llamar a pthread_mutex_init.

Para mutex asignados dinámicamente, después de solicitar memoria (malloc), pthread_mutex_init debe inicializarlo y liberarlo antes. memoria (gratis).

Prototipo:

int pthread _ mutex _ init (pthread _ mutex _ t * restric mutex, const pthread _ mutex attr _ t * restrict attr);

int pthread _ mutex _ destroy(pthread _ mutex _ t * mutex);

Archivo de encabezado:

Valor de retorno: 0 indica éxito, el número de error indica un error.

Descripción: si se usa el atributo predeterminado para inicializar el mutex, simplemente establezca attr en NULL. Hablaremos sobre la función de bloqueo más adelante.

Archivo de encabezado:

int pthread _ mutex _ lock(pthread _ mutex _ t * mutex);

int pthread _ mutex _ try lock(pthread _ mutex _ t * mutex);

Valor de retorno: 0 indica éxito y el número de error indica un error.

Explicación: Específicamente, la función trylock es un método de llamada sin bloqueo, es decir, si el mutex no está bloqueado, la función trylock bloqueará el mutex y obtendrá acceso a * * * recursos compartidos. derechos; si el mutex está bloqueado, la función trylock no bloqueará la espera y devuelve directamente EBUSY, indicando * * * que el recurso está ocupado.

Hablemos de la función de solución:

Archivo de encabezado:

Prototipo: int pthread _ mutex _ unlock(pthread _ mutex _ t * mutex); /p>

p>

Valor de retorno: 0 significa éxito, número de error significa error.

Las variables de condición se utilizan a menudo junto con mutex para lograr la sincronización de subprocesos: las variables de condición compensan las deficiencias de los mutex al permitir que los subprocesos se bloqueen y esperen a que otro subproceso envíe una señal. Al enviar una señal, si ningún hilo está esperando la variable de condición, la señal se perderá; el semáforo tiene un valor de conteo y se registrará cada operación posterior del semáforo.

El mutex debe ser desbloqueado por la persona que lo bloqueó. No es necesario que las operaciones de espera y envío del semáforo las realice el mismo hilo.

2. Un mutex está bloqueado o desbloqueado, similar a un semáforo binario.

3.sem_post es la única función entre varias tecnologías de sincronización que se puede llamar de forma segura en un controlador de señales.

4. Los mutex están optimizados para bloquear; las variables de condición están optimizadas para esperar; los semáforos se pueden usar para bloquear y esperar, por lo que habrá más sobrecarga y complejidad.

5. Los bloqueos Mutex y las variables de condición solo se usan entre subprocesos en el mismo proceso, mientras que los semáforos (llamados semáforos) se pueden usar para la sincronización entre diferentes procesos. Cuando se utiliza un semáforo para la sincronización entre procesos, el semáforo debe establecerse en el área de memoria compartida.

6. El semáforo tiene un valor de conteo y se registrará cada operación posterior del semáforo. Cuando una variable de condición envía una señal, si no hay ningún hilo esperando en la variable de condición, la señal se pierde.

Bloqueos de lectura y escritura

Los bloqueos de lectura y escritura son similares a los bloqueos mutex, pero los bloqueos de lectura y escritura permiten un mayor grado de paralelismo. Un mutex está bloqueado o desbloqueado, y sólo un subproceso puede bloquearlo a la vez.

El bloqueo de lectura-escritura puede estar en tres estados: bloqueo en modo lectura, bloqueo en modo escritura y desbloqueado. En modo de escritura, solo un subproceso puede ocupar el bloqueo de lectura y escritura a la vez, pero en modo de lectura, varios subprocesos pueden ocupar el bloqueo de lectura y escritura al mismo tiempo.

Bloqueado.

Cuando el bloqueo de lectura y escritura está en el estado de bloqueo de escritura, todos los subprocesos que intenten bloquear el bloqueo se bloquearán hasta que se desbloquee el bloqueo. Cuando un bloqueo de lectura y escritura está en el estado de bloqueo de lectura, todos los subprocesos que intentan bloquearlo en modo de lectura pueden acceder a él, pero si un subproceso quiere bloquear el bloqueo en modo de escritura, debe bloquearlo hasta que todos los subprocesos liberen el bloqueo de lectura. Aunque la implementación de los bloqueos de lectura y escritura es diferente, cuando un bloqueo de lectura y escritura está en un estado de bloqueo en modo de lectura, si otro hilo intenta bloquearlo en modo de escritura, el bloqueo de lectura y escritura generalmente bloqueará el bloqueo en modo de lectura posterior. solicitudes. Esto evita la ocupación a largo plazo de los bloqueos del modo de lectura y las solicitudes de bloqueo del modo de escritura pendientes que no se satisfacen.

Los bloqueos de lectura-escritura son ideales para leer estructuras de datos con más frecuencia que escribirlas. Cuando un bloqueo de lectura y escritura está en modo de escritura, la estructura de datos que protege se puede modificar de forma segura porque actualmente solo un subproceso puede poseer el bloqueo en modo de escritura. Cuando el bloqueo de lectura y escritura está en estado de lectura, siempre que el subproceso obtenga el bloqueo de lectura y escritura en modo lectura, la estructura de datos protegida por el bloqueo puede ser leída por varios subprocesos que obtienen el bloqueo en modo lectura.

Los bloqueos de lectura y escritura también se denominan * * * bloqueos exclusivos. Cuando un bloqueo de lectura y escritura está bloqueado en modo lectura, está bloqueado en modo exclusivo. Cuando está bloqueado en modo de escritura, está bloqueado en modo exclusivo.

Inicialización y destrucción:

#Incluye

int pthread_rwlock_init(pthread_rwlock_t * restrict rwlock, const pthread_rwlock attr_t * restrict attr);

int pthread _ rwlock _ destroy(pthread _ rwlock _ t * rwlock);

Devuelve 0 cuando tiene éxito y devuelve un número de error cuando ocurre un error.

Igual que la exclusión mutua, antes de liberar la memoria ocupada por el bloqueo de lectura y escritura, debe borrar el bloqueo de lectura y escritura a través de thread_rwlock_destroy y liberar los recursos asignados por init.

Lectura y escritura:

#Incluye

int pthread _ rwlock _ rd lock(pthread _ rwlock _ t * rwlock

int pthread _ rwlock _ wrlock(pthread _ rwlock _ t * rwlock);

int pthread _ rwlock _ unlock(pthread _ rwlock _ t * rwlock);

Devuelve 0 activado; Success, devuelve el número de error cuando ocurre un error.

Estas tres funciones implementan las operaciones de adquirir bloqueo de lectura, adquirir bloqueo de escritura y liberar bloqueo respectivamente. Las dos funciones para obtener el bloqueo son operaciones de bloqueo. De manera similar, las funciones sin bloqueo son:

#Includes

int pthread _ rwlock _ tryrdlock(pthread _ rwlock _ t * rwlock. );

int pthread _ rwlock _ trywrlock(pthread _ rwlock _ t * rwlock);

Devuelve 0 cuando tiene éxito y devuelve un número de error cuando ocurre un error.

Operación de adquisición de bloqueo sin bloqueo, si se puede adquirir, devuelve 0; de lo contrario, devuelve un EBUSY incorrecto.

Aunque los bloqueos de lectura y escritura mejoran el paralelismo, no son más rápidos que los bloqueos mutex.

Quizás esta sea también la razón por la que se utiliza la exclusión mutua incluso si hay un bloqueo de lectura y escritura, porque tiene una velocidad ligeramente mejor. Esto requiere que consideremos exhaustivamente la velocidad y el paralelismo al escribir programas y encontremos un compromiso.

Por ejemplo, supongamos que usar un mutex tarda 0,5 segundos y usar un bloqueo de lectura y escritura tarda 0,8 segundos. En software como el sistema de gestión de estudiantes, el 90% del tiempo pueden ser operaciones de consulta, por lo que si de repente hay 20 solicitudes, si se usa mutex, la última solicitud de consulta se cumplirá después de 10. En este caso, nadie podría soportarlo. Se deben utilizar bloqueos de lectura y escritura porque los bloqueos de lectura se pueden obtener varias veces. Por lo tanto, las 20 solicitudes se pueden satisfacer en aproximadamente 1 segundo cada una.

En otras palabras, deberíamos usar exclusión mutua en algunos programas que tienen muchas operaciones de escritura o pocos lugares que requieren sincronización, y usar bloqueos de lectura-escritura en algunos programas que tienen muchas más operaciones de lectura que de escritura. sincrónico.

Condición (Condición)

Cuando se usa con un mutex, una variable de condición permite que un hilo espere de manera no racial hasta que ocurra una condición específica.

La condición en sí está protegida por un mutex. Un subproceso debe bloquear el mutex antes de cambiar el estado de la condición. Otros subprocesos no se darán cuenta de este cambio hasta que obtengan el mutex, por lo que deben bloquear el mutex antes de evaluar la condición.

La detección condicional se realiza bajo la protección de un bloqueo mutex. Si la condición es falsa, el hilo bloqueará y liberará automáticamente el mutex, esperando a que cambie el estado. Si otro hilo cambia la condición, señala la condición relevante.

Variable, despierta uno o más subprocesos que la esperan, vuelve a adquirir el mutex y reevalúa la condición. Si dos procesos * * * comparten memoria legible y escribible, entonces se pueden usar variables de condición para lograr la sincronización de subprocesos entre los dos procesos.

Inicialización:

El tipo de datos de la variable de condición es pthread_cond_t, que debe inicializarse antes de su uso, incluidos dos métodos:

Estático: puede configurar el constante PTHREAD_COND_INITIALIZER Asignado a una variable de condición asignada estáticamente.

Dinámica: La función pthread_cond_init se utiliza para limpiar el espacio de memoria de las variables de condición dinámicas antes de liberarlas.

#Incluye

int pthread _ cond _ init(pthread _ cond _ t * restringir cond, pthread _ cond attr _ t * restringir attr

); int pthread _ cond _ destroy(pthread _ cond _ t * cond);

Devuelve 0 cuando tiene éxito y devuelve un número de error cuando ocurre un error.

Nota: El espacio ocupado por la variable de condición aún no ha sido liberado.

Cuando el parámetro attr de pthread_cond_init es NULL, se creará una variable de condición con atributos predeterminados que se analizarán más adelante;

2. Condiciones de espera:

#Incluye

int pthread_cond_wait (pthread_cond_t*restringir cond, pthread_mutex_t* restringir mutex);

int pthread _ cond _ timed wait(pthread _ cond _ t * restringir cond, pthread_mutex_t * restringir mutex, const struct timespec * restringir tiempo de espera

Éxito); Devuelve 0 cuando ocurre un error y devuelve un número de error cuando ocurre un error.

Estas dos funciones bloquean la espera y el tiempo de espera.

La condición de espera de la función de condición de espera se vuelve verdadera, la condición de protección mutex se pasa a pthread_cond_wait y la persona que llama pasa el mutex bloqueado a la función. Esta función coloca el hilo que llama en la lista de hilos que esperan la condición y luego desbloquea el mutex. Estas dos operaciones son atómicas. Por lo tanto, el canal de tiempo entre la verificación de condición y el hilo que se queda dormido esperando el cambio de condición se cierra para que el hilo no pierda ningún cambio de condición.

Cuando pthread_cond_wait regresa, el mutex se bloquea nuevamente.

El retorno de la función pthread_cond_wait no significa que el valor de la condición deba haber cambiado y el valor de la condición deba volver a verificarse.

Cuando regresa la función pthread_cond_wait, el hilo actual bloqueará el mutex correspondiente, incluso si la función devuelve un error.

Después de que se despierta el hilo bloqueado en la variable de condición, el valor de la condición puede cambiar hasta que regrese la función pthread_cond_wait(). Por lo tanto, después de que regresa la función, se debe volver a probar el valor de la barra antes de bloquear el mutex correspondiente. La mejor manera de probar esto es llamar a la función pthread_cond_wait en un bucle y establecer la expresión que satisface la condición como condición de terminación del bucle. Por ejemplo:

pthread _ mutex _ lock();

mientras (la condición es falsa)

pthread _ cond _ wait()

;

pthread _ mutex _ unlock();

El orden de liberación de diferentes hilos bloqueados en la misma variable de condición no es necesario.

Nota: La función pthread_cond_wait() es el punto de salida. Si hay una solicitud de salida pendiente cuando se llama a esta función y se permite que el subproceso salga, entonces el subproceso finalizará y comenzará la ejecución de la función de posprocesamiento, y el mutex asociado con la variable de pieza seguirá bloqueado. .

La función pthread_cond_timedwait se desbloqueará incluso si la condición no ocurre después de un tiempo específico. Este tiempo está especificado por el parámetro abstime. Cuando una función regresa, el mutex correspondiente generalmente está bloqueado, incluso si la función devuelve un error.

Nota: La función pthread_cond_timedwait también es un punto de salida.

El parámetro de tiempo de espera hace referencia a una hora del día. Ejemplo de uso:

pthread _ timestruc _ t to

to.tv_sec = tiempo(vacío)+tiempo de espera;

to .TV_nsec = 0

El código de error devuelto por el tiempo de espera es ETIMEDOUT.

3. Condiciones de notificación:

#Incluye

int pthread _ cond _ signal(pthread _ cond _ t * cond); >int pthread _ cond _ broadcast(pthread _ cond _ t * cond);

Devuelve 0 en caso de éxito y devuelve un número de error en caso de error.

Estas dos funciones se utilizan para notificar a los hilos que se han cumplido las condiciones. Llamar a estas dos funciones también se conoce como enviar una señal a un hilo o condición. Es importante tener en cuenta que el estado de la condición debe cambiarse antes de enviar una señal al hilo.