汇编网首页登录博客注册
lwbfq的学习博客
博客首页博客互动【做检测题】论坛求助

我的博客

个人首页 |  我的文章 |  我的相册 |  我的好友 |  最新访客 |  文章收藏 |  论坛提问 |  友情链接 |  给我留言  
图片载入中
  •  不去想接下来的瞬间可能发生的事,只体味捕捉那瞬间的心情,这才是幸福的人。
  • 『姓名』:不告诉你
  • 『性别』:保密『发送消息
  • 个人说明:说汇编难学,我不信。别人说的不算,我得试验一下。
  • 详细信息『加为好友』
学习动态
友情链接

[2009-02-16 15:35] 推荐博文 关于“函数malloc的实现”的深入探讨

  过完年了,又开始新的学习和生活了。去年用了不少时间把《汇编语言》学习了一下,获益匪浅!十分感谢汇编网各位老师和网友对我的帮助。现在,俺开始跟着学习C语言了。吼吼~~

  不过,在学习“函数malloc的实现”这一章的过程中,俺发现教程中提供的参考代码中,某些细节之处存在小小的纰漏,在此我就大胆一下,对本章中的学习过程进行一下探索和探讨,望各位老师和同学不吝指教。

  首先,根据对教程的理解,我先将整个malloc申请内存引发的流程简单说一下。
  1.用户通过malloc申请内存,开始遍历空间链表,查找合适大小的结点。
  2.如果找到合适大小的结点,就将该结点返回给用户。
  3.如果空间链表中没有合适大小的内存快,就调用morecore函数,在未映射堆进行内存开辟。
  4.在3中开辟了内存之后,通过调用free函数,将该结点加入到空间链表中,并继续执行malloc函数中的遍历空间链表,获得该新加入的结点,返回给用户。

  接下来,我将我实现的malloc的整体代码贴出来,然后,将我对教程中有看法的地方提出来,进行推敲和探讨。(其中,为了检测程序的运行状况,我按照教程后面的教学内容,写了一个简单的打印函数,用来查看程序调用过程中,空间链表的变化,对程序进行了一个简单的测试)

extern _heapstart;
#define NULL 0
#define E 1

/*定义空间链表的结点控制信息*/
union Node{
        struct {
                union Node *next;
                unsigned size;
        } s;
        long a;
};

typedef union Node Header;

static Header * startp; /*起始结点位置*/

static Header * prevp;  /*当前结点的前一个结点位置*/

static Header * p;      /*当前结点位置*/

/*用到的函数原型的定义*/
void * malloc(int);        /*申请内存*/

Header * morecore(unsigned int);        /*从未映射堆中申请空间*/

void free(void *);        /*释放空间(添加到空间链表中)*/

int getListSize();        /*得到现在空间链表的大小(自我测试用)*/

void myPrintf(char *,...);        /*简单的打印函数*/


void * malloc( int byteSize){

        /*将申请的字节数大小byteSize转换成头结点大小为单位的内存块的个数*/
        int nunits = (byteSize + sizeof(Header)-1) / sizeof(Header);

        if(startp == NULL){ /*当头结点为空的时候,进行初始化*/
                startp = (Header *)(_heapstart - 16); /**/
                startp->s.size = 0; /*循环链表的头结点的本体信息空间大小为0*/
                startp->s.next = startp;
        }

        for(p = startp->s.next , prevp = startp ; ; prevp = p , p = p->s.next){

                if(p->s.size >= nunits){
                        if(p->s.size <= nunits + E){
                                prevp->s.next = p->s.next;
                        }else{
                                p->s.size -= nunits + 1;
                                p += p->s.size + 1;
                                p->s.size = nunits;
                        }
                        
                        startp = prevp;
                        return (void *)(p+1);
                }

                /*链表中不存在合适的结点,去为开辟空间申请*/
                if(p == startp){
                        if((p=morecore(nunits)) == NULL){
                                return NULL;
                        }
                        p = startp;/*在后面实现的free函数中,要对startp进行设置,使继续进行的遍历能定位到新添加的结点*/
                }
        }
}

 Header * morecore(unsigned int num){
        unsigned int total ;
    unsigned int former;
        unsigned int temp;
        Header * up; /*申请到的空间地址*/

        total = (num+1)*(sizeof(Header));
        former = _heapstart; 
        temp = _heapstart + total; /*空间申请后的位置*/

        if(temp < 65535){   /*判断是否超出为映射堆空间的最大范围*/
        up = (Header *)former;
                up->s.size = num;
                _heapstart = temp;  /*设置_heapstart的值*/
                free((void *)(up+1));/*加入到空闲堆空间链表中*/
                return up; /*返回整个结点的头指针*/
        }else{
                return NULL;
        }

}

/*释放一个空间*/
void free(void * free){
         Header * freep = ((Header *)free)-1;  /*指向结点控制信息*/
         for(p = startp->s.next , prevp = startp; ;prevp = p , p = p->s.next){/*遍历链表寻找插入结点的位置*/

                 if(freep > p && freep < p->s.next){  /*freep在两个结点之间*/
                        /*将连续的空间连到一起*/
                        if(freep == p + p->s.size + 1){  /*p与freep连成新的p*/
                                p->s.size += freep->s.size + 1;
                                if(p + p->s.size + 1 == p->s.next){ /*新p与p->s.next连成新p*/
                                        p->s.size += (p->s.next)->s.size + 1;
                                        p->s.next = (p->s.next)->s.next; /*设置p指向p->s.next的下一个结点*/
                                        
                                }
                                startp = prevp; /*设置新的startp,使malloc中的下次遍历能够直接定位到这个新申请的结点*/
                        }else if(freep + freep->s.size + 1 == p->s.next){  /*只将freep与p->s.next连接成新的freep*/
                                freep->s.size += (p->s.next)->s.size +1;
                                freep->s.next = (p->s.next)->s.next;
                                p->s.next = freep;
                                startp = p;/*设置新的startp,使malloc中的下次遍历能够直接定位到这个新申请的结点*/
                        }else{ /*不能连接就直接加入到链表*/
                                freep->s.next = p->s.next;
                                p->s.next = freep;
                                startp = p;/*设置新的startp,使malloc中的下次遍历能够直接定位到这个新申请的结点*/
                        }
                        break;
                 }
                 
                 if(p >= p->s.next && freep > p){/*找到位置,freep放在链表的末尾*/
                        if(p + p->s.size + 1 == freep){  /*将链表末尾的结点P与freep连接*/
                                p->s.size += freep->s.size + 1;
                                startp = prevp;/*设置新的startp,使malloc中的下次遍历能够直接定位到这个新申请的结点*/
                        }else{
                                freep->s.next = p->s.next;
                                p->s.next = freep;
                                startp = p;/*设置新的startp,使malloc中的下次遍历能够直接定位到这个新申请的结点*/
                        }
                        
                        break;
                 }

         }

         
}

int getListSize(){
        int num = 1;
        Header * tempStartp = startp;
        Header * tempP ;
        if(tempStartp == NULL) return 0;
        myPrintf("[%d]NodeSize:%d;",0,tempStartp->s.size); /*打印起始结点的大小*/
        for(tempP = tempStartp->s.next; ;tempP = tempP->s.next){
                if( tempP == tempStartp){
                        break;
                }
                myPrintf("[%d]NodeSize:%d;",num,tempP->s.size);        /*打印其他结点的大小*/
                num ++;
                
        }
        return num;
}

/*简单的打印函数,功能还不是很完善^_^*/
void myPrintf(char  * former,...){
        char *p;
        int  intValue;
        char *cp;
        int  offsetnum;
        char cValue;
        char temp;
        int t1;
        int iv;
        int itger;
        offsetnum = 6;
                

        for(p = former;*p;p++){
                if(*p != '%'){
                        temp = *p;
                        asm mov dl,temp
                        asm mov ah,2
                        asm int 21h
                        continue;
                }
                ++p;
                if(*p =='c'){
            cValue = *(int *)(_BP+offsetnum);
            offsetnum+=2;
                        asm mov dl,(cValue)
                        asm mov ah,2
                        asm int 21h
                }else if(*p =='d'){
                        intValue = *(int *)(_BP+offsetnum);
                        offsetnum +=2;
                        itger = 1000;
                        for(t1 = 0;t1<4;t1++){
                                iv = intValue/itger;
                                intValue = intValue-iv*itger;
                                itger = itger/10;
                                asm mov dl,(iv)
                                asm add dl,30h
                                asm mov ah,2
                                asm int 21h
                        }
                        
                        asm mov dl,(intValue)
                        asm mov ah,2
                        asm int 21h
                }else if( * p =='s'){
                        for(  cp = (char *)*(int *)(_BP+offsetnum) ;*cp;cp++){
                                temp = *cp;
                                asm mov dl,temp
                                asm mov ah,2
                                asm int 21h
                        }
                        offsetnum+=2;
                }else{
                        temp = *p;
                        asm mov dl,temp
                        asm mov ah,2
                        asm int 21h
                }
        }
}
/*简单的测试*/
main(){

  int b;
  void * a;
  void * c;
  void * c1;
  b = getListSize();
  myPrintf("ListSize:%d/",b);
  a = malloc(5);
  free(a);
  b = getListSize();
  myPrintf("ListSize:%d/",b);
  a = malloc(1);
  b = getListSize();
  myPrintf("ListSize:%d/",b);
  c1 = malloc(2);
  free(a);
  b = getListSize();
  myPrintf("ListSize:%d/",b);
  c = malloc(9);
  free(c);
  b = getListSize();
  myPrintf("ListSize:%d/",b);
  free(c1);
  b = getListSize();
  myPrintf("ListSize:%d/",b);
 
}

  然后,就来说一下,我对教程中参考代码的一些看法。我们根据开始说的malloc引发的整个内存申请流程,我们发现,当我们从未映射堆中申请到合适的内存块后,我们需要用free函数将该内存块加入到空间链表中。这里就很关键了,首先,教程中没有提供我们free函数实现的参考代码,所以,教程代码的整体性我们没法猜测。不过,我们可以看到,在我们将内存块加入到空间链表时,有一个对内存块的连接操作的判断(目的应该是教程中提到的防止“内存碎片”的产生吧)。free掉的内存块和它前后的内存块存在以下几种连接可能:
1.与前后内存块都不相邻。
2.仅与前面的内存块相邻。
3.仅与后面的内存块相邻。
4.既与前面的内存块相邻,又与后面的内存块相邻。

  假设free函数中当前遍历到的结点为fp,那么,经过上面四种可能的连接方式,我们可以很清楚的看到,我们加入到空间链表的内存块要么成为了当前结点fp(其实是和fp构成了新的内存块,但是,头结点还是fp,所以,新结点我们依然可以说是fp,只是size大小发生了变化),要么就成为了fp->s.next(即当前结点fp的下一个结点)。新结点加入到空间链表之后,malloc应该继续遍历,得到该新添加的结点并返回给申请用户。最高效率的处理方式,当然是在free添加结点后,malloc下一次定位直接定位到该节点。所以,我们需要在free函数的处理过程中做一些处理,并在malloc函数中也做相应的处理,以达到这个目的。怎么做呢?从malloc中可以知道,下一次循环,p = p->s.next,也就是说,我们在所有的处理之后,malloc中的p应该定位到free函数添加的结点的前一个结点位置,才能保证malloc接下来的循环定位第一次就定位在空间链表中新添加的结点处。这个过程,相当于我们将p的起始点重新给设置了一下,为了程序上的统一,我们用startp来个给p设置新值。这样,我们必须在free函数中,根据不同的情况,重新将startp的值进行改变,使得startp指向新结点的前一个结点:
(1)当新结点与前后内存块都不相邻 startp = p
(2)当新结点仅与前面的内存块相邻 startp = prevp(prevp是p的前一个结点)
(3)当新结点仅与后面的内存块相邻 startp = p
(4)当新结点既与前面的内存块相邻,又与后面的内存块相邻 startp = prevp

  free函数中我们对startp做了处理,使得新的startp指向了新添加结点的前一个结点,那么,我们在malloc中就应该使用startp来重新设置控制循环遍历的当前结点p的值,即p = startp,这样p就指向了新添加的结点的前一个位置,下一次遍历就能直接定位到这个新添加的结点了,就能以最快的效率将该新结点内存返回给申请的用户了。

  我说到的对教程中参考代码的看法就是malloc中在调用morecore函数申请到内存后的处理,本人认为教程中的代码
if(p == startp){
                        if((p=morecore(nunits)) == NULL){
                                return NULL;
                        }
                        
                }

应该加入之前说的那条 p = startp 代码,即
if(p == startp){
                        if((p=morecore(nunits)) == NULL){
                                return NULL;
                        }
                        p = startp;
                }
这样就能和后面的free实现过程相呼应,使得整体逻辑就顺畅了。(另外,本人认为(p=morecore(nunits))==Null这行代码可以简化成morecore(nunits)==NULL,反正我们后面也没使用这里的p做什么操作。)

【结论】教程能把知识点描述那么清晰,可见汇编网技术专家确实不是盖的!但是,上面提到的代码处确实应该有一个对于循环控制变量p的定位处理的,想必是专家也有打瞌睡的时候吧。(*^__^*)...嘻嘻
评论次数(6)  |  浏览次数(2950)  |  类型(C语言学习) |  收藏此文  | 

[  272003327   发表于  2009-02-16 16:02  ]

分析得精彩!!!!!!!!!!
我也正在学习C语言

[  mywiil   发表于  2009-02-19 10:11  ]

看出前段时间我学习的不够细致了。
看了楼主的描述,整个思路清晰很多了。
谢谢。

[  maxm   发表于  2009-02-19 23:57  ]

楼主学完汇编用了多长时间?

[  游客   发表于  2009-02-20 09:40  ]

去看看他的博客中的学习作业的提交时间就知道了。

[  游客   发表于  2009-03-10 08:47  ]

喔喔 现在也是在学习这一块了,过来学习学习,顺便给LZ顶一下。。。

[  游客   发表于  2011-06-23 18:06  ]

谢谢 很有用

 
 请输入验证码  (提示:点击验证码输入框,以获取验证码