Programación del sistema Linux 58 Selección de E / S avanzada () del multiplexor de E / S Supervisar la lectura, escritura y el estado anormal de los descriptores de archivos

Por qué se introduce la multiplexación:
en el programa en el que la máquina de estado se da cuenta de copiar los datos entre dos dispositivos, puede encontrar un problema de este tipo: durante la ejecución del programa, debe estar en un estado ocupado y la tasa de uso de la CPU es muy alta La razón por la que se pierde el tiempo de la CPU es que el programa está realmente ocupado juzgando errores falsos, releyendo y reescribiendo la mayor parte del tiempo. Esta es una tarea que requiere una gran cantidad de E / S que no requiere mucha carga, es decir, la cantidad de datos no es grande, pero la E / S es muy intensa. Para tareas intensivas en IO, el programa se puede multiplexar con IO. La esencia es monitorear el comportamiento del descriptor de archivo. Cuando el descriptor de archivo actual tiene un comportamiento que me interesa, se realizarán operaciones posteriores, como la anterior. dos Para el intercambio de datos entre dos dispositivos, el método anterior es la pulsación ciega, que constantemente detecta e intenta ver si el contenido es legible o escribible. Si usamos multiplexación de E / S, puede llegar a ser que cuando un determinado estado de descriptor de archivo tiene una acción que me interesa, realizaré operaciones posteriores para ahorrar tiempo de CPU.

Multiplexación IO: supervise el comportamiento de los descriptores de archivos, ¿fd es legible? Escribible? ¿Hay alguna condición anormal que deba manejarse? Cuando el descriptor de archivo alcanza el estado que nos interesa, la función regresa.
select (), portable
poll (), portable
epoll (), dialecto de Linux basado en poll

       select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O multiplexing

SYNOPSIS
       /* According to POSIX.1-2001, POSIX.1-2008 */
       #include <sys/select.h>

       /* According to earlier standards */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

/ * Si la función no implementa la configuración de tiempo de espera, la función se bloqueará y esperará hasta que ocurra el evento de interés, es decir, el descriptor de archivo en el conjunto de descriptores de archivo en cuestión se volverá legible y escribible. La función solo devolverá cuando está en el estado. Tenga en cuenta que los tres fd_sets no son tipos constantes, son mutables. Cuando select () regresa, estos tres conjuntos ya no son el sitio del supervisor que organizamos antes, sino el lugar donde se almacenan los resultados. Si select () no establece un tiempo de espera, la espera de bloqueo se interrumpe con una señal y se produce un error falso, estos tres conjuntos también se borrarán.

nfds: el número de descriptores de archivo, pero se refiere al valor del descriptor de archivo más grande +1 contenido
readfds: conjunto de descriptor de archivo legible
writefds: conjunto de descriptor de archivo grabable
exceptfds: descriptor de archivo anormal establecido
tiempo de espera de espera Configurar, bloquear. Si no se implementa la configuración de tiempo de espera, la función morirá y esperará

* /

   int select(int nfds, fd_set *readfds, fd_set *writefds,
              fd_set *exceptfds, struct timeval *timeout);

   void FD_CLR(int fd, fd_set *set);//从指定的文件描述符集合中删除指定文件描述符fd
   int  FD_ISSET(int fd, fd_set *set);//判断文件描述符fd 是否存在于文件描述符集合set中
   void FD_SET(int fd, fd_set *set);// 将文件描述符fd 放到 文件描述符集合set中
   void FD_ZERO(fd_set *set); //清空一个文件描述符集合

El
valor de retorno de RETURN VALUE es el número de comportamientos de descriptores de archivos en los que se han producido eventos de interés, y estos descriptores de archivos todavía se colocan en el conjunto de lectura, escritura y error en el conjunto de descriptores de archivo.
Si falla, se devuelve -1 y se establece errno. EINTR es un error falso, el bloqueo de la espera puede ser interrumpido por una señal

ERRORES

   EINTR  A signal was caught; see signal(7). 阻塞等待 可以被信号打断

   EINVAL nfds is negative or exceeds the RLIMIT_NOFILE resource limit (see getrlimit(2)).

   EINVAL the value contained within timeout is invalid.

   ENOMEM unable to allocate memory for internal tables.

   The time structures involved are defined in <sys/time.h> and look like

秒+微秒
           struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
           };

Tenga en cuenta que select (NULL, NULL, NULL, NULL, set timeout) se usa a menudo para lograr un sueño seguro.

Experimento 1: Mejorar la máquina de estado para usar E / S multiplexadas para copiar datos entre dos dispositivos

El programa ya no está en el bucle de errores falsos al volver a leer y reescribir todo el tiempo, y la tasa de uso de la CPU se reduce considerablemente, porque la mayor parte del tiempo está bloqueado en select () esperando cambios en el descriptor de archivo.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>

#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"
#define BUFSIZE 1024

enum
{
	STATE_R = 1,
	STATE_W,
STATE_AUTO,
	STATE_Ex,
	STATE_T
};

struct fsm_st
{
	int state;
	int sfd;
	int dfd;
	char buf[BUFSIZE];
	int len;
	int pos;
	char* errstr;
};

static void fsm_driver(struct fsm_st *fsm)
{
	int ret;

	switch(fsm->state)
	{
		case STATE_R:
			fsm->len = read(fsm->sfd,fsm->buf,BUFSIZE);
			if(fsm->len == 0)
				fsm->state = STATE_T;
			else if(fsm->len < 0)
			{
				if(errno == EAGAIN)
					fsm->state = STATE_R;

				else
				{
					fsm->errstr = "read()";
					fsm->state = STATE_Ex;
				}	
					
			}
			else
			{		
				fsm->pos = 0;
				fsm->state = STATE_W;
			}
		
		break;

		case STATE_W:
			ret = write(fsm->dfd,fsm->buf+fsm->pos,fsm->len);
			if(ret < 0)
			{
				if(errno == EAGAIN)
					fsm->state = STATE_W;
				else
				{
					fsm->errstr = "read()";
					fsm->state = STATE_Ex;
				}
				
			}
			else
			{
				fsm->pos += ret;
				fsm->len -= ret;
				if(fsm->len == 0)
					fsm->state = STATE_R;
				else 
					fsm->state = STATE_W;
			}
		break;

		case STATE_Ex:
			perror(fsm->errstr);
			fsm->state = STATE_T;		
		break;

		case STATE_T:
			/* do something*/
		break;
		
		default:
			abort();
		break;
	}

}

static int max(int a,int b)
{
	if(a > b)
	{	
		return a;
	}
	return b;
}

static void relay(int fd1,int fd2)
{
	int fd1_save,fd2_save;
	struct fsm_st fsm12,fsm21;
	fd_set rset,wset;	


	fd1_save = fcntl(fd1,F_GETFL);
	fcntl(fd1,F_SETFL,fd1_save|O_NONBLOCK);

	fd2_save = fcntl(fd2,F_GETFL);
	fcntl(fd2,F_SETFL,fd2_save|O_NONBLOCK);

	fsm12.state = STATE_R;
	fsm12.sfd = fd1;
	fsm12.dfd = fd2;

	fsm21.state = STATE_W;
	fsm21.sfd = fd2;
	fsm21.dfd = fd1;	

	while(fsm12.state != STATE_T || fsm21.state != STATE_T)	
	{
	//设置现场
		FD_ZERO(&rset);
		FD_ZERO(&wset);

		if(fsm12.state == STATE_R)
			FD_SET(fsm12.sfd,&rset);

		if(fsm12.state == STATE_W)
			FD_SET(fsm12.dfd,&wset);

		if(fsm21.state == STATE_R)
			FD_SET(fsm21.sfd,&rset);

		if(fsm21.state == STATE_W)
			FD_SET(fsm21.dfd,&wset);

  	//监控
		if(fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO)
		{
			if(select(max(fd1,fd2)+1,&rset,&wset,NULL,NULL) < 0)	
			{
				if(errno == EINTR)
					continue;

				perror("select()");
				exit(1);
			}
		}
	
 	//根据监控结果 最下一步动作
		if(FD_ISSET(fd1,&rset) || FD_ISSET(fd2,&wset) || fsm12.state > STATE_AUTO)
		fsm_driver(&fsm12);

		if(FD_ISSET(fd2,&rset) || FD_ISSET(fd1,&wset) || fsm12.state > STATE_AUTO)
		fsm_driver(&fsm21);
	}

	fcntl(fd1,F_SETFL,fd1_save);
	fcntl(fd2,F_SETFL,fd2_save);

}

int main()
{

	int fd1,fd2;

	fd1 = open(TTY1,O_RDWR);
	if(fd1 < 0)
	{

		perror("open()");
		exit(1);
	}

	fd2 = open(TTY2,O_RDWR|O_NONBLOCK);
	if(fd1 < 0)
	{
		perror("open()");
		exit(1);
	}

	relay(fd1,fd2);


	close(fd2);
	close(fd1);
	

}

Problemas con select ():

1 La ubicación del sitio de monitoreo (tres conjuntos) es la misma que la ubicación del resultado del monitoreo (tres conjuntos), y se usa el mismo espacio. Si el conjunto de lectura, el conjunto de escritura y el conjunto de errores cada conjunto son 10 descriptores de archivo, un total de 30 descripciones de archivo Siempre que cambie cualquier descriptor de archivo, como un descriptor de archivo en el conjunto de lectura, se vuelve legible, la función devuelve, es decir, el número de descriptores de archivo que se pueden leer en el conjunto de lectura, que es 1. Las demás colecciones se vaciarán. Para monitorear nuevamente, necesita restablecer el sitio de monitoreo.

2 Se puede cambiar el número de descriptores de archivo que se pueden abrir en un proceso, ulimit -a. Entonces hay un problema con el primer parámetro nfds. Si el número de descriptores de archivo que monitoreamos excede el rango máximo de enteros con signo, se desbordará.

select () organiza descriptores de archivos en unidades de eventos

Supongo que te gusta

Origin blog.csdn.net/LinuxArmbiggod/article/details/114670289
Recomendado
Clasificación