首页 > 其他分享 >epoll机制

epoll机制

时间:2022-11-08 23:40:34浏览次数:34  
标签:task struct epoll fd 机制 NULL data


​/**
 * 该文件名为epoll.c
 *
 *
 * 我的测试环境AS4U3
 * [gan@localhost ~]$ uname -r
 * 2.6.9-34.EL
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <strings.h>

#include <pthread.h>
 

#define MAXLINE 1024
#define OPEN_MAX 100
#define LISTENQ 20
#define INFTIM 1000

#define LOCAL_IP "192.168.1.101" /* 修改为自己本地机器就可以测试了 */
#define SERV_PORT 5555

/**
 * thread task link
 */
struct task
{
  int fd; /* file descriptor */
  struct task *next; /* next task */
};

struct user_data
{
  int fd;
  unsigned int n_size;
  char line[MAXLINE];
};

/* thread exec function */
void *readtask(void *args);
void *writetask(void *args);
 

/* declare epoll_event */
struct epoll_event ev, events[20];
int epfd;

pthread_mutex_t mutex; /* 线程安全使用 */
pthread_cond_t cond1; /* 线程条件等待使用 */

struct task *readhead = NULL,
        *readtail = NULL,
        *writehead = NULL;

void setnonblocking(int sock)
{
  int opts;

  opts = fcntl(sock, F_GETFL);
  if(opts<0)
   {
     perror("fcntl(sock,GETFL)");
     exit(1); /* 其实这样做不怎么好, 最好自己做好出错处理的工作, 不光是进程退出就可以了 */
   }

  if(fcntl(sock, F_SETFL, opts | O_NONBLOCK)<0)
   {
    perror("fcntl(sock,SETFL,opts)");
    exit(1);
   }
}

int main()
{
  int i, maxi, listenfd, connfd, sockfd, nfds;
  socklen_t clilen;
  pthread_t tid1,tid2;
  struct task *new_task=NULL;
  struct user_data *rdata=NULL;

  /* initialize the thread pool */
  pthread_mutex_init(&mutex, NULL);
  pthread_cond_init(&cond1, NULL);

   /* 创建线程, 最好做好错误处理工作, 自己也比较懒. 真正作东西千万别这样噢! */
  pthread_create(&tid1, NULL, readtask, NULL);
  pthread_create(&tid2, NULL, readtask, NULL);

   /* 生成用于处理accept的epoll专用的文件描述符
    * 以前从没用过
    *

Create a new epoll file descriptor by requesting the kernel allocate an event backing store dimensioned[n. 尺寸, 尺度, 维(数), 度(数), 元] for size descriptors. The size is not the maximum size of the backing store but just a hint to the kernel about how to dimension internal structures. The returned file descriptor will be used for all the subsequent calls to the epoll interface. The file descriptor returned by epoll_create must be closed by using POSIX::close.

When successful, epoll_create returns a positive integer identifying the descriptor. When an error occurs, epoll_create returns -1 and errno is set appropriately.

    *
    */
  epfd = epoll_create(256);

  struct sockaddr_in clientaddr;
  struct sockaddr_in serveraddr;

  listenfd = socket(AF_INET, SOCK_STREAM, 0);

  //把socket设置为非阻塞方式

  setnonblocking(listenfd);

   //设置与要处理的事件相关的文件描述符

  ev.data.fd = listenfd;

   //设置要处理的事件类型

  ev.events = EPOLLIN | EPOLLET;

  /*注册epoll事件

Control an epoll descriptor, $epfd, by requesting the operation op be performed on the target file descriptor, fd.

$epfd is an epoll descriptor returned from epoll_create.
$op is one of EPOLL_CTL_ADD, EPOLL_CTL_MOD or EPOLL_CTL_DEL.
$fd is the file desciptor to be watched.
$eventmask is a bitmask of events defined by EPOLLIN, EPOLLOUT, etc.

When successful, epoll_ctl returns 0. When an error occurs, epoll_ctl returns -1 and errno is set appropriately.
   */
  epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

  bzero(&serveraddr, sizeof(serveraddr));
  serveraddr.sin_family = AF_INET;

  char *local_addr = LOCAL_IP;
  inet_aton(local_addr, &(serveraddr.sin_addr));

  serveraddr.sin_port = htons(SERV_PORT);
  bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
  listen(listenfd, LISTENQ);

  maxi = 0;
  for ( ; ; )
   {
     /*等待epoll事件的发生

Wait for events on the epoll file descriptor $epfd.

$epfd is an epoll descriptor returned from epoll_create.
$maxevents is an integer specifying the maximum number of events to be returned.
$timeout is a timeout, in milliseconds

When successful, epoll_wait returns a reference to an array of events. Each event is a two element array, the first element being the file descriptor which triggered the event, and the second is the mask of event types triggered. For example, if epoll_wait returned the following data structure:

     */
     nfds = epoll_wait(epfd, events, 20, 500);

      //处理所发生的所有事件

     for(i = 0; i < nfds; ++i)
      {
       if(events[i].data.fd == listenfd)
         {
         connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);
         if(connfd < 0)
           {
          perror("connfd<0");
          exit(1);
           }

         setnonblocking(connfd);

         char *str = inet_ntoa(clientaddr.sin_addr);

         printf("connect_from >> %s /n", str);

         ev.data.fd = connfd;//设置用于读操作的文件描述符

         ev.events = EPOLLIN | EPOLLET;//设置用于注测的读操作事件


           //注册ev

         epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
         }
       else if (events[i].events & EPOLLIN)
         {
          printf("reading!/n");

          if ((sockfd = events[i].data.fd) < 0)
            continue;

          new_task = (struct task *)malloc(sizeof(struct task));
          new_task->fd = sockfd;
          new_task->next = NULL;

          pthread_mutex_lock(&mutex);//添加新的读任务


          if(readhead == NULL)
             {
             readhead = new_task;
             readtail = new_task;
             }
          else
             {
             readtail->next = new_task;
             readtail = new_task;
             }

              //唤醒所有等待cond1条件的线程

           pthread_cond_broadcast(&cond1);
           pthread_mutex_unlock(&mutex);
           }
        else if (events[i].events & EPOLLOUT)
          {
           rdata = (struct user_data *)events[i].data.ptr;
           sockfd = rdata->fd;

         printf("thread.%u Write data fd.%d len.%d data.%s /n"
        , (uint32_t)pthread_self(), sockfd, rdata->n_size, rdata->line);

           write(sockfd, rdata->line, rdata->n_size);
           close(sockfd);

           free(rdata);

           ev.data.fd = sockfd;//设置用于读操作的文件描述符

           ev.events = EPOLLIN | EPOLLET;//设置用于注测的读操作事件


              //修改sockfd上要处理的事件为EPOLIN

           epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
           }
       }
   }
}

/**
 * thread exec function
 */
void *readtask(void *args)
{
  int fd = -1;
  unsigned int n;

  //用于把读出来的数据传递出去

  struct user_data *data = NULL;

  while (1)
   {
     pthread_mutex_lock(&mutex);

      //等待到任务队列不为空

     while(readhead == NULL)
      {
       printf("thread.%u waiting, task is NULL ... /n", (uint32_t)pthread_self());
       pthread_cond_wait(&cond1, &mutex);
      }

      fd = readhead->fd;

       //从任务队列取出一个读任务

      struct task *tmp = readhead;
      readhead = readhead->next;
      free(tmp);

      pthread_mutex_unlock(&mutex);

      data = (struct user_data *)malloc(sizeof(struct user_data));
      data->fd = fd;

      if ((n = read(fd, data->line, MAXLINE)) < 0)
        {
        if (errno == ECONNRESET)
          {
           close(fd);
          }
        else
          printf("readline error /n");

        if(data != NULL)
           free(data);
        }
      else if (n == 0)
       {
       close(fd);
       printf("Client close connect!/n");
       if(data != NULL)
             free(data);
       }
     else
       {
       data->n_size = n;

       printf("thread.%u read fd.%d len.%d data.%s /n"
        , (uint32_t)pthread_self(), fd, n, data->line);
        
       ev.data.ptr = data;//设置需要传递出去的数据

       ev.events = EPOLLOUT | EPOLLET;//设置用于注测的写操作事件


        //修改sockfd上要处理的事件为EPOLLOUT

       epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);
      }
   }
}


/**
 * 一个简单的Makefile
 */
all: epoll.c
        gcc -Wall -g -o epoll epoll.c -lpthread

clean:
        rm -f epoll​

标签:task,struct,epoll,fd,机制,NULL,data
From: https://blog.51cto.com/u_15747257/5835070

相关文章

  • CAS无锁机制
    1、背景传统Synchronized锁:悲观,如果没有获取到锁的情况下,会让当前线程变为阻塞的状态,释放CPU执行权,效率非常低。乐观锁:本质上没有锁,没有死锁现象,而且效率比较高......
  • grafana agent 动态配置内部机制简单说明
    grafanaagent动态配置目前属于一个体验特性,但是设计上利用了gomplate一个强大的模版引擎工具参考配置运行配置参考agentv2:image:grafana/agent:ma......
  • Linux高并发网络编程开发——epoll-udp
    在学习Linux高并发网络编程开发总结了笔记,并分享出来。10-Linux系统编程-第13天(epoll-udp)目录:一、学习目标二、复习1、通过gdb定位段错误的位置2、TCP状态转换复习三、epoll......
  • RabbitMQ的消息确认ACK机制
    ACK机制由于通信过程的不可靠性,传输的数据不可避免的会出现丢失、延迟、错误、重复等各种状况,TCP协议为解决这些问题设计了一系列机制。这个机制的核心,就是发送方向接收方发......
  • RabbitMQ3/4---持久化机制、内存磁盘控制
    1.RabbitMQ持久化机制RabbitMQ持久化机制分为队列持久化、消息持久化、交换器持久化。不管是持久化的消息还是非持久化的消息都可以被写入到磁盘。(1)RabbitMQ队列持久化队列......
  • logrotate机制与原理
    日志实在是太有用了,它记录了程序运行时各种信息。通过日志可以分析用户行为,记录运行轨迹,查找程序问题。可惜磁盘的空间是有限的,就像飞机里的黑匣子,记录的信息再重要也只能记......
  • python二机制文件解析
    参考连接:https://blog.csdn.net/lovelyaiq/article/details/81988185C语言解析:#include"stdlib.h"#include"stdio.h"typedefunsignedintuint32_t;typedefunsi......
  • jvm双亲委派机制详解
    双亲委派机制​ 记录一下JVM的双亲委派机制学习记录。类加载器种类​ 当我们运行某一个java类的main方法时,首先需要由java虚拟机的类加载器将我们要执行的main方法所......
  • SpringBoot 事件发布监听机制使用、分析、注意点 (一篇到位)
    前言这一篇从应用角度来跟大伙讲讲这个spring事件监听机制,顺便涉及到那些我认为大家应该一块了解的,我也会展开说说。文章内容(包括不限于):1.对比观察者模式2.应用场景......
  • 如何建立高效的质量保障机制
    转载https://www.cnblogs.com/imyalost/p/16069708.html前言这篇文章实际上构思了很久,如标题所述:如何建立高效的质量保障机制。在之前无论是写文章还是工作实践,在质量保......