Chinaunix首页 | 论坛 | 博客
  • 博客访问: 381609
  • 博文数量: 119
  • 博客积分: 1796
  • 博客等级: 上尉
  • 技术积分: 890
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-14 10:16
个人简介

守正

文章分类
文章存档

2013年(1)

2011年(40)

2010年(78)

分类: C/C++

2010-11-04 12:22:01

    某个内存区域,需要在分配空间并成功初始化之后才可以安全地使用。在一个线程中分配内存空间并初始化,其它线程中读或者写该内存空间。
    本来的思路是,读写内存时,如果指向该内存的指针为空,则不能使用,只有非空时,才可以使用。这思路本来不错。但是,如果不注意,也会存在一些问题:例如,代码如下:



MyStruct *ptr ; //全局指针变量

//某线程中的分配内存并初始化的代码
{
    ptr = (MyStruct *) malloc( sizeof(MyStruct) );
    if( NULL==ptr )
    {
        printf("Error");
        return;
    }
    MyStructInit(ptr);
}

//释放内存的代码
{
    if( NULL==ptr )
    {
        printf("Error");
        return;
    }
    free(ptr);
}

//另一个线程中的读写内存的代码
{
    if(NULL==ptr)
    {
        printf("Error");
        return;
    }
    //读写操作
}


    这段代码问题很多,我们一一来看:
    1、首先全局指针变量定义时未初始化,其值不确定,则可能在分配内存之前就为非NULL值了。如此一来,希望通过判断该变量是否为NULL来避免对未分配内存的访问,就无法实现了。应该在定义的同时就进行初始化:

MyStruct *ptr = NULL;

     2、我们继续来看内存的分配和初始化,内存分配之前并没有对全局指针变量进行判空操作。也就是说,如果之前曾经分配过内存,由于误用或者流程错误而再度调用该段代码时,该段代码并不能发现并给出警告。而在多线程环境中,如果代码能发现一些这样的错误,很有利于代码的调试。所以,应该在内存分配之前进行一次判断。

if( NULL!=ptr )//注意,这个也很重要,这可以避免内存的重复分配导致的内存泄露
{
    printf("Error");
    return ;
 }

     3、内存分配之后,要进行分配成功与否的判断,这很重要,因为内存分配并不是一定能成功的。但是,到此为止,也不是说内存分配和初始化就没有问题了。原因在于,内存分配之后,在对内存初始化之前,直接就赋值给了ptr。在多线程环境中,这样也是不安全的,因为在内存分配成功之后和内存初始化完成之前,有可能发生调度(linux中会吗?),这时候,ptr已经被赋值了,所以,另一个线程中通过判断ptr是否为NULL来进行的保护已经失效了,此时,就有可能去访问这个为被初始化的内存空间。
    可以改为如下方式:

//某线程中的分配内存并初始化的代码
{
    if( NULL!=ptr )
    {
        printf("Error");
        return ;
    }
    MyStruct *tmpPtr = (MyStruct *) malloc( sizeof(MyStruct) );
    if( NULL==tmpPtr )
    {
        printf("Error");
        return;
    }
    MyStructInit(tmpPtr);
    ptr = tmpPtr;
}

    4、同理,释放代码时,知道先判断代码是否已经释放,也很重要,避免内存的重复释放。当然,这要求在内存释放之后,要及时将ptr置为NULL。最开始给出的代码中,就没有置为NULL这步操作。那么,修改为如下形式,是否就可以了呢?

//释放内存的代码
{
    if( NULL==ptr )
    {
        printf("Error");
        return;
    }
    free(ptr);
    ptr=NULL;
}

    仍然,不行。
    5、因为free也是非线程安全的,而且在free之后,ptr置为NULL之前,另一线程有可能会去进行读写操作(linux中线程会发生这种情况?)。那会死,因为ptr尚未置为NULL,读写操作是可以进行的,但是实际上内存又已经free了。这会导致错误。代码,可作如下修改:

//释放内存的代码
{
    if( NULL==ptr )
    {
        printf("Error");
        return;
    }
    MyStruct *tmpPtr = ptr;
    ptr=NULL;
    free(tmpPtr);
}

    但是,这样是否就完全没有问题了呢?在单cpu中,这对线程来说,应该就是安全的了。但是,对于中断来说,则不能保证安全性。因为,赋值操作,并不一定是不可中断的,即使赋值操作是不可中断的,那之后的那些操作也是可中断的。所以,如果是中断和线程或者其它中断要共享某资源,是不能用这种方法进行互斥访问的。
    另外,在多cpu系统中,上面的互斥原理,可能也不能行得通。原因就在于,通过这样一个全局变量去进行互斥保护另一资源,可能行不通。因为,判断ptr为非空后,继续操作时,并没有对资源进行锁定。
    总之,这种使用一个全局变量进行安全保护的思路,还需要进一步探讨。
阅读(799) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~