web服务器进度。
编写web服务器
Q1.为什么要写web服务器?
A1.掌握多线程、线程池 编程
编写流程:
version 1.0: 1.拷贝并理解书上的例程->2.采用多线程处理请求->3.测试、优化、重构
1.拷贝并理解书上的例程
遇到的问题:
1.书上的绑定socket的方式,用ip无法访问。
为什么?
使用gethostbyname()取得的地址是错误的,取得了127.0.1.1。
为啥?
gethostbyname()是通过主机名来获取ip的,主机名与ip对应的信息放在/etc/hosts文件中,而我系统中这个文件中的信息是错误的。
我系统中的/etc/hosts文件:
127.0.1.1 ubuntu.ubuntu-domain ubuntu
2.用ip直接绑定socket,对于绑定的ip可以访问,但是对于没有绑定的ip不可以访问。
解决方法:
1.参考lighttp source code。
对于socket的bind实现在NetWork.c->network_server_init()中。
对于没有主机名的绑定:addr.sin_addr.s_addr = htonl(INADDR_ANY);
为什么用htonl(INADDR_ANY)来绑定,就可实现多个ip的访问?
INADDR_ANY 的值为 0.0.0.0
代表系统中的所以ip地址。
学习到的东西:
1.dup2()函数,复制文件描述符,用于重定向。
2.采用多线程处理
遇到的问题:
1.pthread_create()创建300多线程后报错, Resource temporarily unavailable
pthread有两种状态 joinable 和 unjoinable
如果线程是joinable,当线程自己退出或调用pthread_exit()时都不会释放线程所占用堆栈和
线程描述符表。只有当你调用pthread_join之后这些资源才会被释放。
如果是unjoinable的线程,这些资源会在线程退出时或pthread_exit()时自动释放。
unjoinable可以在pthread_create时指定,或在线程中调用pthread_detach(pthread_self())
将状态改为unjoinable。
2.什么是文件描述符?
linux下一种抽象的标识(整数),用于访问文件。
3.oepn()做了什么?
把一个文件描述符和文件联系起来。
4.close()做了什么?
解除文件描述符和文件的联系。
5.不关闭文件描述符 会导致什么结果?
文件描述符可能会被用完(整数是有限的),导致不能打开新的文件
学到的东西:
1.fclose()会导致 文件描述符的关闭。
3.测试、优化、重构
性能测试工具 gprof 的使用
1.编译时加上 -pg
2.程序运行后会生成gmon.out
3.gprof bin/server gmon.out -b 看结果
遇到的问题
1.为什么server Ctrl+C(SIGINT) 退出没有生成gmon.out?
In order for the profiling data (gmon.out) to be written, the program must either exit or return from main.
Other exit methods, such as exits from signals (including SIGINT or SIGTERM, generated by typing ^C),
will not emit profiling information correctly.
解决方法:
用其他信号退出 如SIGUSR2
-------------------------------------------------------------------------
version 1.1: 1.实现线程池
Q1.什么是线程池
A1.线程的集合
Q2.为什么需要线程池
A2.由于创建线程和释放线程是需要时间的,如果程序需要大量的线程,那么在线程创建和释放上花销会很大。
Q3.什么情况先需要线程池
A3.需要大量的线程来完成的任务,例如web服务器,游戏服务器
Q4.线程池是干嘛的
A4.主要就是减少线程创建和释放上的花销。
线程池组成部分:
1.线程池管理:用于创建并管理线程池
2.工作线程(work_thread):线程池中的线程
3.任务接口(task):任务的通用接口,供工作线程使用
4.任务队列:用于存放还没有处理的任务,一种缓存机制
5.用于线程同步的互斥锁
线程池工作模式:
1.由使用者向任务队列中添加任务
2.工作线程从队列中取出任务并执行
3.如果队列中没有任务则睡眠
1.实现线程池的创建
Q1.如何实现线程池的数据结构
A1.需要的信息
1.线程的集合
2.每个线程的状态
3.总线程的数量
4.任务队列
2.实现工作线程
Q1.需要完成什么工作
A1.
1.工作线程从队列中取出任务并执行
需要实现任务接口与任务队列
2.如果队列中没有任务则睡眠
3.线程的同步
读写锁
3.实现任务队列
1.先实现一个队列
由于是web服务器,队列的大小很难确定,所以要是用链式结构。
但是链式结构,会频繁的调用malloc,free,所以还要实现一个空闲链表,来解决这一问题。
2.任务的数据结构设计
1.用于执行任务的函数(接口统一)
2.函数需要的参数
---------------------------------------------------------------------
线程池模型基本完成
1.测试->2.优化
1.测试
遇到的问题
1.程序莫名奇妙的退出
原因:线程初始化在队列初始化的前面,线程创建后会访问队列,但是这时
队列还没创建,所以会发生访问内存无效的错误。
2.优化
1.优化任务队列
工作线程会频繁的访问任务队列,所以这块要求的效率很高
1.使用链表队列,会造成频繁的调用malloc函数,效率会很低。
如果使用空闲链表是不是,会提高效率呢?
答案是肯定的,只要在空闲链表够用的情况下,可以在O(1)的时间内创建任务。
在这里使用普通的链表,肯定效率是达不到要求的。
比如说:我们要初始化空闲链表给它100个节点,普通的情况是调用100次malloc函数来分配。这样肯定是不行的。
这个问题可以这样解决:我们把100个节点,看成一个大的节点,一次就分配一个大的节点。这样malloc的调用次数就减少了。
当然这100个节点的连接方式还是和普通链表一样的.
2.优化工作线程
现在的工作模式是,工作线程不断的去访问任务队列,获取任务,这样效率是很低的。
假设有100个线程,那这100个线程就会不断的去访问任务队列,但是由于任务队列是临界资源,一个线程访问时其他线程会阻塞等待,
这样就有可能导致在添加任务时阻塞等待的时间过长。