Uso de la función select de multiplexación de E / S bajo Linux

El papel de la función select:

    Si hay dos lugares en nuestro programa que necesitan ser bloqueados, como leer datos del servidor y leer datos del teclado al mismo tiempo (si no utiliza el bloqueo y las consultas, ocupará muchos recursos del sistema) . En este momento tenemos dos bloqueos, por supuesto , puede usar un método multiproceso o multiproceso para resolver , pero hoy presentamos otro método: multiplexación de E / S, como select, sondeo y otros mecanismos, select it puede monitorear Necesitamos monitorear los cambios en el archivo descriptor-leer o escribir o anormal.

头文件:
#include <sys/select.h>

函数原型:
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

Parámetro:

1.int maxfdp es un valor entero, que se refiere al rango de todos los descriptores de archivo en la colección, es decir , el valor máximo de todos los descriptores de archivo más 1 , ¡no puede estar equivocado! En Windows, el valor de este parámetro no importa y se puede configurar incorrectamente.
2. fd_set * readfds es un puntero a la estructura fd_set, este conjunto debe incluir descriptores de archivos, debemos monitorear los cambios de lectura de estos descriptores de archivos, es decir, nos preocupa si podemos leer datos de estos archivos , si esto Hay un archivo legible en la colección, select devolverá un valor mayor que 0, lo que indica que hay un archivo legible, si no hay un archivo legible, de acuerdo con el parámetro de tiempo de espera para determinar si el tiempo de espera, si el tiempo excede el tiempo de espera, select devuelve 0 Si se produce un error, se devuelve un valor negativo. Puede ingresar un valor NULL, lo que indica que no le importan los cambios de lectura de archivos.
3. fd_set * writefds es un puntero a la estructura fd_set, este conjunto debe incluir descriptores de archivos, debemos monitorear los cambios de escritura de estos descriptores de archivos, es decir, nos preocupa si podemos escribir datos en estos archivos , si esto Si hay un archivo en la colección que se puede escribir, select devolverá un valor mayor que 0, lo que indica que hay un archivo que se puede escribir. Si no hay ningún archivo que se pueda escribir, el tiempo de espera se determinará de acuerdo con el tiempo de espera. Se devuelve un valor negativo cuando se produce un error. Puede pasar un valor NULL para indicar que no le importan los cambios de escritura de archivos.
4.fe_set * errorfds es la misma que la intención de los dos parámetros anteriores, utilizada para monitorear las excepciones de error de archivo.
5. struct timeval * timeout es el tiempo de espera de select , este parámetro es muy importante, puede hacer select en tres estados.
      Primero: si se pasa NULL como un parámetro formal, es decir, no se pasa ninguna estructura de tiempo, la selección se coloca en un estado bloqueado, Debe esperar hasta que un descriptor de archivo cambie en el conjunto de descriptores de archivo de monitoreo;
      segundo: si el valor de tiempo se establece en 0 segundos 0 milisegundos, se convierte en una función pura sin bloqueo, independientemente de si el descriptor de archivo ha cambiado, Todos regresan inmediatamente para continuar la ejecución, el archivo devuelve 0 si no hay cambio y un valor positivo cuando hay un cambio;
      tercero: el valor del tiempo de espera es mayor que 0, este es el período de espera del tiempo de espera, es decir, la selección se bloquea dentro del período de tiempo de espera y llega un evento dentro del período de tiempo de espera Regresa, de lo contrario, volverá sin importar lo que pase después del tiempo de espera, el valor de retorno es el mismo que el anterior.

Valor de retorno:

  Valor negativo: seleccione error;
  valor positivo: algunos archivos se pueden leer y escribir o error;
  0: espere el tiempo de espera, no hay ningún archivo que pueda leerse , escribirse o ser incorrecto.


Código de error:

Si la ejecución es exitosa, se devuelve el número de descriptores de archivo cuyo estado ha cambiado. Si se devuelve 0, significa que ha transcurrido el tiempo de espera antes de que el estado del descriptor haya cambiado. Cuando se produce un error, se devuelve -1. La causa del error se almacena en errno. En este momento, el parámetro readfds, Los valores de writefds, excepto fds y timeout se vuelven impredecibles.
 

EBADF 文件描述词为无效的或该文件已关闭

EINTR 此调用被信号所中断

EINVAL 参数n为负值。

ENOMEM 核心内存不足

Descripción de la función:

select () se usa para esperar a que cambie el estado del descriptor de archivo. El parámetro maxfdp representa el descriptor de archivo más grande más 1, y los parámetros readfds, writefds y exceptfds se denominan frases descriptivas y se utilizan para devolver el estado de lectura, escritura o excepciones al descriptor. La macro a continuación proporciona una manera de manejar estas tres frases descriptivas:

FD_CLR(inr fd,fd_set* set);		用来清除描述词组set中相关fd的位

FD_ISSET(int fd,fd_set *set);	用来测试描述词组set中相关fd的位是否为真

FD_SET(int fd,fd_set*set);		用来设置描述词组set中相关fd的位

FD_ZERO(fd_set *set);			用来清除描述词组set的全部位

Descripción de la estructura:

1) struct fd_set puede entenderse como un conjunto, que almacena descriptores de archivo (descriptores de archivo), es decir, identificadores de archivos, que pueden ser lo que llamamos archivos de significado ordinario, por supuesto, cualquier dispositivo, tubería, FIFO, etc. bajo Unix son Tiene la forma de un archivo, todo incluido, por lo que no hay duda de que un socket es un archivo y un controlador de socket es un descriptor de archivo.
La colección fd_set puede ser manipulada manualmente por algunas macros, como
 

清空集合FD_ZERO(fd_set *);
将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set*);
将一个给定的文件描述符从集合中删除FD_CLR(int,fd_set*);
检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。一会儿举例说明。

2) struct timeval es una estructura comúnmente utilizada por todos para representar valores de tiempo: hay dos miembros, uno es segundos y el otro son números sutiles. Como se muestra a continuación:

truct timeval
{
	time_t tv_sec;		//seconds 秒
	time_t tv_usec;		//microseconds 微秒,1000000 微秒 = 1秒
}; 
//头文件
#include <sys/time.h>

Nota:

1. Los parámetros de cada descriptor de archivo fd_set establecido en la función select () han enviado cambios antes y después de select ():

Antes: indica la colección de descriptores de archivos de interés

Después: una recopilación de datos (si no se devuelve en un tiempo de espera)

En otras palabras: primero configuramos el descriptor de archivo de cada E / S para monitorearlo en la colección fd_set, luego llamamos a select (), y finalmente la colección fd_set solo tiene archivos "anormales" (incluyendo lectura, escritura y anormal) Descriptor, por lo que los pasos mencionados anteriormente para establecer la colección fd_set, llamar a select () y determinar qué IO tiene datos, generalmente se implementan en un ciclo while.

2. El núcleo realiza los cambios enviados por la colección fd_set antes y después de llamar a la función de selección.

Ejemplo 1: ¿Hay datos en el teclado de monitoreo en Linux?

#include <sys/time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>

#include <unistd.h>
#include <iostream>
using namespace std;

int main ()
{
    int keyboard;
    int ret,i;
    char c;
    fd_set readfd;
    struct timeval timeout;
    keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);
    assert(keyboard>0);
    while(1)
    {
        timeout.tv_sec=1;
        timeout.tv_usec=0;
        FD_ZERO(&readfd);                //结构体清0
        FD_SET(keyboard,&readfd);        //把键盘文件的句柄加入"读集合"之中
 
        ///监控函数
        ret=select(keyboard+1,&readfd,NULL,NULL,&timeout);    //第一个参数为最大文件句柄+1
        if(ret == -1)   //错误情况
            cout<<"error"<<endl ;
        else if(ret)    //返回值大于0 有数据到来
            if(FD_ISSET(keyboard,&readfd))    //检查集合中键盘文件是否可以读(因为加入"读集合"的可能不止键盘)
            {            
                /* 读出键盘的数据 */
                i=read(keyboard,&c,1);
                if('\n'==c)
                    continue;
                printf("hehethe input is %c\n",c);
                if ('q'==c)
                    break;
            }
        else    //超时情况
        {
            cout<<"time out"<<endl;
            continue;
        }
    }
}

 

Ejemplo 2: ¿Se puede leer la supervisión de varios archivos en Linux (pseudocódigo)?

1.    
    fd_set tRFds;            //定义fd_set类型结构体的变量tRFds
    FD_ZERO(&tRFds);		//结构体先清0
    int g_iMaxFd = -1;        //定义g_iMaxFd记录 最大句柄+1
2.
    比较要监控的各个文件的句柄,取出最大值再加1赋给g_iMaxFd
    if(Fd1 > Fd2)
    {
        g_iMaxFd = Fd1+1;
    }
    else
    {
        g_iMaxFd = Fd2+1;
    }
3.
    FD_SET(Fd1, &tRFds);	//把Fd1、Fd2加入"读集合"之中
    FD_SET(Fd2, &tRFds);    //Fd1和Fd2为两个不同的文件句柄
    ...
4.
    iRet = select(g_iMaxFd, &tRFds, NULL, NULL, NULL);
	if (iRet > 0)			//返回正数说明有数据可读了
	{
		
		if (FD_ISSET(Fd1, &tRFds))        //是文件1可读
		{
			Fd1_read(...);
		}

        else if(FD_ISSET(Fd2, &tRFds))      //是文件2可读
        {    
            Fd2_read(...);  
        }	

        ...
	}

La referencia principal de este artículo: https://blog.csdn.net/mayue_web/article/details/89021273

42 artículos originales publicados · Me gusta 10 · Visitantes más de 10,000

Supongo que te gusta

Origin blog.csdn.net/qq_37659294/article/details/104263105
Recomendado
Clasificación