`
lobin
  • 浏览: 427205 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

多路复用

 
阅读更多

多路复用这种技术最早是在信息论领域提出的。多路复用是指在一条物理信道上传播多种信号,每种信号的频率或者频段不一样,多路复用技术将一条物理信道划分成多个频道,每个频道传播一个频段的信号。这样多个频率或者频段的信号可以在一条物理信道上进行传播。主要目的是为了有效地利用带宽。

 

多路复用通常分为频分多路复用、时分多路复用、波分多路复用、码分多址和空分多址。

 

多路复用在软件编程领域的应用

 

在软件编程领域,多路复用技术也被用到实际编程中。比如我们需要同时处理来自控制台的标准I/O输入输出,来自文件的读写,管道的读写等。比如我们需要同时处理来自多个客户端的I/O操作,比如我们需要同时处理大量来自客户端程序的I/O请求。还有比如我们需要同时处理来自多个数据流/管道的数据处理,这种应用场景需求在目前大数据领域是非常普遍,很多大数据平台技术都有应用到这种技术的。

 

I/O多路复用

 

I/O多路复用的实现

I/O多路复用的实现比较典型包括Select、Poll、以及Epoll。

 

Select、Poll、以及Epoll的实现和应用我在另外几篇文章都有详细讲解。

 

为什么叫“I/O多路复用”呢?

I/O多路复用主要提供了一种机制来同时处理来自多个地方的I/O请求。类似像Select、Poll以及Epoll这些实现,它们都是基于一种I/O事件处理机制来同时处理多个fd的I/O事件,进而基于事件驱动来处理相关fd的I/O请求。

 

kqueue

 

写道
kernel event notification mechanism

int kqueue(void);

该系统调用提供了一种当kevent内核事件发生或者条件存在时通知用户的基于一种称为过滤器(一小段内核代码)的结果的通用机制.

 

内核事件kevent通过一个(ident, filter)对来标识表示。

 

我通过man kqueue查看帮助文档时还看到,unix后面把可选的udata value也作为标识内核事件kevent的一个要素,即kevent通过一个(ident, filter, and optional udata value)元组来标识表示,这里的叫法也不一样了,这里是一个三元组,当然上面的(ident, filter)对也是一个元组,上面是一个二元组。udata value作为标识内核事件kevent的一个要素是可选的,只有kevent的flags指定了EV_UDATA_SPECIFIC,udata value才被认为是元组的一部分,即是一个三元组,udata value也作为标识内核事件kevent的一个要素。否则的话,udata value不被认为是元组的一部分,即还是一个三元组,和上面的一样。

 

kqueue的事件触发时机和linux下epoll的水平触发类似,事件注册后,只要读缓存中有数据可读,就会触发EVFILT_READ读事件,只要写缓存可写,就会触发EVFILT_WRITE写事件。

 

 

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/event.h>

typedef struct
{
  char *ptr;
  unsigned int size;
  unsigned int offset; 
} buffer_t;

#define BUFFER_INITIAL_DEFAULT_SIZE 1024

int buffer_alloc(buffer_t *buf, int size)
{
  if (! buf) 
  {
    return -1;
  }
  if (size <= 0)
  {
    size = BUFFER_INITIAL_DEFAULT_SIZE;
  }
  buf->size = size;
  buf->ptr = (char *) malloc(buf->size);
  if (! buf->ptr)
  {
    return -1;
  }
  buf->offset = 0;
  return 0;
}

int main(int argc, char **argv)
{
  int kq_fd;

  struct kevent event, *eventlist;
  int nevents = 100;

  int fd;
  struct sockaddr_in sock_addr;
  int reuse = 1;
  buffer_t *r_buf;

  kq_fd = kqueue();
  if (kq_fd == -1)
  {
    printf("kqueue err=%d!\n", errno);
    return -1;
  }  

  fd = socket(AF_INET, SOCK_STREAM, 0);
  if (fd == -1)
  {
    printf("socket err=%d\n", errno);
    return -1;
  }

  bzero(&sock_addr, sizeof(struct sockaddr_in));
  sock_addr.sin_family = AF_INET;
  sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);

  sock_addr.sin_port = htons(3333);
  
  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
  {
    printf("setsockopt err=%d, attempting to set SO_REUSEADDR option for sock=%d\n", errno, fd);
    return -1;	
  }

  if (bind(fd, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr_in)) == -1)
  {
    printf("bind err=%d\n", errno);
    return -1;
  }

  if (listen(fd, 50) == -1)
  {
    printf("listen err=%d\n", errno);
    return -1;
  }

  r_buf = malloc(sizeof(buffer_t));
  buffer_alloc(r_buf, 0);

  event.ident = fd;
  event.filter = EVFILT_READ;
  event.flags = EV_ADD | EV_ENABLE;
  event.fflags = 0;
  event.data = 0;
  event.udata = r_buf;

  // just register, do not return pending events.
  // and, only resiter one event by one call, but not pass 
  // an array of event to changelist to register number of 
  // events by one call.
  if (kevent(kq_fd, &event, 1, NULL, 0, NULL) == -1)
  {
    printf("kevent err=%d, while register events.\n", errno);
    return -1;
  }

  eventlist = malloc(sizeof(struct kevent) * nevents);
  if (eventlist == NULL)
  {
    printf("alloc err!\n");
    return -1;
  }
 
  while (1)
  {
    int i;

    // just return pending events, do not register event.
    int nretevents = kevent(kq_fd, NULL, 0, eventlist, nevents, NULL);
    if (nretevents == -1)
    {
      printf("kevent err=%d\n", errno);
      return -1;
    }
    printf("kevent, return %d\n", nretevents);
    for (i = 0; i < nretevents; i++)
    {
      struct kevent *e = &eventlist[i];
      buffer_t *r_buf = e->udata;
      
      printf("%d: ident=%lu, filter=%d, flags=%u, fflags=%u\n", 
        i, e->ident, e->filter, e->flags, e->fflags);
      if (e->filter == EVFILT_READ)
      {
        if (e->ident == fd)
        {
          // accept
          int fd_get;
          struct sockaddr_in sockaddr;
          char *ip;

          socklen_t sockaddr_len = sizeof(struct sockaddr_in);
          fd_get = accept(fd, (struct sockaddr *) &sockaddr, &sockaddr_len);
          if (fd_get == -1)
          {
            printf("accept err=%d\n", errno);
          }
          else
          {
            buffer_t *r_buf;

            ip = inet_ntoa(sockaddr.sin_addr);
            printf("accept, ip=%s\n", ip);


            r_buf = malloc(sizeof(buffer_t));
            if (! r_buf)
            {
              printf("alloc err!\n");
              return -1;	
            }
            if (buffer_alloc(r_buf, 0))
            {
              printf("buffer alloc err!\n");
              return -1;	
            }
            
            event.ident = fd_get;
            event.udata = r_buf;

            // just register, do not return pending events.
            // and, only resiter one event by one call, but not pass 
            // an array of event to changelist to register number of 
            // events by one call.
            if (kevent(kq_fd, &event, 1, NULL, 0, NULL) == -1)
            {
              printf("kevent err=%d, while register events.\n", errno);
              return -1;
            }
          }
        }
        else
        {
          // read
          
          if (r_buf->offset >= r_buf->size)
          {
            int size = r_buf->size + BUFFER_INITIAL_DEFAULT_SIZE;
            r_buf->ptr = realloc(r_buf->ptr, size);
            if (! r_buf->ptr) 
            {
              printf("alloc err!\n");
            }
            else
            {
              r_buf->size = size;
            }
          }
          if (r_buf->offset < r_buf->size)
          {
            ssize_t nbytes;
            nbytes = read(e->ident, r_buf->ptr + r_buf->offset, r_buf->size - r_buf->offset);
            if (nbytes == -1)
            {
              printf("read err=%d\n", errno);
            }
            r_buf->offset += nbytes;
            printf("buffer: read=%.*s\n", r_buf->offset, r_buf->ptr);

            printf("event: ident=%lu, filter=%d, flags=%u, fflags=%u, data=%ld, udata=%p\n", 
              event.ident, event.filter, event.flags, event.fflags, event.data, event.udata);

            //*
            //event.filter = EVFILT_WRITE;
            //event.flags = EV_ADD | EV_ONESHOT | EV_ENABLE;
            
            event.ident = e->ident;
            event.filter = EVFILT_WRITE;
            event.flags = EV_ADD | EV_ONESHOT | EV_ENABLE;
            event.fflags = 0;
            event.data = 0;
            event.udata = r_buf;
            
            if (kevent(kq_fd, &event, 1, NULL, 0, NULL) == -1)
            {
              printf("kevent err=%d, while register events.\n", errno);
              return -1;
            }
            //*/
          }
        }
      }
      else if (e->filter == EVFILT_WRITE)
      {
        // write
        
        write(e->ident, "Ok\n", 3);
        
        printf("event: ident=%lu, filter=%d, flags=%u, fflags=%u, data=%ld, udata=%p\n", 
          event.ident, event.filter, event.flags, event.fflags, event.data, event.udata);
        //*
        //event.ident = e->ident;
        //event.filter = EVFILT_READ;
        //event.flags = EV_ADD | EV_ENABLE;
        
        event.ident = e->ident;
        event.filter = EVFILT_READ;
        event.flags = EV_ADD | EV_ENABLE;
        event.fflags = 0;
        event.data = 0;
        event.udata = r_buf;
        if (kevent(kq_fd, &event, 1, NULL, 0, NULL) == -1)
        {
          printf("kevent err=%d, while register events.\n", errno);
          return -1;
        }
        //*/
      }
    }
  }
  return 0;
}

 

 

 

 

分享到:
评论

相关推荐

    IO多路复用之select全面总结(必看篇)

    IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合: (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。 (2)当一个客户...

    多路复用模拟IIC程序

    首先,"多路复用"是指在单个引脚上控制多个设备的能力,这是通过GPIO端口的多路复用功能实现的。STM32芯片具有强大的GPIO复用功能,允许一个引脚在不同功能之间切换,例如,既可以作为普通输入/输出,也可以作为I²C...

    windows下多路复用IO(select,WSAAsyncSelect,WSAEventSelect)

    在Windows操作系统中,多路复用I/O(Multiplexed Input/Output)是一种高效地管理多个网络连接的技术,它允许程序同时处理多个套接字事件,而无需为每个连接创建单独的线程或进程。本篇文章将深入探讨三种在Windows...

    博图HMI多路复用功能

    在HMI设计中,多路复用功能是一种高效的方法,能够大大提升编程效率,特别是在处理大量相似数据或变量时。 在PLC编程中,当需要控制多个相似设备,如伺服轴时,通常会使用用户定义的数据类型(UDT)和数组。UDT允许...

    TIA博途Wincc-多路复用变量的使用方法示例(实现多台相同设备参数的画面精简).docx

    本示例主要讲解如何使用TIA博途Wincc中的多路复用变量功能来实现多台相同设备参数的画面精简,从而提高项目的效率和操作便利性。 首先,多路复用变量是解决同一类型设备数量众多时,减少画面重复的一种技术。在本...

    多路复用技术的综述

    配置多路复用线路有许多种不同方法,多路复用器的类型也各异,常用的有频分多路复用(FDM)、时分多路复用(TDM)、波分多路复用(WDM)、码分多路复用(CDM)等。 频分多路复用(FDM)是一种常用的多路复用技术,...

    socket 多路复用

    Socket多路复用是一种在计算机网络编程中广泛使用的机制,主要应用于服务器端,它允许多个客户端连接同时处理,而无需为每个连接创建新的独立线程或进程。这种技术极大地提高了服务器的并发处理能力,降低了资源消耗...

    多进程&多路复用并发的http服务器

    Nginx以其卓越的性能和稳定性在Web服务器市场占据一席之地,其核心特性之一就是其对多路复用并发的支持。 **多进程模型** 在多进程模型中,服务器启动时会创建多个子进程来处理客户端请求。每个子进程都是独立的,...

    基于多路复用的阻塞服务器

    在IT领域,服务器是网络应用的核心,而"基于多路复用的阻塞服务器"是一种高效处理并发请求的技术。多路复用(Multiplexing)技术允许服务器通过单个套接字同时处理多个连接,显著提高了系统资源的利用率,降低了系统...

    io多路复用解析

    IO 多路复用解析 IO 多路复用是一种高效的输入/输出(I/O)管理机制,允许单个进程同时监控多个文件描述符(FD),以便在其中某个文件描述符上发生事件时,立即响应。IO 多路复用机制广泛应用于服务器端编程、网络...

    基于cyclone2 (EP2C8Q)设计的多路复用器(4通道8位带三态输出)Verilog源码 quartus 9.0工程

    基于cyclone2 (EP2C8Q)设计的多路复用器(4通道8位带三态输出)Verilog源码 quartus 9.0工程 module MUX_4_8 ( //Input ports. DI_SEL, DI_EN, DI_0, DI_1, DI_2, DI_3, //Output ports. DO ); //======...

    IO多路复用实现TCPecho

    在IT领域,IO多路复用是一种高效地管理多个并发连接的技术,尤其在服务器端编程中,它能显著提升系统资源利用率和响应速度。本文将深入探讨如何利用IO多路复用来实现一个TCP echo服务器,该服务器运行在Linux环境下...

    gsm0710muxd多路复用源码及协议文档

    《gsm0710muxd多路复用源码及协议文档》是关于GSM07.10多路复用协议的重要参考资料,对于理解并应用这一通信协议有着极其关键的作用。GSM07.10是全球移动通信系统(Global System for Mobile Communications, GSM)...

    西门子之地址多路复用如何工作?.zip

    西门子之地址多路复用如何工作?zip,西门子之地址多路复用如何工作?在地址多路复用中,根据多路复用变量的数值修改变量的地址参数。 地址多路复用的目的: 通过地址复用,可使用一个变量来寻址控制器地址区中的多个...

    串口多路复用协议gsm0710

    串口多路复用协议(Serial Multiplexing Protocol),通常被称为GSM07.10或CMUX(Control Multiplexer),是一种在串行通信中实现多路复用的技术,广泛应用于GSM网络中的BSS(Base Station Subsystem,基站子系统)...

    多路复用-11周.ppt

    多路复用是一种在通信系统中整合多个独立信号的技术,使得多个数据流可以在同一信道或媒介上传输,从而提高信道的利用率和系统的效率。本篇主要介绍节目复用、系统复用以及数据增值业务,同时涉及MPEG-2编码中的PES...

    多路复用IO的简单实现

    多路复用IO是一种高效的I/O管理策略,允许一个单独的线程或者进程同时处理多个I/O事件,极大地提高了系统的并发能力。在本主题中,我们将探讨如何实现一个多路复用IO的简单系统,以实现同时处理标准输入和TCP连接...

    windows下socket多路复用

    在Windows环境下进行网络编程,Socket多路复用是一种高效处理多个连接请求的技术,它允许多个套接字(sockets)在同一时刻被监控,以便服务端能够同时处理多个客户端的连接。这种技术通常通过`select`函数来实现,...

    select和epoll实现多路复用.zip

    在IT领域,多路复用技术是用于高并发网络编程的一种高效策略,它允许一个单一的线程同时处理多个连接。本项目通过`select`和`epoll`两种方法实现了这一功能,并使用`jmeter`进行了性能测试。下面将详细讨论这两个多...

Global site tag (gtag.js) - Google Analytics