Fate
淮阴市井笑韩信,汉朝公卿忌贾生。
淮阴市井笑韩信,汉朝公卿忌贾生。
以前这里只是个小小的没有名字的广场,但我一直记得这。
看了一遍二百者也,其实有个很简单的道理,这种话这种词,只有在心里默念过无数遍,才会聊天脱口而出。
俱往矣…
I found something interesting.
-,前面加\之后,-会被转义,\不会显示出来;而有些字符,如<>前面加\,虽然也能将<>转义,但是\也会被显示出来?
这是因为 Markdown 中的转义规则不同。Markdown 使用反斜杠(\)作为转义字符,用于将特殊字符标记为普通字符。
对于一些特殊字符,如 \, *, _, [, ], (, ), {, }, #, +, -, ., ! 等,它们在 Markdown 中具有特殊的语法含义。如果您想要将它们显示为普通字符而不是特殊符号,可以在它们前面加上反斜杠进行转义。
例如,\- 表示输出一个连字符而不是列表项,\* 表示输出一个星号而不是斜体标记。这些转义符号将被解析为其后的字符,并将其显示为普通文本。
然而,对于一些字符,如 <, >,由于安全性考虑,在 Markdown 中的转义规则更严格。即使您在其前面加上反斜杠进行转义,它们仍然会被解析为 HTML 标签或特殊字符。这是为了防止潜在的 HTML 注入攻击。
所以,当您在 Markdown 中使用 \ 转义 < 或 > 字符时,您会注意到 \ 也被显示出来,以提醒读者这是一个转义字符,而不是真实的 < 或 >。
总之,Markdown 的转义规则是为了在语法简洁性和安全性之间取得平衡,以提供一种方便的方式来表示特殊字符,并避免潜在的安全风险。
- GPT3.5-Turbo
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。
__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))!
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;
}
根据编译器的不同,没有经过__declspec(dllexport)声明的函数可能不会导出到dll的导出表。
此点可为debug用。
如果修改,可能导致死锁。
// 声明函数指针类型
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
除了计算机,我真的很喜欢复古的东西。