您现在的位置是:首页 >技术教程 >网络编程 lesson4 linux下的IO模型和特点网站首页技术教程

网络编程 lesson4 linux下的IO模型和特点

SouthernBird 2024-06-17 10:20:03
简介网络编程 lesson4 linux下的IO模型和特点

目录

阻塞式IO

工作流程

 非阻塞式IO

工作流程

设置非阻塞IO方法

fcntl实例

信号驱动IO

工作流程

实例:在LINUX下检听自己鼠标的动作

IO多路复用(重点,难点)

工作流程

本文核心内容!!!使用多路I/O多路复用技术的基本流程

详细流程(了解)


在Linux中,有几种常见的I/O模型,它们决定了应用程序如何进行输入和输出操作。

阻塞式IO

工作流程

        在阻塞I/O模型中,当应用程序发起I/O操作时,它会一直阻塞(即暂停执行),直到操作完成并返回结果。这意味着应用程序无法执行其他任务,直到I/O操作完成。阻塞I/O模型对于简单的应用程序来说是最简单的模型,但在处理多个并发连接时可能会导致性能问题。

特点:最简单,最常用;效率低

常见的阻塞IO有:

read,recv,recvfrom
write,send(注意sendto没有阻塞)
accept,connect

 非阻塞式IO

工作流程

非阻塞I/O模型允许应用程序在发起I/O操作后立即返回,而不需要等待操作完成。如果操作不能立即完成,应用程序可以执行其他任务而不被阻塞。通过轮询来检查操作是否完成,可以实现非阻塞I/O。这种模型需要应用程序不断地查询I/O状态,可能会导致CPU资源浪费

特点:无需等待,可处理多路IO;需要轮询,浪费cpu资源

设置非阻塞IO方法

1.通过函数自带的参数进行设置

例如:recv函数中可以通过MSG_DONTWAIT进行设置

2.通过设置文件描述符设置非阻塞(fcntl函数)

int fcntl(int fd, int cmd, ...  );
功能:设置文件描述符属性
参数:
   fd:文件描述符
   cmd:设置方式 - 功能选择
        F_GETFL  获取文件描述符的状态信息     第三个参数化忽略
        F_SETFL  设置文件描述符的状态信息     通过第三个参数设置
                O_NONBLOCK  非阻塞
                O_ASYNC      异步
                O_SYNC      同步
  arg:设置的值  in
返回值:
      特殊选择返回特殊值 - F_GETFL  返回的状态值(int)
        其他:成功0  失败-1,更新errno
        
使用:0为例
  0-原本:阻塞、读权限  修改或添加非阻塞
  int flags=fcntl(0,F_GETFL);//1.获取文件描述符原有的属性信息
  flags = flags | O_NONBLOCK;//2.修改添加权限
  fcntl(0,F_SETFL,flags);    //3.将修改好的权限设置回去

fcntl实例

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

#define N 128
int main(int argc, char const *argv[])
{
    char buf[N];
    //注意fcntl得函数顺序
    int Jurisdiction = fcntl(0, F_GETFL); //将0得权限拿出
    Jurisdiction=0|O_NONBLOCK;    //添加上非阻塞权限
    fcntl(0, F_SETFL, Jurisdiction);      //将修改后得权限设置回去
    while (1)
    {
        fgets(buf, N, stdin);
        printf("%s",buf);
        sleep(1);
        if (strncmp("quite", buf, 5) == 0)
        {
            break;
        }
    }
    return 0;
}

信号驱动IO

工作流程

信号驱动I/O模型通过向应用程序发送信号来通知它I/O操作的完成。应用程序在发起I/O操作后可以继续执行其他任务,而当操作完成时,操作系统会发送一个信号给应用程序。应用程序需要注册信号处理程序来处理相应的信号。这种模型通常用于处理低延迟的I/O操作。

  • 通过信号方式,当内核检测到设备数据后,会主动给应用发送信号SIGIO
  • 应用程序收到信号后做异步处理即可。
  • 应用程序需要把自己的进程号告诉内核,并打开异步通知机制。

特点:异步通知模式,需要底层驱动的支持

也是通过fcntl实现

//将APP进程号告诉驱动程序
fcntl(fd, F_SETOWN, getpid());

//使能异步通知
int flag;
flag = fcntl(fd, F_GETFL);
flag |= O_ASYNC;  //也可以用FASYNC标志
fcntl(fd, F_SETFL, flag);

signal(SIGIO, handler);

实例:在LINUX下检听自己鼠标的动作

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

int fd;

void handler(int sig)
{
    char buf[32];
    int ret = read(fd, buf, sizeof(buf));
    buf[ret] = '';
    printf("mouse:%s
", buf);
}

int main(int argc, char const *argv[])
{
    fd = open("/dev/input/mouse0", O_RDONLY);
    if (fd < 0)
    {
        perror("open err.");
        return -1;
    }
    //1.将fd和进程pid告诉底层驱动
    //作用:当fd有IO操作,底层驱动将SIGIO信号发送给pid
    fcntl(fd, F_SETOWN, getpid());
    
    //设置fd的异步通知属性
    int flags = fcntl(fd, F_GETFL); //1.获取文件描述符原有的属性信息
    flags = flags | O_ASYNC;        //2.修改添加权限
    fcntl(fd, F_SETFL, flags);      //3.将修改好的权限设置回去

    signal(SIGIO, handler);
    while (1)
    {
        sleep(1);
        printf("welcome to SouthernBird.
");
    }
    close(fd);
    return 0;
}

IO多路复用(重点,难点)

工作流程

/O多路复用模型使用操作系统提供的特定机制(如select、poll、epoll)来监视多个I/O操作的状态。应用程序可以通过将多个I/O操作注册到一个多路复用器中,然后通过查询多路复用器来等待任何一个操作完成。

这样可以避免使用阻塞或非阻塞I/O时需要轮询查询每个操作的状态。I/O多路复用模型通常用于处理多个并发连接,提高了应用程序的性能和响应能力。

  • 采用阻塞式IO处理多路输入输出流效率太低,达不到想要的效果
  • 采用非阻塞进行轮询太浪费资源
  • 采用多线程多进程处理会让程序变得更加复杂

本文核心内容!!!使用多路I/O多路复用技术的基本流程

1. 先构造一张有关文件描述符的表(集合、数组); 
2. 将你关心的文件描述符加入到这个表中;
3. 然后调用一个函数。 select / poll 
4. 当这些文件描述符中的一个或多个已准备好进行I/O操作的时候
该函数才返回(阻塞)。
5. 判断是哪一个或哪些文件描述符产生了事件(IO操作);
6. 做对应的逻辑处理;

详细流程(了解)

多路复用(Multiplexing)I/O是一种在单个线程中同时监控多个I/O操作的机制,它可以帮助提高应用程序的性能和响应能力。以下是多路复用的基本流程:

  1. 创建并初始化多路复用器:在开始之前,需要创建一个多路复用器,通常使用操作系统提供的特定机制,如select、poll或epoll。这个多路复用器将用于监视多个I/O操作的状态。
  2. 创建并注册需要监视的I/O事件:在多路复用器创建之后,需要创建需要监视的I/O事件,例如套接字(socket)、文件描述符(file descriptor)等。对于每个I/O事件,需要将其注册到多路复用器中。
  3. 调用多路复用器等待事件:一旦所有I/O事件都注册到多路复用器中,应用程序可以调用多路复用器的等待函数(如select、poll、epoll_wait等),并指定等待的超时时间。多路复用器会在此处等待,直到至少有一个事件发生或超时。
  4. 处理已触发的I/O事件:当多路复用器的等待函数返回时,表示至少有一个I/O事件已经触发。应用程序可以遍历触发的事件列表,并采取相应的操作。可能的操作包括读取数据、发送数据、关闭连接等。
  5. 重复上述步骤:一旦处理完已触发的事件,应用程序可以再次注册新的I/O事件到多路复用器中,并回到第3步,继续等待和处理事件。

通过这个基本流程,应用程序可以在单个线程中同时监视和处理多个I/O事件,而无需为每个事件创建独立的线程或进程。这样可以减少线程或进程的开销,提高应用程序的性能和资源利用率。同时,多路复用也能提供更高的并发连接能力和响应能力,适用于需要处理大量并发连接的服务器应用程序。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。