编译
读代码习惯于先跑起来。源代码中写得很清楚了,人家是针对 Solaris 写的,在linux上需要稍微修改一下:
- 去掉 Makefile 中的”-lsocket”
- 把 Makefile 中的”-lpthread” 挪到源代码文件后面
总的过程
代码量不大,先过一遍代码结构和风格,同时列出一些各步中用到的函数(特别感兴趣的可以深入查一下);第二遍再看一些系统函数细节的处理;最后可以扩展一下一些感兴趣的概念。不过代码很小,只有一个文件,加上注释才 502 行,所以一次读完细节也没有什么问题。
代码结构
扩展
HTTP 请求和响应格式
- 请求 <request-line> : 包含请求类型、要访问的资源和 HTTP 版本等,如 “GET / HTTP/1.1”<headers><blank line><body>
- 响应 <status-line> : 如 “HTTP/1.1 200 OK”<headers><blank line>[<response-body>]
Linux 创建进程
fork函数可以克隆一份自己作为子进程,父进程中返回子进程的 PID,在子进程中返回 0:pid_t pid = fork();if (pid < 0) { // 错误,通常是最大进程数限制或内存用尽} else if (pid == 0) { // 子进程代码} else { // 父进程代码}
pid_t pid = fork();
if (pid < 0) {
// 错误,通常是最大进程数限制或内存用尽
} else if (pid == 0) {
// 子进程代码
} else {
// 父进程代码
}
Linux C 中执行 Shell 命令
主要有三种方法:fork+exec、system、popen。
- fork + exec族函数 : exec族函数会替换原有的进程(保留进程号和环境变量),所以经常和fork配合:fork一个子进程,子进程中使用exec启动 Shell 命令。如果子进程立即执行exec,那么可以使用vfork,它创建的进程不会拷贝,在执行exec之前会在父进程的空间中执行(并会保证比父进程先执行,执行了exec后才能开始调度),从而提高效率,但是也存在子进程有可能破坏父进程数据的风险。
- system :重新启动一个 Shell 命令(就调用fork创建子进程来执行/bin/sh -c),阻塞父进程,执行完后返回,父进程继续执行。
- popen : 和system类似,会建立一个与父进程通信的管道(单向的)。
我觉得fork + exec一种就可以了,尽管简单的可能会多写点代码,但足够灵活,并且也不是很复杂。
管道操作
Linux 管道是半双工的,如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。
pipe函数创建管道:
#include
// …
int pipe_fields[2];
if(0 == pipe(pipe_fields)) {
// pipe_fields[0]是读取端
// pipe_fields[1]是写入端
} esel {
// 失败
// erron == EMFILE 进程已用完文件描述词最大量
// erron == ENFILE 系统已无文件描述词可用
// erron == EFAULT 参数 filedes 数组地址不合法
}
执行 CGI 时,在父进程中创建两个管道用以双向传输, 父子进程中可以关闭不相关的输入和输出端。子进程中使用dup2函数将输入和输出管道复制到标准输入和标准输出,使用execl调用 CGI 脚本,这样CGI脚本中对标准输入输出的操作就会定向到与父进程通信的管道。
pid_t = fork();
pipe(in_pipes);
pipe(out_pipes);
// …
if(pid == 0) { // 子进程
dup2(out_pipes[1], 1); // 输出管道写入端定向到子进程的标准输出
close(out_pipes[0]) // 关闭输出管道的读取端
dup2(in_pipes[0], 0); // 输入管道读取端定向到子进程的标准输入
close(in_pipes[1]) // 关闭输入管道的写入端
// 调用`execl`执行 CGI 脚本
// …
} else if (pid > 0) { // 父进程
close(in_pipes[0]) // 关闭输入管道的读取端
close(out_pipes[1]) // 关闭输出管道的写入端
// 向输入管道写入
// 从输出管道读取 CGI 脚本执行结果
// …
close(in_pipes[1])
close(out_pipes[0])
// waitpid(pid, …)
}
更进一步(可选)
- socket 编程概略,使用select处理异步,使用更高效的epoll等
- 添加其它支持和容错处理等”