近期闲来无事,想偶尔整理一下apache的代码,观摩其整体,还是发现原来一切都是内存在引导
其内存分配机制涉及到文件、网络、进程、线程、时间片等所有元素中。
我们分析往往不得其要领,按照一般的架构来说,是主从式架构,但是其挂钩策略我一直不得其深入,我
想要了解的也不是这个,我想抓的主要是基础,包括了文件描述符的操作,网络套接字的操作,消息队列等这些
内容。
当然一切的了解只在unix下进行,至于其他系统,我是不做具体分析了
对于文件描述符来说,可以在creat的时候返回,也可以在open的时候指定其模式为非阻塞的,这是我从unix高级
环境编程中看到,而程序中文件结构存在于apr_arch_file_io.h中
而关于缓冲区的管理则在buffer中有体现,主要有设置缓冲区以及获取缓冲区长度。
struct apr_file_t
{
apr_pool_t *pool;
int filedes;
char *fname;
apr_int32_t flags;
int eof_hit;
int is_pipe;
apr_interval_time_t timeout;
int buffered;
enum {BLK_UNKNOWN,BLK_OFF,BLK_ON}blocking;
int ungetchar;
char *buffer;
apr_size_t bufpos;
apr_size_t bufsize;
unsigned long dataRead;
int direction;
apr_off_t filePtr;
};
文件描述符的结构,主要是文件描述符以及内存池。其他的都是属性文件名、标志、结尾、是否管道、阻塞、缓冲区
、读写位置
在apr_file_open的时候,传入了回传文件结构以及文件名、标志以及内存池。
此时,首先确定了文件的flags,然后根据flags以及文件名打开了文件,默认权限为读写,之后通过fcntl设置了文件的标志,
然后是在池内分配内存。
管道的创建通过了文件描述符。
通过pipe文件内apr_file_pipe_create(apr_file_t **in, apr_file_t **out,apr_pool_t *pool),通过pipe函数生成两个描述符,然后在pool中申请两个结构in和out
之后就将描述符赋值就ok。
至于读写操作,分开位于readwrite文件中,file_read_buffered(apr_file_t *thefile,void *buf,apr_size_t *nbytes);
首先将缓冲区上锁,基于同步需要,之后判断是否字节足够,从而调用read系统调用读出字节到缓冲区中,之后移动thefile的属性记录,
然后将读出的字节拷贝到buf中退出。
apr_file_read(apr_file_t *thefile,void *buf,apr_size_t *nbytes);
如果thefile还有缓冲区,则调用file_read_buffered进行读取,否则,直接读到buf中,同时移动文件属性位置。
至于apr_file_write(apr_file_t *thefile,void *buf,apr_size_t *nbytes)
首先看thefile是否有缓冲区,然后判断文件是否足够缓冲区,偏移到指定的写位置,之后memcpy到缓冲区中结束。
apr_file_writev(apr_file_t *thefile,const struct iovec *vec,apr_size_t nvec,apr_size_t *nbytes)
判断thefile是否有足够的缓冲区
其余包括了读取一个字节等函数。
socket描述符
struct apr_socket_t
{
apr_pool_t *pool;
int socketdes;
int type;
int protocol;
apr_sockaddr_t *local_addr;
apr_sockaddr_t *remote_addr;
apr_interval_time_t timeout;
int local_port_unknown;
int local_interface_unknown;
int remote_addr_unknown;
apr_int32_t options;
apr_int32_t inherit;
sock_userdata_t *userdatas;
};
struct apr_sockaddr_t
{
apr_pool_t *pool;
char *hostname;
char *servname;
apr_port_t port;
apr_int32_t family;
apr_socklen_t salen;
int ipaddr_len;
int addr_str_len;
void *ipaddr_ptr;
apr_sockaddr_t *next;
union
{
struct sockaddr_in sin;
}sa;
};
struct sock_userdata_t
{
sock_userdata_t *next;
const char *key;
void *data;
};
apr_socket_create(apr_socket_t **new,int ofamily,int type,int protocol,apr_pool+t *cont)
先通过cont申请空间,然后创建socket句柄,并初始化结构体
apr_socket_send(apr_socket_t *sock,const char *buf,apr_size_t *len)
通过调用write写入socket描述符中,
apr_socket_recv(apr_socket_t *sock,char *buf,apr_size_t *nsize);
apr_socket_sendto(apr_socket_t *sock,apr_sockaddr_t *where,apr_int32_t flags,const char *buf,apr_size_t *len);
apr_socket_recvfrom(apr_sockaddr_t *from,apr_socket_t *sock,apr_int32_t flags,char *buf,apr_size_t *len);
有了socket的经验,或许学起epoll模型来会更加得心应手。
apr_int16_t get_epoll_event(apr_int16_t event);
主要是设置监测句柄属性并返回。
struct apr_pollset_private_t
{
int epoll_fd;
struct epoll_event *pollset;
apr_pollfd_t *result_set;
APR_RING_HEAD(pfd_query_ring_t,pfd_elem_t) query_ring;
APR_RING_HEAD(pfd_free_ring_t,pfd_elem_t) free_ring;
APR_RING_HEAD(pfd_dead_ring_t,pfd_elem_t) dead_ring;
};
struct pfd_elem_t
{
APR_RING_ENTRY(pfd_elem_t) link;
apr_pollfd_t pfd;
};
struct apr_pollset_t
{
apr_pool_t *pool;
apr_uint32_t nelts;
apr_uint32_t nalloc;
apr_uint32_t flags;
apr_file_t *wakeup_pipe[2];
apr_pollfd_t wakeup_pfd;
apr_pollset_private_t *p;
apr_pollset_provider_t *provider;
};
struct apr_pollset_provider_t
{
apr_status_t (*create)(apr_pollset_t *,apr_uint32_t,apr_pool_t *,apr_uint32_t);
apr_status_t (*add)(apr_pollset_t *,const apr_pollfd_t *);
apr_status_t (*remove)(apr_pollset_t *,const apr_pollfd_t *);
apr_status_t (*poll)(apr_pollset_t *,apr_interval_time_t ,apr_int32_t ,const apr_pollfd_t **);
apr_status_t (*cleanup)(apr_pollset_t *);
const char *name;
};
typedef enum
{
APR_NO_DESC,
APR_POLL_SOCKET,
APR_POLL_FILE,
APR_POLL_LASTDESC
}apr_datatype_e;
typedef union
{
apr_file_t *f;
apr_socket_t *s;
}apr_descriptor;
struct apr_pollfd_t
{
apr_pool_t *p;
apr_datatype_e desc_type;
apr_int16_t reqevents;
apr_int16_t rtnevents;
apr_descriptor desc;
void *client_data;
};
apr_status_t impl_pollset_create(apr_pollset_t *pollset,apr_uint32_t size,apr_pool_t *p,apr_uint32_t flags);
首先通过epoll_create创建一个描述符,然后设置描述符的标志,之后将之初始化到pollset中返回。
apr_status_t impl_pollset_add(apr_pollset_t *pollset,const apr_pollfd_t *descriptor);
获取描述符请求事件标志,然后将描述符epoll_ctl到里边,不过要设置EPOLL_CTL_ADD,而在remove中需要调用
设置为EPOLL_CTL_DEL,
分析着就不想在往下了,值得下一回解说了,这些只是基础,还有许多包括进程通讯的signals,以及管道,和日志等这些东西没有分析过,下篇我决定做hook的解说,这点就比较难了