简体   繁体   English

破坏全局和静态对象的最佳方法?

[英]Best Way To Destruct Global And Static Objects?

What is the best way to end the lifetime of an object with static storage duration?结束具有静态存储持续时间的对象的生命周期的最佳方法是什么?

Current implementation finds the caller of __run_exit_handlers which then will be used to determine the __exit_funcs .当前实现找到__run_exit_handlers的调用者,然后将用于确定__exit_funcs

However this would easily fail since offset to __run_exit_handlers can change easily even in glibc with the same version.然而,这很容易失败,因为即使在具有相同版本的 glibc 中,对__run_exit_handlers偏移也很容易改变。 Another thing that could be done is to resolve the address of __run_exit_handlers first then use it in finding the caller rather than using a hardcoded call offset.可以做的另一件事是首先解析__run_exit_handlers的地址,然后使用它来查找调用者,而不是使用硬编码的调用偏移量。

Current Working Code :当前工作代码

#include <iostream>
#include <fstream>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <cstdio>

#include <execinfo.h>

struct A
{
    A(std::string pName)
        : mName(pName)
    {
        std::printf("%s %s\n", __PRETTY_FUNCTION__, mName.c_str());
    }

    ~A()
    {
        std::printf("%s %s\n", __PRETTY_FUNCTION__, mName.c_str());
    }
    volatile int i = 0;
    std::string mName;
};

A a{"a"};
A b{"b"};
A c{"c"};

class StaticDestroyer
{
public:
    StaticDestroyer()
    {
        std::ifstream maps("/proc/self/maps", std::ios::in);
        char line[1024];
        uint8_t* magic = nullptr;
        while (maps.getline(line, sizeof(line)))
        {
            char perms[4];
            uint8_t *magicbegin, *magicend;
            std::string lsv(line);
            if (std::string::npos == lsv.find("/libc-",0,6)) continue;
            std::sscanf(line, "%lx-%lx %4s", &magicbegin, &magicend, perms);
            if (perms[0]==114 && perms[2]==120)
            {
                 magic = findMagic(magicbegin, magicend);
                 break;
            }
        }

        if (magic==nullptr)
            throw std::runtime_error("magic not found!");

        mHead = *(HakaishinNode**)magic;
    }

    bool destroy(void* pTarget)
    {
        HakaishinNode *current = mHead;
        while (nullptr != current)
        {
            for (size_t i = current->idx-1 ; i>0; i--)
            {
                const  Hakaishin *const f = &current->fns[i];
                if (4 == f->type && pTarget == f->arg)
                {
                    void (*destruct) (void *arg, int status) = f->fn;
                    asm ("ror $2*8+1, %0\nxor %%fs:%c2, %0" : "=r" (destruct) : "0" (destruct), "i" (48));
                    destruct (f->arg, 1);
                    if (current->idx-1 != i) for (size_t j = i; j < current->idx ; j++) current->fns[j] = current->fns[j+1];
                    current->idx--;
                    return true;
                }
            }

            current = current->next;
        }
        return false;
    }
private:
    struct Hakaishin
    {
        long int type;
        void (*fn) (void *arg, int status);
        void *arg;
        void *dso_handle;
    };

    struct HakaishinNode
    {
        struct HakaishinNode *next;
        size_t idx;
        Hakaishin fns[32];
    };

    uint8_t* findMagic(uint8_t* magicbegin, uint8_t* magicend)
    {
        const void* const begin = magicbegin;
        int32_t ptr;
        while ((magicbegin+7) <= magicend)
        {
            if (magicbegin[0]==0x48 && (magicbegin[1]==0x8b || magicbegin[1]==0x8d))
            {
                std::memcpy(&ptr, magicbegin+3, sizeof(ptr));
                uint8_t* magicp = magicbegin+ptr+7;
                if (ptr==0x38a5c1) return magicp;
            }
            magicbegin++;
        }
        return nullptr;
    }

    HakaishinNode* mHead = nullptr;
};

A& getA()
{
    static A a{"getA"};
    return a;
}

A& getA2()
{
    static A a{"getA2"};
    return a;
}

int main()
{
    std::printf("entering...\n");
    StaticDestroyer d;
    d.destroy(&a);
    d.destroy(&b);
    auto& ga = getA();
    d.destroy(&ga);
    getA2();
    std::printf("returning...\n");
}

Output:输出:

A::A(std::string) a
A::A(std::string) b
A::A(std::string) c
entering...
A::~A() a
A::~A() b
A::A(std::string) getA
A::~A() getA
A::A(std::string) getA2
returning...
A::~A() getA2
A::~A() c

Static objects will be destructed with the termination of the program.静态对象将随着程序的终止而被破坏。

If you like to manage the resources don't make it static or use a static pointer.如果您喜欢管理资源,请不要将其设为静态或使用静态指针。 Here you can allocate and de-allocate the corresponding resources.您可以在此处分配和取消分配相应的资源。 This approach comes very close to a singleton, which is considered to be an anti pattern .这种方法非常接近单例,它被认为是一种反模式

Conclusion: If you need to manage a resource don't make it static.结论:如果您需要管理资源,请不要将其设为静态。

The need to mess around with the default behavior of life-time in such a way indicates that you have a design flaw in your application.需要以这种方式处理生命周期的默认行为表明您的应用程序存在设计缺陷。

So you should either consider restructuring your program to not use globals.因此,您应该考虑重组您的程序以不使用全局变量。 Or at least change the way how you handle the globals.或者至少改变你处理全局变量的方式。 So if you really need globals and release them earlier, then switch to unique_ptr :所以如果你真的需要全局变量并提前释放它们,那么切换到unique_ptr

#include <iostream>
#include <functional>
#include <memory>

struct A
{
    A(std::string pName)
        : mName(pName)
    {
        std::printf("%s %s\n", __PRETTY_FUNCTION__, mName.c_str());
    }

    ~A()
    {
        std::printf("%s %s\n", __PRETTY_FUNCTION__, mName.c_str());
    }
    volatile int i = 0;
    std::string mName;
};

auto a = std::make_unique<A>("a");
auto b = std::make_unique<A>("b");
auto c = std::make_unique<A>("c");

auto& getA()
{
    static auto a = std::make_unique<A>("getA");
    return a;
}

auto& getA2()
{
    static auto a = std::make_unique<A>("getA2");
    return a;
}

int main() {
    std::printf("entering...\n");

    a = nullptr;
    b = nullptr;
    c = nullptr;

    getA();
    getA2();

    getA() = nullptr;

    std::printf("returning...\n");
}

That way you can release the objects managed by the unique_ptr earlier, but they will be released on exit automatically if you don't set them to nullptr manually.这样你就可以释放之前由unique_ptr管理的对象,但如果你不手动将它们设置为nullptr ,它们将在退出时自动释放。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM