看了一遍二百者也,其实有个很简单的道理,这种话这种词,只有在心里默念过无数遍,才会聊天脱口而出。

俱往矣…
IMG_1123.jpeg

I found something interesting.

-,前面加\之后,-会被转义,\不会显示出来;而有些字符,如<>前面加\,虽然也能将<>转义,但是\也会被显示出来?
这是因为 Markdown 中的转义规则不同。Markdown 使用反斜杠(\)作为转义字符,用于将特殊字符标记为普通字符。

对于一些特殊字符,如 \, *, _, [, ], (, ), {, }, #, +, -, ., ! 等,它们在 Markdown 中具有特殊的语法含义。如果您想要将它们显示为普通字符而不是特殊符号,可以在它们前面加上反斜杠进行转义。
例如,\- 表示输出一个连字符而不是列表项,\* 表示输出一个星号而不是斜体标记。这些转义符号将被解析为其后的字符,并将其显示为普通文本。

然而,对于一些字符,如 <, >,由于安全性考虑,在 Markdown 中的转义规则更严格。即使您在其前面加上反斜杠进行转义,它们仍然会被解析为 HTML 标签或特殊字符。这是为了防止潜在的 HTML 注入攻击。
所以,当您在 Markdown 中使用 \ 转义 < 或 > 字符时,您会注意到 \ 也被显示出来,以提醒读者这是一个转义字符,而不是真实的 < 或 >。

总之,Markdown 的转义规则是为了在语法简洁性和安全性之间取得平衡,以提供一种方便的方式来表示特殊字符,并避免潜在的安全风险。
- GPT3.5-Turbo

Linux

1.__attribute__((constructor)) and __attribute__((destructor))

void __attribute__((constructor)) my_constructor()
{
    printf("加载\n");
}

void __attribute__((destructor)) my_destructor()
{
    printf("卸载\n");
}

constructor会在dll被加载的时候执行,但是要注意一点:

void* dlopen(const char* filename, int flags);
//flags: RTLD_NOW, RTLD_LAZY

RTLD_NOW的时候constructor立即被执行,RTLD_LAZY当so中函数被调用的时候才会执行constructor。

2.执行了两次destructor

__attribute__((destructor))在使用 dlclose() 显式关闭共享库句柄时会在共享库被卸载之前调用,在程序结束时,无论是否显式调用了 dlclose(),共享库句柄会被自动关闭,__attribute__((destructor)) 声明的函数也会在共享库被卸载之前被调用。
这就意味着destructor可能被执行两次

void* handle = dlopen("./libexample.so", RTLD_LAZY);
dlclose(handle);

此时若只希望执行一次,可以考虑:

bool is_destructor_called = false;

void __attribute__((destructor)) my_destructor()
{
    if (!is_destructor_called) {
        // 执行析构操作
        is_destructor_called = true;
    }
}

[WARNING]不要显式调用__attribute__((constructor))/__attribute__((destructor))!


Windows

1.BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved);

DllMain是DLL的入口点函数,当DLL被加载、卸载、线程附加或线程分离时,系统会自动调用DllMain函数。

hModule:表示当前被加载的DLL模块的句柄。

ul_reason_for_call:标识了调用DllMain的原因,它可能取以下值:
    DLL_PROCESS_ATTACH:表示DLL被加载到进程时调用。
    DLL_PROCESS_DETACH:表示DLL从进程中卸载时调用。
    DLL_THREAD_ATTACH:表示DLL被线程附加时调用。
    DLL_THREAD_DETACH:表示DLL从线程中分离时调用。

lpReserved:保留参数,一般情况下不使用,应设置为NULL。

一般进入DllMain可以首先switch:

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){
    switch (ul_reason_for_call){
        case DLL_PROCESS_ATTACH:
            // 此处处理动态链接库被进程加载的事件
            // 可以在这里进行一些初始化操作
            break;

        case DLL_PROCESS_DETACH:
            // 此处处理动态链接库被进程卸载的事件
            // 可以在这里进行一些清理操作
            break;

        case DLL_THREAD_ATTACH:
            // 此处处理动态链接库被线程加载的事件
            break;

        case DLL_THREAD_DETACH:
            // 此处处理动态链接库被线程卸载的事件
            break;
    }
    return TRUE;
}

2.__declspec(dllexport)

根据编译器的不同,没有经过__declspec(dllexport)声明的函数可能不会导出到dll的导出表。
此点可为debug用。

3.白加黑不要轻易修改DllMain()

如果修改,可能导致死锁。

// 声明函数指针类型
typedef void (*FuncPtr)();

int main()
{
    // 加载 DLL
    HINSTANCE hDll = LoadLibrary("mydll.dll");
    if (hDll == NULL)
    {
        // 处理加载 DLL 失败的情况
        return 1;
    }
    // 获取函数指针
    FuncPtr pFunc2 = (FuncPtr)GetProcAddress(hDll, "func2");
    if (pFunc2 == NULL)
    {
        // 处理获取函数指针失败的情况
        // 释放 DLL
        FreeLibrary(hDll);
        return 1;
    }
    // 调用 func2 函数
    pFunc2();
    // 释放 DLL
    FreeLibrary(hDll);

    return 0;
}

当一个线程调用 LoadLibrary 加载被注入DLL时,操作系统会自动调用被注入DLL中的 DllMain 函数,并传递 DLL_PROCESS_ATTACH 参数。在 DllMain 函数中,可能会进行一些初始化操作或创建线程等。此时,如果同时有另一个线程尝试修改或替换被注入DLL,也会触发 LoadLibrary 调用,并导致另一个 DllMain 函数的调用。
由于 DllMain 函数没有进行适当的同步处理,多个线程同时调用 DllMain 函数时,可能会发生死锁。

总是忘了怎么操作,let's encrypt网站上的教程是有问题的(对我而言)。

sudo apt install certbot
sudo certbot certonly --manual --preferred-challenges dns -d <site-domain> -d "*.<site-domain>"

即可,不要用snap的certbot,总会报奇奇怪怪的错误。
如果已经用snap安装了certbot,记得先卸载:

sudo snap remove certbot

“Most of the good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.”

- Linus Torvalds
profilePic2.JPG

I like TUI more and more.