浏览 2881 次
锁定老帖子 主题:lighttpd 学习笔记一
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-05-04
最后修改:2009-05-05
server_init 函数初始化lighttpd 的server 数据结构,涉及到其中一些buffer,array的结构初始化工作,主要参考的数据结构buffer,array,chunkqueue等等 通过启动参数-f 读取配置文件 config_read if (!i_am_root && (geteuid() == 0 || getegid() == 0)) { /* we are setuid-root */ log_error_write(srv, __FILE__, __LINE__, "s", "Are you nuts ? Don't apply a SUID bit to this binary"); server_free(srv); return -1; } 这段代码是当lighttpd binary 被setuid(类似 chmod 4755)时,禁止运行,不明白为什么要这样处理,回头再了解一下 i_am_root 是 getuid==0的结果,即root用户 getuid会返回 0,非root会返回正整数 后面比较奇怪的地方时 if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* select limits itself * * as it is a hard limit and will lead to a segfault we add some safety * */ srv->max_fds = FD_SETSIZE - 200; } else { srv->max_fds = 4096; } 默认没有修改配置文件的情况下,linux 2.6的srv->event_handler应该是epoll,就是执行else分支,我用gdb也是这个结果,为什么max_fds最多只有4096呢?libevent里直接就是rlimit里的 rlim_cur,通常会被设置成65535 接下来比较重要的是 network_init 调用(参考network.c文件) 在network_init中也就是调用 socket,setsocketopt,bind,listen等操作,创建出一个listen socket #ifdef HAVE_FORK /* start watcher and workers */ num_childs = srv->srvconf.max_worker; if (num_childs > 0) { int child = 0; while (!child && !srv_shutdown && !graceful_shutdown) { if (num_childs > 0) { switch (fork()) { case -1: return -1; case 0: child = 1; break; default: num_childs--; break; } } else { int status; if (-1 != wait(&status)) { /** * one of our workers went away */ num_childs++; } else { switch (errno) { case EINTR: /** * if we receive a SIGHUP we have to close our logs ourself as we don't * have the mainloop who can help us here */ if (handle_sig_hup) { handle_sig_hup = 0; log_error_cycle(srv); /** * forward to all procs in the process-group * * we also send it ourself */ if (!forwarded_sig_hup) { forwarded_sig_hup = 1; kill(0, SIGHUP); } } break; default: break; } } } } /** * for the parent this is the exit-point */ if (!child) { /** * kill all children too */ if (graceful_shutdown) { kill(0, SIGINT); } else if (srv_shutdown) { kill(0, SIGTERM); } log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); return 0; } } #endif 这段代码的功能是fork出若干child进程,然后父进程投入睡眠等待子进程退出,如果有退出的子进程,父进程会马上在fork出新的子进程,就是所谓的 watcher - worker的模型,这个网上的资料也有很清楚的论述 接下来子进程(可能很多个)开始注册事件 lighttpd也是使用event-driven模型,fdevents是全局的事件处理器数据结构,实现的功能类似于libevent if (NULL == (srv->ev = fdevent_init(srv->max_fds + 1, srv->event_handler))) { log_error_write(srv, __FILE__, __LINE__, "s", "fdevent_init failed"); return -1; } fdevent_init初始化事件处理器 具体的事件选择应该已经通过读取配置文件设置好了,以epoll为例子,实际执行的是 ev->type = FDEVENT_HANDLER_LINUX_SYSEPOLL; #define SET(x) \ ev->x = fdevent_linux_sysepoll_##x; SET(free); SET(poll); SET(event_del); SET(event_add); SET(event_next_fdndx); SET(event_get_fd); SET(event_get_revent); if (-1 == (ev->epoll_fd = epoll_create(ev->maxfds))) { fprintf(stderr, "%s.%d: epoll_create failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", __FILE__, __LINE__, strerror(errno)); return -1; } if (-1 == fcntl(ev->epoll_fd, F_SETFD, FD_CLOEXEC)) { fprintf(stderr, "%s.%d: epoll_create failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", __FILE__, __LINE__, strerror(errno)); close(ev->epoll_fd); return -1; } ev->epoll_events = malloc(ev->maxfds * sizeof(*ev->epoll_events)); #define SET(x) \ ev->x = fdevent_linux_sysepoll_##x; ##的作用是字符连接即初始化函数指针 ev->free=fdevent_linux_sysepoll_free等操作 通过上面代码初始化好了事件处理器srv->ev 接下来调用 network_register_fdevents (文件network.c) int network_register_fdevents(server *srv) { size_t i; if (-1 == fdevent_reset(srv->ev)) { return -1; } /* register fdevents after reset */ for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_register(srv->ev, srv_socket->fd, network_server_handle_fdevent, srv_socket); fdevent_event_add(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); } return 0; } 此时srv->srv_sockets.used应该为1,就是刚刚建立的listen fd fdevent_register代码 int fdevent_register(fdevents *ev, int fd, fdevent_handler handler, void *ctx) { fdnode *fdn; fdn = fdnode_init(); fdn->handler = handler; fdn->fd = fd; fdn->ctx = ctx; ev->fdarray[fd] = fdn; return 0; } fdarray用于保存fd与对应回调handler及参数ctx之间的关系,将来能以O(1)的性能查找到某个fd对应的回调函数及参数 server.c最后就是一个巨大的循环 /* main-loop */ while (!srv_shutdown) { //巨大的循环代码段 } if (srv->srvconf.pid_file->used && srv->srvconf.changeroot->used == 0 && 0 == graceful_shutdown) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { log_error_write(srv, __FILE__, __LINE__, "sbds", "unlink failed for:", srv->srvconf.pid_file, errno, strerror(errno)); } } } #ifdef HAVE_SIGACTION log_error_write(srv, __FILE__, __LINE__, "sdsd", "server stopped by UID =", last_sigterm_info.si_uid, "PID =", last_sigterm_info.si_pid); #else log_error_write(srv, __FILE__, __LINE__, "s", "server stopped"); #endif /* clean-up */ log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); return 0; 由于fork会原样复制父进程的heap和stack区,并保持独立,所以fork方式的好处就是完全不需要共享任何东西,代价是内存占用量要比较多 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |