Apache整体分析--基础

本文详细剖析了Apache APR中文件描述符及socket描述符的内存管理机制,包括内存分配、缓冲区管理、读写操作等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

近期闲来无事,想偶尔整理一下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的解说,这点就比较难了















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值