工控网中基于Linux的嵌入式HTTP服务器设计
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()返回一个新的套接口描述符,主进程就创建一个新的子线程(轻质进程)处理这个新的连接。
子线程用于处理每具体的HTTP请求。子线程首先解析用户的HTTP请求。当用户请求一个网页时,子线程查找文件系统。如果该网页文件存在,且通过权限认证,就把它从CF卡读入内存并扫描,发现有自定义标记则调用相应函数进行处理,最后把结果返回给浏览器;否则给一个简单的出错消息。当用户是上传数据时,子线程调用相应函数读取数据进行处理,并返回处理结果给浏览器。
4.2 代码设计
在嵌入式HTTP服务器的代码设计中,考虑到代码的移植性和扩展性,利用C语言实现了面向对象风格的代码结构。代码主要由两上数据结构request_inf和response_inf以及其上一组操作函数组成。
结构request_inf和response_inf分别用来保存HTTP请求报文和响应报文的所有信息。在结构定义时,应根据具体应用特点设计结构包含的成分。
嵌入式HTTP服务器的函数包括通用函数、CGI函数和自定义标记处理函数等,其中通用函数是一些与HTTP1.1协议有关的函数。
(1)通用函数
*void prase_request_line(char *,struct *request_inf)
该函数用来解析HTTP请求报文的请求行(Request_Line),并把相应信息存放在结构request_inf中。其中,对请求行中URI部分的解析包括两种情况。如果用户请求一个网页,则获取文件路径、文件类型;如果用户要求上传数据,则把数据放在一个字符数组中。然后将文件路径和类型,或者指向该数组的指针、方法、版本号信息都放入结构re
《工控网中基于Linux的嵌入式HTTP服务器设计(第3页)》