服务器应用程序中的C内存管理
我是一名学生,正在用C编写HTTP代理应用程序。我在内存管理方面遇到了问题。在我以前的所有应用程序中,我只是围绕malloc编写了一个包装器,当malloc失败时,该包装器就会中止服务器应用程序中的C内存管理,c,linux,memory-management,posix,http-proxy,C,Linux,Memory Management,Posix,Http Proxy,我是一名学生,正在用C编写HTTP代理应用程序。我在内存管理方面遇到了问题。在我以前的所有应用程序中,我只是围绕malloc编写了一个包装器,当malloc失败时,该包装器就会中止 void *xmalloc(size_t size) { void *ptr; assert(size); ptr = malloc(size); if (!ptr) abort(); return ptr; } 现在我发现这还不够,因为我只想在内存暂
void *xmalloc(size_t size)
{
void *ptr;
assert(size);
ptr = malloc(size);
if (!ptr)
abort();
return ptr;
}
现在我发现这还不够,因为我只想在内存暂时不足导致内存分配失败时拒绝客户端并继续为其他客户端提供服务。如果我不想在每次malloc调用后都用检查将代码弄得乱七八糟(我在解析代码时每个函数都有很多检查),那么处理内存管理的其他选项有哪些,哪一个最适合我,服务器应用程序处理内存管理和内存不足的常用方法是什么
从我当前的代码中考虑这个函数,它解析HTTP消息头部分的一行(xstrndup调用xmalloc):
int http\u header\u parse(http\u hdr\u table*t,const char*s)
{
常量字符*p;
常量字符*b;
字符*tmp_名称;
char*tmp_值;
int-ret=-1;
断言(t);
断言;
p=b=s;
/*字段名*/
对于(;;p++){
如果(*p==':'){
如果(p-b如果您所在的系统支持fork(),linux也支持fork(),那么您可以在其自己的进程中运行每个客户端连接。首次建立客户端连接时,您可以将主进程fork到子进程中以处理其余的请求。然后您可以中止()就像你一直做的那样,只有特定的客户端连接受到影响。这是一个典型的unix服务器模型
如果不想或不能使用fork(),则需要通过引发异常来中止请求。在C中,这可以通过在首次建立连接时使用setjump()来完成,然后在检测到内存不足时调用longjump()。这将重置执行,并将堆栈重置回调用setjump()的位置
问题是,这将泄漏到该点为止分配的所有资源(例如,其他已成功分配到内存不足的内存分配)。因此,您的内存分配程序必须跟踪每个请求的所有内存分配。调用longjump()时,setjump()然后,返回位置必须释放与中止的请求关联的所有内存
这就是apache使用池所做的。apache使用池跟踪资源分配,以便在中止或代码未释放时自动释放它们:
你也应该考虑池模型,而不是简单地包装MalCube(),这样一个客户端就不能使用系统中的所有内存。
< P>如果你想使用一个相对较低级别的语言,比如C,那么你不应该太担心添加诸如<代码>的东西(tMPa值=NULL)GOOTUT;< /代码> 2个地方。
如果你不能忍受2个额外的代码,那么可以尝试一个支持异常的语言(例如C++),然后添加Futs/Test/catch来代替。注意:我真的不喜欢C++,但是使用C++比实现你自己的“异常”更需要有意义。C中的功能和整个自动资源分配层。
另一种可能是使用它的GC\u malloc
而不是malloc
(你不需要调用free
或GC\u free
),它的
GC\u oom\u fn
函数指针(在内存不再可用时从GC\u malloc
内部调用)可以设置为特定的内存不足处理程序(这将拒绝传入的HTTP请求,可能使用longjmp
)
使用Boehm GC的主要优点是,您不再关心动态分配的数据的free
(前提是它是使用GC\u malloc
或friends分配的,例如,GC\u malloc\u atomic
用于内部没有任何指针的数据)
请注意,内存管理不是一个模块化属性。某些给定数据的活动性是一个完整的程序属性,请参阅wikipage和编程习惯用法。现代语言提供垃圾收集和异常。C没有,因此您必须努力工作。这里没有神奇的解决方案
一些提示:
创建一个会话结构,并使所有分配的内存都指向该结构。当会话中止时,始终调用清理函数。这样,即使您必须在许多地方检查故障,至少所有故障都以相同的方式处理
您甚至可以创建一个session\u allocate()
尝试在会话开始时集中所有分配。分配完所需的所有资源后,剩下的代码就不必担心失败了
当然,您可以使用alloca
,但这存在一些问题,这意味着必须小心使用。或者,您可以编写代码,以最小化和本地化malloc的使用。例如,您可以重写上述函数以本地化分配:
static size_t field_name_length(const char *s)
{
const char *p = s;
for ( ; *p != ':'; ++p) {
if (is_ctl_char(*p) || is_sep_char(*p))
return 0;
}
return (size_t) (p - s);
}
static size_t value_length(const char *s)
{
const char *p = s;
for (; *p && !is_crlf(p); p+=2) {
/* nothing */
}
return *p ? (size_t) (p - s) : 0;
}
int http_header_parse(http_hdr_table *t, const char *s)
{
const char *v;
int ret = -1;
size_t v_len = 0;
size_t f_len = field_name_length(s);
if (f_len) {
v = s + f_len + 1;
v = s + strspn(s, " \t");
v_len = value_length(s);
}
if (v_len > 0 && f_len > 0) {
/* Allocation is localised to this block */
const char *name = xstrndup(s, f_len);
const char *value = xstrndup(v, v_len);
if (name && value) {
http_hdr_table_set(t, name, value);
ret = 0;
}
xfree(value);
xfree(name);
}
return ret;
}
或者,更好的是,您可以修改http\u hdr\u table\u set
以接受指针和长度并完全避免分配。这是一个非常危险的建议。setjump
和longjump
可以用来实现类似异常的东西,但它们的级别要低得多,很难正确实现。@ugoren-I agree这个建议是有风险的,谢谢你指出,因为我没有对我的答案作太多的警告-这也是一个巨大的任务,但如果是C,setjump/longjump是我所知道的实现它的全部-现在可能有一些库可以使用,使它更容易,但这是一个很滑的斜坡-我们不妨一直使用Apache。问题是他想实现的“it”是什么?如果是例外,你是对的——这是唯一的方法。
static size_t field_name_length(const char *s)
{
const char *p = s;
for ( ; *p != ':'; ++p) {
if (is_ctl_char(*p) || is_sep_char(*p))
return 0;
}
return (size_t) (p - s);
}
static size_t value_length(const char *s)
{
const char *p = s;
for (; *p && !is_crlf(p); p+=2) {
/* nothing */
}
return *p ? (size_t) (p - s) : 0;
}
int http_header_parse(http_hdr_table *t, const char *s)
{
const char *v;
int ret = -1;
size_t v_len = 0;
size_t f_len = field_name_length(s);
if (f_len) {
v = s + f_len + 1;
v = s + strspn(s, " \t");
v_len = value_length(s);
}
if (v_len > 0 && f_len > 0) {
/* Allocation is localised to this block */
const char *name = xstrndup(s, f_len);
const char *value = xstrndup(v, v_len);
if (name && value) {
http_hdr_table_set(t, name, value);
ret = 0;
}
xfree(value);
xfree(name);
}
return ret;
}