项目来源
https://github.com/linyacool/WebServer
项目内容
-
使用了epoll边沿触发+EPOLLONESHOT+非阻塞IO
-
使用了一个固定线程数的线程池
-
实现了一个任务队列,由条件变量触发通知新任务的到来
-
实现了一个小根堆的定时器及时剔除超时请求,使用了STL的优先队列来管理定时器
-
解析了HTTP的get、post请求,支持长短连接
-
mime设计为单例模式
-
线程的工作分配为:
主线程负责等待epoll中的事件,并把到来的事件放进任务队列,在每次循环的结束剔除超时请求和被置为删除的时间结点
工作线程阻塞在条件变量的等待中,新任务到来后,某一工作线程会被唤醒,执行具体的IO操作和计算任务,如果需要继续监听,会添加到epoll中
- 锁的使用有两处:
第一处是任务队列的添加和取操作,都需要加锁,并配合条件变量,跨越了多个线程。
第二处是定时器结点的添加和删除,需要加锁,主线程和工作线程都要操作定时器队列。
ip地址和端口复用
https://blog.csdn.net/qq_18973645/article/details/54729035
epoll边沿触发
https://blog.csdn.net/heluan123132/article/details/70196759
EPOLLONESHOT
https://blog.csdn.net/le119126/article/details/46364399
https://blog.csdn.net/liuhengxiao/article/details/46911129
如果是多线程在处理,一个SOCKET事件到来,数据开始解析,这时候这个SOCKET又来了同样一个这样的事件,而你的数据解析尚未完成,那么程序会自动调度另外一个线程或者进程来处理新的事件,这造成一个很严重的问题,不同的线程或者进程在处理同一个SOCKET的事件,这会使程序的健壮性大降低而编程的复杂度大大增加!!即使在ET模式下也有可能出现这种情况!!
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
线程池
https://www.cnblogs.com/yangang92/p/5485868.html
互斥锁、条件变量:https://blog.csdn.net/ffilman/article/details/4871920
定时器
http://www.cnblogs.com/zhanghairong/p/3757656.html
epoll
epoll_fd //所有监听的事件的集合
// 添加/修改/删除 描述符 事件类型
epoll_ctl ( epoll_fd, EPOLL_CTL_ADD, fd, &event ) //添加/修改/删除/事件
// 活跃事件集合
epoll_wait(epoll_fd, events, max_events, timeout)//返回活跃事件的数目
struct epoll_event
{ _u32 events; //事件类型
union
{ //这是一个联合体,只能存一个数据,但是数据和描述符都是必须的,
//所以要将fd放到自定义数据中
void * ptr; //自定义数据
int fd; //描述符
_u32 u32;
_u64 u64;
} data;
};
线程池
struct threadpool_t
{
pthread_mutex_t lock; //互斥锁
pthread_cond_t notify; //条件变量
pthread_t *threads; //线程数组(线程的ID)
threadpool_task_t *queue; //任务队列
int thread_count; //线程数量
int queue_size; //任务队列数量
int head; //队头
int tail; //队尾
int count; //正在等待的任务数量
int shutdown;
int started;
};
//初始化线程池
threadpool_t *threadpool_create(int thread_count, int queue_size, int flags);
//增加一个任务到队列,需要加锁,
int threadpool_add(threadpool_t *pool, void (*function)(void *), void *argument, int flags);
//线程执行,需要加锁,因为要从队列中获取任务
static void *threadpool_thread(void *threadpool);
互斥锁和条件变量:
https://www.cnblogs.com/gvlthu23061/p/7315980.html
与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量分为两部分:条件和变量。条件本身是由互斥量保护的。线程在改变条件状态前先要锁住互斥量。条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待“条件变量的条件成立”而挂起;另一个线程使“条件成立”(给出条件成立信号)。条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两线程共享可读写的内存,条件变量可以被用来实现这两线程间的线程同步。 互斥锁一个明显的缺点是它只有两种状态,锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般来说,条件变量被用来进行线程间的同步。条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出。线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待被下一次唤醒。这个过程一般用while语句实现。
状态解析:https://blog.csdn.net/u014530704/article/details/78932967
线程池:https://www.cnblogs.com/yangang92/p/5485868.html
url:https://www.cnblogs.com/zhuanzhuruyi/p/6508565.html
进展
准备
配置了sublime、opencv、修改了Makefile,终于是跑起来了