3.4 多线程
最初的进程定义包含程序、资源及其执行三部分,其中程序通常指代码,资源通常包括 内存资源、I/O资源、信号处理等,而程序的执行指执行上下文,这一部分后来发展为线程。在线程的概念出现以前,为了减小进程切换的开销,操作系统设计者逐渐修改正进程的概念,允许将进程所占有的资源从其主体剥离出来,允许某些进程共离享一部分资源,例如文件、信号、数据内存、甚至代码,这就是轻质进程的概念。Linux内核的2.0.x版本就已经实现了轻质进程。应用程序可以通过一个统一的clone()系统调用接口,用不同的参数指定创建轻质进程还是普通进程。在内核中,clone()调用经过参数传递和解释后会调用do_fork(),这个核内函数同时也是fork、vfork()系统调用的最终实现。在do_fork()中,不同的flone_flags将导致不同的行为。在LinuxThreads中,使用(CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND)参数调用clone()创建“线程”,表示共享内存、共享文件系统、共享文件描述符表,以及共享信号处理方式。Linux操作系统下,已经实现基于轻质进程的符号POSIX1003.C标准的线程库LinuxThreads。
在传统的Unix服务器程序设计中,为了使服务器具备并发处理连接的能力,通常采用父进程处理连接,并调用fork()创建子进程来处理用户请求的方法。这种方法的缺点是进程创建慢,耗费资源,进程切换开销大,进程之间通信比较困难等,不适用对资源、速度有要求的嵌入式系统。因此,在嵌入式HTTP服务器的开发中使用线程的方法。利用LinuxThreads提供的pthread_create()等函数派生出线程,也即轻质进程来处理多个HTTP请求。
4、 工作流程和代码设计
4.1工作流程
嵌入式HTTP服务器程序开始运行时,主进程首先创建一个接口,并和主机地址绑定到一起,随后置为被动监听状态,等待客户端连接请求的到来。分别用函数socket()创建一个接口,bind()绑定地址,listen()监听,accept()接收来完成。当建立一个TCP连接后,函数accept()返回一个新的套接口描述符,主进程就创建一个新的子线程(轻质进程)处理这个新的连接。