??xml version="1.0" encoding="utf-8" standalone="yes"?>久久99精品久久只有精品,国产国产成人精品久久,欧美亚洲另类久久综合婷婷http://www.shnenglu.com/feixuwu/archive/2011/05/14/146395.htmlfeixuwufeixuwuSat, 14 May 2011 13:16:00 GMThttp://www.shnenglu.com/feixuwu/archive/2011/05/14/146395.htmlhttp://www.shnenglu.com/feixuwu/comments/146395.htmlhttp://www.shnenglu.com/feixuwu/archive/2011/05/14/146395.html#Feedback0http://www.shnenglu.com/feixuwu/comments/commentRss/146395.htmlhttp://www.shnenglu.com/feixuwu/services/trackbacks/146395.html 问题   最q游戏开始技术封了Q不q刚刚上U?个小ӞServer挂了,挂在框架代码里,一个不可能挂的地方?br />从CallStack看,是在获取数据时发送请求包的时候挂的,׃框架部分是其他部门的同事开发的Q所以查问题的时候就拉上他们了,
大家折腾?天,没有实质性的q展Q服务器q是基本上每3个小时宕Zơ。由于上层逻辑大部分都在我那,所以压力比较大Q宕机的直接原因是hashtable的一个桶的指针异常,
q个hashtable是框架代码的一个内部成员,按道理我们是无从破坏的,只有可能是多U程环境下P代器损坏D的?br />但是框架代码在这个地方确实无懈可击,所以真正的原因应该q是上层代码破坏了堆内存Q很可能是一个memcpy界D的。这毕竟是个猜想Q如何找到证据呢Q这是个问题?br />把所有代码里的memcpy览了一遍,没有发现明显问题?br />

猜测

  一般游戏中比较Ҏ出现但是不好查的问题很多时候都是脚本(luaQ导致的Q我们的脚本部分是一个同事几q前写的Q在几个产品中都使用q,按道理没q么脆弱Q不q老大q是和最初开发这个模块的部门沟通了下,
q真发现问题了,赶紧拿了新的版本更新上去。经q一天的观察Q服务器没有宕机了,OKQ问题碰巧解决了,背了q么久的黑锅Q终于放下来了?br />

PageHeap

   假如没有y解决了这个问题,正常的思\该如何解册个问题呢Q这个时候我怀念windows了,在windows下有PageHeap来解册cd界的问题。基本思\是每次分配内存的时候,都将内存的结放在页的边~,紧接着q块内存分配一块不能写的内存,q样Q一旦写界Q就会写异常Q导致宕机。linux下没有现成的工具Q但是linux提供了mmap功能Q我们可以自己实现这样一个功能,当然Q这一切都不用自己动手了,tcmalloc已经包含?br />q个功能了,不过在文档里基本没有介绍Q我也是在阅读tcmalloc代码时看到的Q这个功能默认是关闭的,打开q个开关需要改写代码:

q个代码在debugallocation.cc里:

DEFINE_bool(malloc_page_fence,
            EnvToBool("TCMALLOC_PAGE_FENCE", false),
            "Enables putting of memory allocations at page boundaries "
            "with a guard page following the allocation (to catch buffer "
            "overruns right when they happen).");
把falseҎtrue可以了?br />惌在项目里加入PageHeap功能Q只需要链接的时候加?-ltcmalloc_debug卛_。把它加入项目中Q试着q行下,直接挂了Q?br />仔细一看,原来是项目中很多成员变量没有初始化导致的Qtcmalloc_debug会自动将new 和malloc出来的内存初始化为指定|q样Q一旦变量没有初始化Q很Ҏ暴露了?br />修改完这个问题后Q编译,再运行,q是挂,q个是mprotect的时候挂的,错误是内存不够,q怎么可能呢,其实是达C资源限制了?br />echo 128000 > /proc/sys/vm/max_map_count
把map数量限制加大,再运行,OK了!
 
  但是游戏Server启动后,发现一个问题,CPU长期处于100%Q导致登陆一个玩安很困难,gdb中断后,info threadQ发现大部分的操作都在mmap和mprotect,最开?br />怀疑我的linux版本有问题,Dq?个AP慢,写了试E序试了下,发现其实API不慢Q估计是频繁调用D的?br />所以得换种思\优化下才可以Q其实大部分情况下,我们free的时候,无需页面munmap掉,可以先cacheq来Q下ơ分配的时候,如果有,直接拿来用就可以了?br />最单的cache法是定义一个void* s_pageCache[50000]数组Q页面数相同的内存组成一个链表,挂在一个数l项下,q个很像STL的小内存处理Q我们可以将mmap出来的内存的
前面几个字节(一个指针大?用于索引下一个freePage。当然这个过E需要加锁,不能用pthread的锁Q因Z们会调用malloc{内存分配函敎ͼQ必ȝspinlockQ从linux源码里直接抄一个过来即可?br />static void*   s_pagePool[MAX_PAGE_ALLOC]={0};

malloc的时候,先从pagePool里面获取:
// 先从pagePool?br /> void* pFreePage = NULL;
 spin_lock(&s_pageHeapLock);
 assert(nPageNum < MAX_PAGE_ALLOC);
 if(s_pagePool[nPageNum])
 {
   pFreePage = s_pagePool[nPageNum];
   void* pNextFreePage = *((void**)pFreePage);
   s_pagePool[nPageNum] = pNextFreePage;
 }
 spin_unlock(&s_pageHeapLock);

free内存的时候,直接攑ֈpagePoll?
spin_lock(&s_pageHeapLock);
 assert(nPageNum < MAX_PAGE_ALLOC);
 void* pNextFree = s_pagePool[nPageNum];
 *(void**)pAddress = pNextFree;
 s_pagePool[nPageNum] = pAddress;
 
 spin_unlock(&s_pageHeapLock);

~译、运?OK了,CPUq速降下来了,I的时候不?%,而且也能辑ֈ写溢出的问题?br />


feixuwu 2011-05-14 21:16 发表评论
]]>
core和CallStackhttp://www.shnenglu.com/feixuwu/archive/2011/04/10/143871.htmlfeixuwufeixuwuSun, 10 Apr 2011 06:47:00 GMThttp://www.shnenglu.com/feixuwu/archive/2011/04/10/143871.htmlhttp://www.shnenglu.com/feixuwu/comments/143871.htmlhttp://www.shnenglu.com/feixuwu/archive/2011/04/10/143871.html#Feedback0http://www.shnenglu.com/feixuwu/comments/commentRss/143871.htmlhttp://www.shnenglu.com/feixuwu/services/trackbacks/143871.html  最q项目开始集中测试了Q服务器E序l常crashQ由于服务器一般情况下都是关闭了core的,所以好几次都只能通过杂ؕ的日志来定位问题?br>当然Q我们可以通过ulimit来打开core开养I不过q可能带来新的问题:我们的服务器E序每个core文g大概?G多,试期间如果频繁crash,没有注意及时清理Q一不小心就会把盘写满Q?br>而且core文g毕竟是和q程E序相关的,有时候找相应版本也是个麻烦事?br>
能否在程序crash的时候,callStack以及参数和局部变量都记录到日志里Q?br>q个技术其实在游戏客户端已l用了很多年了,一般游戏客Lcrash后,都会弹出一个是否发送错误的选择框,其实是发送的CallStack的日志和MiniDUmp文g?br>要想记录CallStack必然涉及到Stack的遍历,linux下的Stack遍历使用很简单,单的backtrace可以搞定,man backtrace有现成的例子,
q比windows下复杂的头疼的StackWalk好用的多?br>
解决了Stack遍历问题后,q剩下一个问题:如何在程序crash的时候得到通知执行我们自己的dump代码Q?br>在Windwos下有SEH异常来实现这个功能,而linux下可以通过使用信号在进Ecrash的时候执行自q处理代码?br>
好了Q开始写个简单代码测试下:
首先讄几个主要crash信号的处理函?br>signal(SIGSEGV, &DumpHelper::OnCrash);
signal(SIGABRT, &DumpHelper::OnCrash);
signal(SIGFPE, &DumpHelper::OnCrash);

在OnCrash里我们用前面提到的backtracepd函数Q来记录堆栈:
void* szStackFrame[100];
int nFrameCount = backtrace(szStackFrame, 100);
char** strFrameInfo = backtrace_symbols(szStackFrame, nFrameCount); 
char szDumpFileName[1024] = {0};
snprintf(szDumpFileName, sizeof(szDumpFileName), "dump_%u.log", (unsigned int)time(NULL) );
FILE* pFile = fopen(szDumpFileName, "wb");
if(!pFile) return;
for(int i = 0; i < nFrameCount; i++)
{
    fprintf(pFile, "%s\n", strFrameInfo[i]);
}
fclose(pFile);
free(strFrameInfo);

接着Q设|几个嵌套调用的函数Q?br>void fun()
{
 //assert(0);
 int* p = NULL;
 *p =3;
}

void fun1()
{
 fun();
}

void fun2()
{
 fun1();
}

void fun3()
{
 fun2();
}

最后,我们在main函数里执行fun3,注意~译的时候带?rdynamic 选项?span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px">

q行下,果然可以打印基本的堆栈,不过马上Q发C新的问题Q这个堆栈信息也太简陋了Q只有调用函数的名字Q其余的参数、局部变量完全没有,
q个和gdb能看到的callStack差距也太大了?br>解决q个问题最单的办法是用gdb来打印堆?在这里,gdb和其他程序有区别Q如果你试图通过 echo "bt"|gdb -p XXX>a.txt来获得堆栈,那将会非常失望,
Ҏ不v作用Qgoogle了下Q基本没什么解军_法?br>不过gdb 可以从文件读入指令,例如 gdb XXX<cmddataQ这l了我们ZQ?br>system("echo  \"bt full|gcore\">testcmd");
  char dbx[160]={0};
     sprintf(dbx, "gdb -p %d ./main<testcmd >gdbdump_%d.log", getpid(), getpid() );
  system(dbx);

试q行Q发现可以打印详l的堆栈Q不q,要求机器上有gdb.
上面的命令还dump了一个core文gQ不q这个core文g的堆栈信息是错误的,我不知道Z么。。。?br>
多线E环境下使用上述办法Q只能输Z个线E的堆栈Q需要先获取U程数目Q然后逐个U程打印堆栈?br>
最后,Z避免影响正常的coredump,要在OnCrash的处理函数里信L处理函数讄为默认?br>如果我一定要有core呢,setrlimit吧,Lcore限制卛_?/span>

feixuwu 2011-04-10 14:47 发表评论
]]>
GCC目~译速度优化http://www.shnenglu.com/feixuwu/archive/2011/03/19/142210.htmlfeixuwufeixuwuSat, 19 Mar 2011 08:39:00 GMThttp://www.shnenglu.com/feixuwu/archive/2011/03/19/142210.htmlhttp://www.shnenglu.com/feixuwu/comments/142210.htmlhttp://www.shnenglu.com/feixuwu/archive/2011/03/19/142210.html#Feedback5http://www.shnenglu.com/feixuwu/comments/commentRss/142210.htmlhttp://www.shnenglu.com/feixuwu/services/trackbacks/142210.html执行文gQ基本不考虑~译成动态库Q所有代码的头文件依赖也是一团糟Q随着目的增大,~译速度来慢Q到后来~译一个项?q程同时~译都需?0来分钟?br> 
  其实分析下可以发玎ͼ主要的编译速度损耗在头文件上Q尤其是模板相关的头文g。VC有一个预~译头文件技术,常用的公共头文件放在一P预先~译成pch文gQ这?br>可以加快~译速度。gcc到底有没有类似技术呢Q打开gcc的手册搜索了precompiledQ发现还真有相关介绍Q用方法也很简单?br> 
主要是以下步骤:
  1、在目下徏立一?stdafx.h的文Ӟ包含了大部分公共头文件。在每个cpp最开始都#include "stdafx.h"。cpp文g包含了这个预~译头文件后Q就可以原来和
stdafx .h 里头文g重复的内容删除了Q尤其是模板相关的头文gQ另外,非PCH的头文g里尽量少包含其他头文件?nbsp;    
  2、修改makefile文g, 加入OBJ?gch的依?用一个简单的目做示例,一看就明白
   
TARGET=TimerTest
PCH=stdafx.h.gch
PCH_H=stdafx.h
OBJ=stdafx.o TimerManager.o TimerTest.o

%.o:%.cpp
    g++ -Wall -c -g $^ -o $@

$(TARGET):$(OBJ)
    g++ -g  $^ -o $@


pch.d:stdafx.cpp
    g++ -g -MM stdafx.cpp |sed 's/stdafx.o/stdafx.h.gch/'>$@

-include pch.d

$(OBJ):$(PCH)
$(PCH):
    g++ $(PCH_H)

clean:
    rm -f $(OBJ) $(PCH)

    完成以上内容后,make clean,再重新编译,初步估计只需?分钟Q!  整整优化?-5倍?br>      



feixuwu 2011-03-19 16:39 发表评论
]]>
定时器的实现http://www.shnenglu.com/feixuwu/archive/2011/03/13/141744.htmlfeixuwufeixuwuSun, 13 Mar 2011 14:06:00 GMThttp://www.shnenglu.com/feixuwu/archive/2011/03/13/141744.htmlhttp://www.shnenglu.com/feixuwu/comments/141744.htmlhttp://www.shnenglu.com/feixuwu/archive/2011/03/13/141744.html#Feedback0http://www.shnenglu.com/feixuwu/comments/commentRss/141744.htmlhttp://www.shnenglu.com/feixuwu/services/trackbacks/141744.html没有定时器,所有需要定时的dQ都只能dcMOnUpdate的函敎ͼ在主循环的时候执行。定旉求少的时候,看不出明昄问题Q但是一旦这U需求多了,其是很多内部对象有定时需求的时候,
q个问题比较明显了Q写好了OnUpdate后,q要建立一条从d@环MainLoop到自wOnUpdate的调用链?br> 
  事g其实是一个广播和订阅的关p,Delegate是实现q样一套机制的利器Q目前Delegate的实C要有2U,一U是CodeProject上的一个FastDelegate实现Q另外一个比较典型的实现是boost?br>实现了,无论采取哪种实现ҎQ实现难度都不算太大?br>  Server当前框架对定时器无Q何支持,只有一个DoMainLoop的函数可以派生来q行自己的定旉辑?br>  我原来都是用的ACE装的组Ӟ用了一D|间也没发现明N题,不过ACE的定时器不太适合在这个新目用,主要原因有如下几点:
  1、ACE库太大了Q不想仅仅ؓ了定时器引入一个这么庞大的库?br>  2、ACE的定时器需要额外启动一个定时器U程Q定时Q务是在定时器U程跑的Q而我们的目逻辑其实是在单个U程q行的,如果直接采用ACE定时器,会给逻辑带来额外的复杂度。由于整个逻辑U程的框架是公共模块Q手头也没有代码Q所以将定时器线E的d发送到主逻辑U程q行也是不可行的?br>  3、ACE的定时器有很多种QTIMER_QUEUE、TIMER_WHELL、TIMER_HEAP{,个h感觉q些定时器的插入、取消操作都比较耗时Q加以改装放CU程run的带价将会很大?br>
其实linux内核有一个比较高性能的定时器Q代码在kernel/Timer.c里, 2.6内核的定时器代码更是z?br>linux的定时Q务都是以jiffie 为单位的Qlinux所有定时Q务分?个阶梯,
struct tvec {
    struct list_head vec[TVN_SIZE];
};

struct tvec_root {
    struct list_head vec[TVR_SIZE];
};

struct tvec_base {
    spinlock_t lock;
    struct timer_list *running_timer;
    unsigned long timer_jiffies;
    struct tvec_root tv1;
    struct tvec tv2;
    struct tvec tv3;
    struct tvec tv4;
    struct tvec tv5;
} ____cacheline_aligned;

对一个新的定时Q务,处理Ҏ如下:
static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
{
    unsigned long expires = timer->expires;
    unsigned long idx = expires - base->timer_jiffies;
    struct list_head *vec;

    if (idx < TVR_SIZE) {
        int i = expires & TVR_MASK;
        vec = base->tv1.vec + i;
    } else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
        int i = (expires >> TVR_BITS) & TVN_MASK;
        vec = base->tv2.vec + i;
    } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
        int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
        vec = base->tv3.vec + i;
    } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
        int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
        vec = base->tv4.vec + i;
    } else if ((signed long) idx < 0) {
        /*
         * Can happen if you add a timer with expires == jiffies,
         * or you set a timer to go off in the past
         */
        vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);
    } else {
        int i;
        /* If the timeout is larger than 0xffffffff on 64-bit
         * architectures then we use the maximum timeout:
         */
        if (idx > 0xffffffffUL) {
            idx = 0xffffffffUL;
            expires = idx + base->timer_jiffies;
        }
        i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
        vec = base->tv5.vec + i;
    }
    /*
     * Timers are FIFO:
     */
    list_add_tail(&timer->entry, vec);
}
从上可以看到Linux对定时器的处理:对即在TVR_SIZE 个jiffies内到辄定时dQ将它挂到第一ltv1 下,具体是挂到expires & TVR_MASK 对应的列表上厅R?br>同一个jiffies到达的定时器是挂在同一个链表的?br>同理Q挂到第二个l的?到期旉于 1 << (TVR_BITS + TVN_BITS) jiffies的?br>挂到W三个组的是 到期旉于1 << (TVR_BITS + 2 * TVN_BITS) jiffies的?br>挂到W四个组的是 到期旉于 1 << (TVR_BITS + 3 * TVN_BITS) jiffies的?br>过1 << (TVR_BITS + 3 * TVN_BITS) 的挂到第五组?br>q样Q所有到期的d都会在第一l。Q何时刻都可以直接通过当前jiffies&TVR_SIZE 来找到需要运行的定时器Q务列表,定时器的插入效率是O(1)?br>
下面是定时器的运行代码:
static int cascade(struct tvec_base *base, struct tvec *tv, int index)
{
    /* cascade all the timers from tv up one level */
    struct timer_list *timer, *tmp;
    struct list_head tv_list;

    list_replace_init(tv->vec + index, &tv_list);

    /*
     * We are removing _all_ timers from the list, so we
     * don't have to detach them individually.
     */
    list_for_each_entry_safe(timer, tmp, &tv_list, entry) {
        BUG_ON(tbase_get_base(timer->base) != base);
        internal_add_timer(base, timer);
    }

    return index;
}

#define INDEX(N) ((base->timer_jiffies >> (TVR_BITS + (N) * TVN_BITS)) & TVN_MASK)

/**
 * __run_timers - run all expired timers (if any) on this CPU.
 * @base: the timer vector to be processed.
 *
 * This function cascades all vectors and executes all expired timer
 * vectors.
 */
static inline void __run_timers(struct tvec_base *base)
{
    struct timer_list *timer;

    spin_lock_irq(&base->lock);
    while (time_after_eq(jiffies, base->timer_jiffies)) {
        struct list_head work_list;
        struct list_head *head = &work_list;
        int index = base->timer_jiffies & TVR_MASK;

        /*
         * Cascade timers:
         */
        if (!index &&
            (!cascade(base, &base->tv2, INDEX(0))) &&
                (!cascade(base, &base->tv3, INDEX(1))) &&
                    !cascade(base, &base->tv4, INDEX(2)))
            cascade(base, &base->tv5, INDEX(3));
        ++base->timer_jiffies;
        list_replace_init(base->tv1.vec + index, &work_list);
        while (!list_empty(head)) {
            void (*fn)(unsigned long);
            unsigned long data;

            timer = list_first_entry(head, struct timer_list,entry);
            fn = timer->function;
            data = timer->data;

            timer_stats_account_timer(timer);

            set_running_timer(base, timer);
            detach_timer(timer, 1);
            spin_unlock_irq(&base->lock);
            {
                int preempt_count = preempt_count();
                fn(data);
                if (preempt_count != preempt_count()) {
                    printk(KERN_ERR "huh, entered %p "
                           "with preempt_count %08x, exited"
                           " with %08x?\n",
                           fn, preempt_count,
                           preempt_count());
                    BUG();
                }
            }
            spin_lock_irq(&base->lock);
        }
    }
    set_running_timer(base, NULL);
    spin_unlock_irq(&base->lock);
}
当第一l运行完一轮后Q需要将tv2的一l新的定时Q务加到第一l。这好比时钟的指针Q秒针运行一圈后Q分针步q一|后箋的调整都是类伹{?
cascade 是负责下一l的定时dd到前面的d阶梯。只有当W一轮的定时d全部q行完毕后,才会需要从W二轮调入新的Q务,只有W二U别的Q务都调入完毕后,才需要从W三轮的定时d调入新的dQ?br> if (!index &&
            (!cascade(base, &base->tv2, INDEX(0))) &&
                (!cascade(base, &base->tv3, INDEX(1))) &&
                    !cascade(base, &base->tv4, INDEX(2)))
            cascade(base, &base->tv5, INDEX(3));

q就是负责调整的代码Q相当的z?br>参照上述代码实现一个定时器后,加入4000个定时Q务:
    for(int i = 1; i < 4000; i++)
    {
        g_TimerHandle[i] = g_timerManager.setTimer(&tmpSink1, i, i*10, "ss");
    }
?0毫秒?000*10毫秒,q行后,试下性能Q?br>函数?nbsp;                                   执行ơ数    最时?nbsp;    q_旉       最大时?br>TimerManager::runTimer    2170566        10              10               3046   
可以看到Q除了个别时间是因ؓU程切换D数据比较大外,q_每次q行runTimer的时间是10微秒?br>q个旉q包括每个定时器的执行消耗,效率q是不错的?br>

feixuwu 2011-03-13 22:06 发表评论
]]>
Ogre初体?/title><link>http://www.shnenglu.com/feixuwu/archive/2010/09/25/127669.html</link><dc:creator>feixuwu</dc:creator><author>feixuwu</author><pubDate>Sat, 25 Sep 2010 13:44:00 GMT</pubDate><guid>http://www.shnenglu.com/feixuwu/archive/2010/09/25/127669.html</guid><wfw:comment>http://www.shnenglu.com/feixuwu/comments/127669.html</wfw:comment><comments>http://www.shnenglu.com/feixuwu/archive/2010/09/25/127669.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.shnenglu.com/feixuwu/comments/commentRss/127669.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/feixuwu/services/trackbacks/127669.html</trackback:ping><description><![CDATA[  最q游戏又要封了Q工作比较紧张,晚上下班了比较篏Q回家懒得写代码了,不过Z倒是l箋完成了对 新剑侠情~(和月׃说的资源格式相同Q的资源逆向。完成了资源逆向后,H然兴致来了Q写了个单的地图查看器,到目前ؓ止,一切运行正常。后来做了个单的DemoQ实C基本的寻路和技能动L放,其实新剑侠情~原本的技能效果以今天的眼光看h也还可以Q即便如此,我还是集成了hge的粒子系l进去,试了下效果,q是挺奇怪的?br>做完了这些之后,本想为我的PSP山寨一个新剑侠情缘。不料后来连l加了好几天班,加了几天班之后,Z懒了Q山寨游戏的事情也就无疾而终了?br>前面写过几篇逆向工程的文章,前几天翻出来看了下,感觉像是另一个h写的天书Q我自己看自q文章且如此Q别人就更不用说了,其实对大部分言Q关心的只是逆向的成果。对新剑侠情~的资源和相x染感兴趣的朋友可以单独Email我?<br>  开始阅读Ogre代码正是在这百无聊赖的状态下开始的QOgre推出来很多年了,貌似05q就听说朋友说vq这个项目,不过我一向是专注服务端开发,对客L开发经验不是很多,?D领域完全是的新手了Q所以一直也没仔l研I。这几天拿v原来下蝲的一个版本,单读了下代码?br>Ogre的结构还是很清晰的,和手册上说的一P主要是那几个对象,Demo大部分也很简单,代码量不多,看v来很振奋人心?br>但是Ҏq样的新手来_首先想了解的当然是渲染流E?Ogre的渲染流E确实会?D新手不适应Q它是从RenderTarget开始的Q一个RenderTarget可以有几个ViewPortQ每个ViewPort都有一个独立的摄像机,q可以实现同屏幕多个渲染?br>通过ViewPort对象的update调用<br> mCamera->_renderScene(this, mShowOverlays); <br>来执行场景渲染,而场景渲染里Q最重要的要_findVisibleObjects了,<br>q个函数可见的物体d到渲染队列里Q这个函数非常的l,里面q用CVistor,_不好Ҏ被绕晕,好在我挺住了Q熬q来了?br>熟悉了大致的渲染程后,我觉得该写点东西来实战了?br>3D教程的开始一般会教大家画三角形,所以我也想用OgreM三角形玩玩,<br>一开始,我也想从像那些Demo一样从ExampleApplicationl承Q不q我发现q样启动太慢了,而且我不需要加载那么多的材质,<br>所以自己手动Configure了,代码如下:<br>Ogre::LogManager* pLogManager = new Ogre::LogManager;<br>    Ogre::Log* pLog = pLogManager->createLog("ogreLearn1.log");<br>    pLog->setDebugOutputEnabled(true);<br><br>    Ogre::Root* pRootObject = new Ogre::Root;<br>    pRootObject->loadPlugin("RenderSystem_Direct3D9_d.dll");<br>    pRootObject->loadPlugin("Plugin_OctreeSceneManager_d.dll");<br>    <br>    Ogre::RenderSystem* pRenderSystem = pRootObject->getRenderSystemByName("Direct3D9 Rendering Subsystem");<br>    pRenderSystem->setConfigOption("Full Screen", "False");<br>    pRootObject->setRenderSystem(pRenderSystem);    <br>    Ogre::RenderWindow* pRenderWindow = pRootObject->initialise(true); <br><br>~译试了下Q可以正常运行,不过发现屏幕是花的,我还没有创徏场景呢,l箋d摄像机和ViewPort以及场景<br>// 创徏场景和摄像机以及ViewPort<br>    Ogre::SceneManager* pSceneManager = pRootObject->createSceneManager(Ogre::ST_GENERIC, "OgreLearn1");<br>    Ogre::Camera* pCamera = pSceneManager->createCamera("MainCamara");<br>    pCamera->setPosition(0.0, 0.0, -20.0);<br>    pCamera->lookAt(0, 0, 0);<br>    pCamera->setNearClipDistance(2);<br><br>    Ogre::Viewport* pViewPort = pRenderWindow->addViewport(pCamera);<br>    pViewPort->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 1.0f) );<br>    pCamera->setAspectRatio(pViewPort->getActualWidth()/pViewPort->getActualHeight() ); <br><br>最后加上pRootObject->startRendering(); <br> ~译q行Q一切正常,屏幕颜色也变成了惌的黑Ԍ恩,下一步该d三角形了Q我不太喜欢用OgreManualObjectQ一堆的J琐操作。这里用自定义的Mesh来绘?角Ş?br>pSceneManager->setAmbientLight(Ogre::ColourValue(0.2, 0.2, 0.2) );<br>        Ogre::MeshPtr pMeshData = Ogre::MeshManager::getSingleton().createManual("Learn", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);<br>        Ogre::SubMesh* pSubMesh = pMeshData->createSubMesh();<br>        pSubMesh->useSharedVertices = false;<br>        pSubMesh->vertexData = new Ogre::VertexData;<br>        pSubMesh->vertexData->vertexStart = 0;<br>        pSubMesh->vertexData->vertexCount = 3;<br><br> 先设|了环境?其实没啥用,我后面会止)Q然后创Z一个自定义的Mesh,<br>紧接着的是创徏一个SubMeshQ要知道Ogre中最的|格是SubMesh,创徏好SubMesh后,要填充网格结构了Q?br>创徏了一个VertexData,讄点数目?Q也是一个三角ŞQ,下面该定义顶Ҏ式了Q?br>Ogre::VertexDeclaration* pDecle = pSubMesh->vertexData->vertexDeclaration;<br>        size_t sOffset = 0;<br>        pDecle->addElement(0, sOffset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);<br>        sOffset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);<br>        pDecle->addElement(0, sOffset, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE);<br>        sOffset += Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR); <br><br>上述代码定义了顶Ҏ式,只有基本的坐标和颜色?br>下一步将是申h存,填充点l构?br>Ogre::HardwareVertexBufferSharedPtr vBuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(sOffset, 3, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);<br>        float* pReal = static_cast<float*>(vBuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));<br>        Ogre::RGBA* pColor = NULL;<br><br>        *pReal++ = -2.0f;<br>        *pReal++ = 0.0f;<br>        *pReal++ = 0.0f;<br>        pColor = (Ogre::RGBA*)pReal;<br>        pRenderSystem->convertColourValue(Ogre::ColourValue(1.0f, 0.0, 0, 0.0f), pColor);<br>        pReal = (float*)(pColor+1);<br>        <br>        *pReal++ = 0.0f;<br>        *pReal++ = 2.0f;<br>        *pReal++ = 0.0f;<br>        pColor = (Ogre::RGBA*)pReal;<br>        pRenderSystem->convertColourValue(Ogre::ColourValue(0.0f, 0, 1.0, 1.0f), pColor);<br>        pReal = (float*)(pColor+1);<br><br><br>        *pReal++ = 2.0f;<br>        *pReal++ = 0.0f;<br>        *pReal++ = 0.0f;<br>        pColor = (Ogre::RGBA*)pReal;<br>        pRenderSystem->convertColourValue(Ogre::ColourValue(1.0f, 0, 0, 1.0f), pColor);<br>        pReal = (float*)(pColor+1);<br>        vBuf->unlock();<br>        pSubMesh->vertexData->vertexBufferBinding->setBinding(0, vBuf);<br>        <br>        pMeshData->load();<br>        pMeshData->_setBounds(Ogre::AxisAlignedBox(-2, 0, -1, 2, 2, 1) ); <br>填充点后,讄|格包围盒,q样一个自定义的网格就创徏好了Q接下来要创Z个用该|格的实体了<br>    Ogre::Entity* pEntity = pSceneManager->createEntity("TestEntity", "Learn");<br>        pEntity->setMaterialName("BaseWhiteNoLighting");<br><br>        pSceneManager->getRootSceneNode()->createChildSceneNode()->attachObject(pEntity);<br>        pEntity->getParentNode()->setPosition(3, 0, 0);<br>        pEntity->getParentNode()->rotate(Ogre::Quaternion(1.0f, 1.0f, 0, 1.0f) ); <br><br>好了Q这样实体也创徏好了Q接下来执行渲染吧:<br>pRootObject->startRendering(); <br><br> <h2>遇到的问?/h2>   上述代码是运行正常的Q但是一开始,我执行的l果是看不到M东西Q跟t了下,发现实体每次都被摄像剪了Q才发觉自定义Mesh要自p|包围盒子,<br>讄可包围盒子?br> 讄了包围盒后,数据已经q入了D3D的渲染管道,但是q是没看C角ŞQ仔l观察,原来摄像机对着的是三角形的背面。。?br>调整摄像机后Q终于能看到一个三角Ş了,不过是白色的。。?br>从这个症状看Q应该是没有关闭光照D的,但是我明明主动调用RenderSystem关闭光照了啊Q仔l跟t了下原来是材质在捣乱,<br>默认的材质是开启了光照的,所以在渲染前的SceneManager::_setPass 的时候,开启了光照?br>q好办,d讄了关闭光照的材质"BaseWhiteNoLighting" 后,l于看到了彩色三角Ş了?br><br><br> <img src ="http://www.shnenglu.com/feixuwu/aggbug/127669.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/feixuwu/" target="_blank">feixuwu</a> 2010-09-25 21:44 <a href="http://www.shnenglu.com/feixuwu/archive/2010/09/25/127669.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>select ?epollhttp://www.shnenglu.com/feixuwu/archive/2010/07/10/119995.htmlfeixuwufeixuwuSat, 10 Jul 2010 10:40:00 GMThttp://www.shnenglu.com/feixuwu/archive/2010/07/10/119995.htmlhttp://www.shnenglu.com/feixuwu/comments/119995.htmlhttp://www.shnenglu.com/feixuwu/archive/2010/07/10/119995.html#Feedback1http://www.shnenglu.com/feixuwu/comments/commentRss/119995.htmlhttp://www.shnenglu.com/feixuwu/services/trackbacks/119995.html今天闲来无事Q翻看了下内总码,l合内核代码和大家分享下我的观点?br>

一、连接数

我本Z曄在项目中用过select和epoll,对于selectQ感触最q是linux下select最大数目限?windows 下似乎没有限?Q每个进E的select最多能处理FD_SETSIZE个FD(文g句柄)Q?br>如果要处理超q?024个句柄,只能采用多进E了?br>常见的用slect的多q程模型是这LQ?一个进E专门acceptQ成功后fd通过unix socket传递给子进E处理,父进E可以根据子q程负蝲分派。曾l用q?个父q程+4个子q程 承蝲了超q?000个的负蝲?br>q种模型在我们当时的业务q行的非常好。epoll在连接数斚w没有限制Q当然可能需要用戯用API重现讄q程的资源限制?br>

二、IO差别

1、select的实?/h2> q段可以l合linux内核代码描述了,我用的?.6.28Q其?.6的代码应该差不多吧?br>先看看select:
selectpȝ调用的代码在fs/Select.c下,
asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp,
            fd_set __user *exp, struct timeval __user *tvp)
{
    struct timespec end_time, *to = NULL;
    struct timeval tv;
    int ret;

    if (tvp) {
        if (copy_from_user(&tv, tvp, sizeof(tv)))
            return -EFAULT;

        to = &end_time;
        if (poll_select_set_timeout(to,
                tv.tv_sec + (tv.tv_usec / USEC_PER_SEC),
                (tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC))
            return -EINVAL;
    }

    ret = core_sys_select(n, inp, outp, exp, to);
    ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);

    return ret;
}
前面是从用户控g拯各个fd_set到内核空_接下来的具体工作在core_sys_select中,
core_sys_select->do_select,真正的核心内容在do_select里:
int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
{
    ktime_t expire, *to = NULL;
    struct poll_wqueues table;
    poll_table *wait;
    int retval, i, timed_out = 0;
    unsigned long slack = 0;

    rcu_read_lock();
    retval = max_select_fd(n, fds);
    rcu_read_unlock();

    if (retval < 0)
        return retval;
    n = retval;

    poll_initwait(&table);
    wait = &table.pt;
    if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
        wait = NULL;
        timed_out = 1;
    }

    if (end_time && !timed_out)
        slack = estimate_accuracy(end_time);

    retval = 0;
    for (;;) {
        unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;

        set_current_state(TASK_INTERRUPTIBLE);

        inp = fds->in; outp = fds->out; exp = fds->ex;
        rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;

        for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
            unsigned long in, out, ex, all_bits, bit = 1, mask, j;
            unsigned long res_in = 0, res_out = 0, res_ex = 0;
            const struct file_operations *f_op = NULL;
            struct file *file = NULL;

            in = *inp++; out = *outp++; ex = *exp++;
            all_bits = in | out | ex;
            if (all_bits == 0) {
                i += __NFDBITS;
                continue;
            }

            for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
                int fput_needed;
                if (i >= n)
                    break;
                if (!(bit & all_bits))
                    continue;
                file = fget_light(i, &fput_needed);
                if (file) {
                    f_op = file->f_op;
                    mask = DEFAULT_POLLMASK;
                    if (f_op && f_op->poll)
                        mask = (*f_op->poll)(file, retval ? NULL : wait);
                    fput_light(file, fput_needed);
                    if ((mask & POLLIN_SET) && (in & bit)) {
                        res_in |= bit;
                        retval++;
                    }
                    if ((mask & POLLOUT_SET) && (out & bit)) {
                        res_out |= bit;
                        retval++;
                    }
                    if ((mask & POLLEX_SET) && (ex & bit)) {
                        res_ex |= bit;
                        retval++;
                    }
                }
            }
            if (res_in)
                *rinp = res_in;
            if (res_out)
                *routp = res_out;
            if (res_ex)
                *rexp = res_ex;
            cond_resched();
        }
        wait = NULL;
        if (retval || timed_out || signal_pending(current))
            break;
        if (table.error) {
            retval = table.error;
            break;
        }

        /*
         * If this is the first loop and we have a timeout
         * given, then we convert to ktime_t and set the to
         * pointer to the expiry value.
         */
        if (end_time && !to) {
            expire = timespec_to_ktime(*end_time);
            to = &expire;
        }

        if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
            timed_out = 1;
    }
    __set_current_state(TASK_RUNNING);

    poll_freewait(&table);

    return retval;
}
上面的代码很多,其实真正关键的代码是q一?
mask = (*f_op->poll)(file, retval ? NULL : wait);
q个是调用文件系l的 poll函数Q不同的文gpȝpoll函数自然不同Q由于我们这里关注的是tcpq接Q而socketfs的注册在 net/Socket.c里?br>register_filesystem(&sock_fs_type);
socket文gpȝ的函C是在net/Socket.c里:
static const struct file_operations socket_file_ops = {
    .owner =    THIS_MODULE,
    .llseek =    no_llseek,
    .aio_read =    sock_aio_read,
    .aio_write =    sock_aio_write,
    .poll =        sock_poll,
    .unlocked_ioctl = sock_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = compat_sock_ioctl,
#endif
    .mmap =        sock_mmap,
    .open =        sock_no_open,    /* special open code to disallow open via /proc */
    .release =    sock_close,
    .fasync =    sock_fasync,
    .sendpage =    sock_sendpage,
    .splice_write = generic_splice_sendpage,
    .splice_read =    sock_splice_read,
};
从sock_poll跟随下去Q?br>最后可以到 net/ipv4/tcp.c?br>unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
q个是最l的查询函数Q?br>也就是说select 的核心功能是调用tcp文gpȝ的poll函数Q不停的查询Q如果没有想要的数据Q主动执行一ơ调度(防止一直占用cpuQ,直到有一个连接有惌的消息ؓ止?br>从这里可以看出select的执行方式基本就是不同的调用poll,直到有需要的消息为止Q如果select 处理的socket很多Q这其实Ҏ个机器的性能也是一个消耗?br>

2、epoll的实?/h2> epoll的实C码在 fs/EventPoll.c下,
׃epoll涉及到几个系l调用,q里不逐个分析了,仅仅分析几个关键点,
W一个关键点?br>static int ep_insert(struct eventpoll *ep, struct epoll_event *event,
             struct file *tfile, int fd)
q是在我们调用sys_epoll_ctl d一个被理socket的时候调用的函数Q关键的几行如下Q?br>epq.epi = epi;
    init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);

    /*
     * Attach the item to the poll hooks and get current event bits.
     * We can safely use the file* here because its usage count has
     * been increased by the caller of this function. Note that after
     * this operation completes, the poll callback can start hitting
     * the new item.
     */
    revents = tfile->f_op->poll(tfile, &epq.pt);
q里也是调用文gpȝ的poll函数Q不q这ơ初始化了一个结构,q个l构会带有一个poll函数的callback函数Qep_ptable_queue_procQ?br>在调用poll函数的时候,会执行这个callbackQ这个callback的功能就是将当前q程d?socket的等待进E上?br>static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead,
                 poll_table *pt)
{
    struct epitem *epi = ep_item_from_epqueue(pt);
    struct eppoll_entry *pwq;

    if (epi->nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache, GFP_KERNEL))) {
        init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);
        pwq->whead = whead;
        pwq->base = epi;
        add_wait_queue(whead, &pwq->wait);
        list_add_tail(&pwq->llink, &epi->pwqlist);
        epi->nwait++;
    } else {
        /* We have to signal that an error occurred */
        epi->nwait = -1;
    }

注意到参?whead 实际上是 sk->sleepQ其实就是将当前q程d到sk的等待队列里Q当该socket收到数据或者其他事件触发时Q会调用
sock_def_readable 或者sock_def_write_space 通知函数来唤醒等待进E,q?个函数都是在socket创徏的时候填充在skl构里的?br>从前面的分析来看Qepoll实是比select聪明的多、轻杄多,不用再苦哈哈的去轮询了?br>


feixuwu 2010-07-10 18:40 发表评论
]]>推荐一个跨q_内存分配?/title><link>http://www.shnenglu.com/feixuwu/archive/2010/07/10/119980.html</link><dc:creator>feixuwu</dc:creator><author>feixuwu</author><pubDate>Sat, 10 Jul 2010 09:32:00 GMT</pubDate><guid>http://www.shnenglu.com/feixuwu/archive/2010/07/10/119980.html</guid><wfw:comment>http://www.shnenglu.com/feixuwu/comments/119980.html</wfw:comment><comments>http://www.shnenglu.com/feixuwu/archive/2010/07/10/119980.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.shnenglu.com/feixuwu/comments/commentRss/119980.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/feixuwu/services/trackbacks/119980.html</trackback:ping><description><![CDATA[  昨天一个同事一大早在群里推荐了一个google project上的开源内存分配器Q?a swaped="true" target="_blank">http://code.google.com/p/google-perftools/</a>Q,据说google的很多品都用到了这个内存分配库Q而且l他试Q我们的游戏客户端集成了q个最新内存分配器后,FPS提高了将q?0帧左叻Iq可是个了不L提升Q要知道3Dl的兄弟忙了几周也没见这么大的性能提升?br><br>如果我们自己本n用的crt提供的内存分配器Q这个提升也不得什么。问题是我们内部pȝ是有一个小内存理器的Q一般来说小内存分配的算法都大同异Q现成的实现也很多,比如linux内核的slab、SGI STL的分配器、ogre自带的内存分配器Q我们自q内存分配器也和前面列丄实现差不多。让我们来看看这个项目有什么特别的吧?br><br> <h1>一、用方?/h1> 打开主页Q由于公司网l禁止SVN从外部更斎ͼ所以只能下载了打包的源代码。解压后Q看到有个doc目录Q进去,打开使用文档Q发C用方法极为简单:<br>To use TCMalloc, just link TCMalloc into your application via the "-ltcmalloc" linker flag.再看法Q也没什么特别的Q还是和slab以及SGI STL分配器类似的法?br>unix环境居然只要链接q个tcmalloc库就可以了!Q太方便了,不过我手头没有linux环境Q文档上也没提到windows环境怎么使用Q?br>打开源代码包Q有个vs2003解决ҎQ打开Q随便挑选一个测试项目,查看目属性,发现仅仅?点不同:<br>1、链接器命o行里多了<br>  "..\..\release\libtcmalloc_minimal.lib"Q就是链接的时候依赖了q个内存优化库?br>2、链接器->输入->强制W号引用 多了 __tcmalloc?br>q样可以正的使用tcmalloc库了Q测试了下,试目q行OK!<br><br> <h1>二、如何替换CRT的malloc</h1> 从前面的描述可知Q项目强制引用了__tcmallocQ?搜烦了测试代码,没发现用到_tcmalloc相关的函数和变量,q个选项应该是ؓ了防止dll被优化掉(因ؓ代码里没有什么地方用到这个dll的符??br>初看hQ链接这个库后,不会影响M现有代码:我们没有引用q个Lib库的头文Ӟ也没有用过q个dll的导出函数。那么这个dll是怎么优化应用E序性能的呢Q?br>实际调试Q果然发现问题了Q看看如下代?br>    void* pData = malloc(100);<br>00401085 6A 64            push        64h  <br>00401087 FF 15 A4 20 40 00 call        dword ptr [__imp__malloc (4020A4h)] <br>跟踪 call mallocq句Qstepq去Q发现是<br>78134D09 E9 D2 37 ED 97   jmp         `anonymous namespace'::LibcInfoWithPatchFunctions<8>::Perftools_malloc (100084E0h) <br>果然Q从q里开始,p转到libtcmalloc提供的Perftools_malloc了?br>原来是通过API挂钩来实现无~替换系l自带的malloc{crt函数的,而且q是通过大家公认的不推荐的改写函数入口指令来实现的,一般只有在游戏外挂和金p怹cȝ软g才会用到q样的挂钩技术,<br>而且金山词霸l常需要更新补丁解决不同系l兼定w题?br><br> <h1>三、性能差别原因</h1> 如前面所qͼtcmalloc实用了很hacker的办法来实现无缝的替换系l自带的内存分配函数Q本人在使用q类技术通常是用来干坏事的。。。)Q但是这也不以解释Z么它的效率比我们自己的好那么多?br>回到tcmalloc 的手册,tcmalloc除了使用常规的小内存理外,对多U程环境做了Ҏ处理Q这和我原来见到的内存分配器大有不同Q一般的内存分配器作者都会偷懒,把多U程问题扔给使用者,大多是加<br>个bool型的模板参数来表C是否是多线E环境,q美其名?可定Ӟ末了q得吹嘘下模板的优越性?br>tcmalloc是怎么做的呢? {案是每U程一个ThreadCacheQ大部分操作pȝ都会支持thread local storage 是传说中的TLS,q样可以实现每U程一个分配器了,<br>q样Q不同线E分配都是在各自的threadCache里分配的。我们的目的分配器׃是多U程环境的,所以不三七二十一Q全都加锁了Q性能自然׃了?br><br>仅仅是如此,q是不以将tcmalloc和ptmalloc2分个高下Q后者也是每个线E都有threadCache的?br>关于q个问题Qdoc里有一D说明,原文贴出来:<br>ptmalloc2 also reduces lock contention by using per-thread arenas but there is a big problem with ptmalloc2's use of per-thread arenas. In ptmalloc2 memory can never move from one arena to another. This can lead to huge amounts of wasted space.<br>大意是这LQptmalloc2 也是通过tls来降低线E锁Q但是ptmalloc2各个U程的内存是独立的,也就是说Q第一个线E申L内存Q释攄时候还是必L到第一个线E池中(不可UdQ,q样可能D大量内存费?br> <br> <h1>四、代码细?/h1> <h2>1、无~替换malloc{crt和系l分配函数?/h2>    前面提到tcmalloc会无~的替换掉原有dll中的mallocQ这意味着使用tcmalloc的项目必L MDQ多U程dllQ或者MDdQ多U程dll调试Q。tcmalloc的dll定义了一?br>static TCMallocGuard module_enter_exit_hook;<br>的静态变量,q个变量会在dll加蝲的时候先于DllMainq行Q在q个cȝ构造函敎ͼ会运行PatchWindowsFunctions来挂钩所有dll?malloc、free、new{分配函敎ͼq样pC替换功能Q除此之外,<br>Z保证pȝ兼容性,挂钩API的时候还实现了智能分析指令,否则写入W一条Jmp指o的时候可能会破环后箋指o的完整性?br><br> <h2>2、LibcInfoWithPatchFunctions 和ThreadCache?/h2> LibcInfoWithPatchFunctions模板cd含tcmalloc实现的优化后的malloc{一pd函数。LibcInfoWithPatchFunctions的模板参数在我看来没什么用处,tcmalloc默认可以挂钩<br>最?0个带有malloc导出函数的库(我想肯定是够用了)。ThreadCache在每个线E都会有一个TLS对象Q?br>__thread ThreadCache* ThreadCache::threadlocal_heap_?br> <h2>3、可能的问题</h2> <br>设想下这样一个情景:假如有一个dll 在tcmalloc之前加蝲Qƈ且在分配了内存(使用crt提供的mallocQ,那么在加载tcmalloc后,tcmalloc会替换所有的free函数Q然后,在某个时刻,<br>在前面的那个dll代码中释放该内存Q这岂不是很危险。实际测试发现没有Q何问题,关键在这里:<br> span = Static::pageheap()->GetDescriptor(p);<br>    if (!span) {<br>      // span can be NULL because the pointer passed in is invalid<br>      // (not something returned by malloc or friends), or because the<br>      // pointer was allocated with some other allocator besides<br>      // tcmalloc.  The latter can happen if tcmalloc is linked in via<br>      // a dynamic library, but is not listed last on the link line.<br>      // In that case, libraries after it on the link line will<br>      // allocate with libc malloc, but free with tcmalloc's free.<br>      (*invalid_free_fn)(ptr);  // Decide how to handle the bad free request<br>      return;<br>    } <br>tcmalloc会通过span识别q个内存是否自己分配的,如果不是Qtcmalloc会调用该dll原始对应函数(q个很重?释放。这样就解决了这个棘手的问题?br> <h1>五、其?/h1> 其实tcmalloc使用的每个技术点我从前都用过Q但是我从来没想q用API挂钩来实现这样一个有的内存优化库(即惌Q也是一闪而过否定了Q?br>从tcmalloc得到灉|Q结合常用的外挂技术,可以很轻杄开发一个独立工Pq个工具可以挂蝲到指定进E进行内存优化,在我看来Q这可能可以作ؓ一个外挂辅助工h优化那些<br>内存优化做的很差D帧速很低的国游戏?br> <img src ="http://www.shnenglu.com/feixuwu/aggbug/119980.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/feixuwu/" target="_blank">feixuwu</a> 2010-07-10 17:32 <a href="http://www.shnenglu.com/feixuwu/archive/2010/07/10/119980.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.9r6.com.cn" target="_blank">޹þþۺ</a>| <a href="http://www.geyr.cn" target="_blank">ɫۺϾþۺ</a>| <a href="http://www.nyeas.cn" target="_blank">ŷƷ99þ</a>| <a href="http://www.htlon.cn" target="_blank">Ʒþþþþþ˿</a>| <a href="http://www.m28587.cn" target="_blank">ݺݾƷþþĻ</a>| <a href="http://www.setocaster.cn" target="_blank">ձŷþþþѲ</a>| <a href="http://www.ab1987.cn" target="_blank">2021˾Ʒþ</a>| <a href="http://www.cnshscj.cn" target="_blank">˾þۺӰԺ</a>| <a href="http://www.90xueyuan.cn" target="_blank">뾫ƷþѼ</a>| <a href="http://www.wooblog.cn" target="_blank">91þ㽶Ů߿</a>| <a href="http://www.xkr-bsc.cn" target="_blank">2021¾þþӾƷ</a>| <a href="http://www.021-10000.cn" target="_blank">ƷŮͬþþþõӰԺ</a>| <a href="http://www.jingzhuikang.cn" target="_blank">777þþƷһ</a>| <a href="http://www.uoip.cn" target="_blank">þþþþùƷ</a>| <a href="http://www.51yueda.cn" target="_blank">69þþƷһ</a>| <a href="http://www.i35idc.cn" target="_blank">ۺϾþþþþĻ޹ۺһ </a>| <a href="http://www.2rwx2.cn" target="_blank">ŷvaþþþ</a>| <a href="http://www.qsk2ko.cn" target="_blank">91þþþþۺ</a>| <a href="http://www.qhsn.net.cn" target="_blank">ùþþۺ</a>| <a href="http://www.rct7.cn" target="_blank">þӰ㶮</a>| <a href="http://www.alibabataba.cn" target="_blank">69Ʒþþþվ</a>| <a href="http://www.wenydz.cn" target="_blank">Ʒþùһ㽶</a>| <a href="http://www.gxyy.org.cn" target="_blank">Ʒþþþþù</a>| <a href="http://www.ouhly.cn" target="_blank">þþƷ޾Ʒ</a>| <a href="http://www.zqfhsb8.cn" target="_blank">91Ʒþþþþio</a>| <a href="http://www.hthdw.cn" target="_blank">þ97㽶</a>| <a href="http://www.wongceline.cn" target="_blank">99þþƷëƬ</a>| <a href="http://www.slchurch.cn" target="_blank">Ʒþþþþþ</a>| <a href="http://www.whruide.cn" target="_blank">ɫۺϾþۺۺ</a>| <a href="http://www.qishini.cn" target="_blank">ɫþþþۺ</a>| <a href="http://www.radowatchessale.cn" target="_blank">ɫɫݺɫۺϾþ</a>| <a href="http://www.xiaohaa.cn" target="_blank">պþ</a>| <a href="http://www.jiedidianzubiao.cn" target="_blank">þɫһ</a>| <a href="http://www.sqcn.com.cn" target="_blank">ƷžžþþƷŮͬŷպۺ </a>| <a href="http://www.hiinterface.cn" target="_blank">츾޾þĻ</a>| <a href="http://www.debtee.cn" target="_blank">ƯޱгĻþ</a>| <a href="http://www.054q.cn" target="_blank">þav߳avav紵</a>| <a href="http://www.elishe.cn" target="_blank">Ʒþþþһ</a>| <a href="http://www.lureng.cn" target="_blank">Ʒþþ99</a>| <a href="http://www.fa808.cn" target="_blank">Ʒþþþþù</a>| <a href="http://www.pqdaili.cn" target="_blank">ƷŷƬþùŷ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>