一些我竟然不知道的系统相关知识 - 这都不知道「持续更新」
最近发现我竟然不知道或理解错误的系统知识,包含Linux系统时区设置、coredump文件命名的问题。
这都不知道
Linux系统时区设置
设置Linux时区主要有两种方法,来源与man 5 localtime
设置/etc/localtime文件链接到/usr/share/zoneinfo目录下对应时区文件
$ ll /etc/localtime lrwxrwxrwx 1 root root /etc/localtime -> /usr/share/zoneinfo/Asia/Shanghai
此为系统级别的修改,其文件格式可通过
man 5 tzfile
查看。timedatectl
命令就是对该文件进行修改从而达成修改时区的目的。设置环境变量
TZ
设置环境变量
TZ
可对上述文件设置的时区进行覆盖,一般应用于临时设置,而不作为系统级别的设置。export TZ='Asia/Shanghai'
coredump文件命名问题
一般情况下我们的软件都会打开coredump功能,在程序崩溃时将终止时的内存映像保存为文件即coredump,可以使用gdb等工具分析崩溃时程序执行的语句和运行数据。该coredump文件生成的文件名一般配置成包含可执行文件名,这样就可以从coredump文件名就可以知道是哪个程序产生的,从而方便用gdb加载可执行文件。
但在实际工作中,曾多次遇到coredump文件名中可执行文件的名变成其它名称,例如thread_pool等,而不是原本的可执行文件名。这种coredump实际使用gdb打开其实可以看到实际产生的进程名core was generated by /path/your_bin
。这个疑点一直埋藏心中。
这次频繁遇到该问题,仔细分析发现是由于日志库的线程设置了线程名称,导致如果该线程收到异常信号生成coredump时,文件命名使用该线程的名称而不是默认的可执行文件名。
设置和获取线程名称的系统调用prctl()如下:
#include <linux/prctl.h> /* Definition of PR_* constants */
#include <sys/prctl.h>
int prctl(PR_SET_NAME, char name[16]);
int prctl(PR_GET_NAME, const char name[16]);
使用ChatGPT写个测试程序来验证下。首先设置coredump的路径和pattern。
sudo sysctl -w kernel.core_pattern="/tmp/core_%e_%p_%t"
下表1为/proc/sys/kernel/core_pattern的说明符,在实际生成coredump时就会替代为对应的值。其中%e
就是会被替换成崩溃线程的线程名称。
说明符 | 替代为 |
---|---|
%c | 对核心文件大小的资源软限制(字节数;始于 Linux 2.6.24) |
%e | 可执行文件名(不含路径前缀),准确说是线程名称,线程名称默认是可执行文件名 |
%g | 遭转储进程的实际组 ID |
%h | 主机系统的名称 |
%p | 遭转储进程的进程 ID |
%s | 导致进程终止的信号编号 |
%t | 转储时间,始于 Epoch,以秒为单位 |
%u | 遭转储进程的实际用户 ID |
%% | 单个 % 字符 |
编写如下程序,在创建的线程中打印当前线程名然后设置线程名为TestThread
,之后调用raise()
触发崩溃产生coredump。
#include <iostream>
#include <pthread.h>
#include <sys/prctl.h>
#include <csignal>
#include <unistd.h>
// 线程函数
void* threadFunc(void* arg) {
char threadName[16];
prctl(PR_GET_NAME, threadName);
std::cout << "Thread name is: " << threadName << std::endl;
// 设置线程名称
prctl(PR_SET_NAME, "TestThread");
// 打印线程名称
prctl(PR_GET_NAME, threadName);
std::cout << "Thread name set to: " << threadName << std::endl;
// 故意引发崩溃
raise(SIGSEGV); // 发送 SIGSEGV 信号引发段错误
return nullptr;
}
int main() {
pthread_t thread;
// 创建线程
if (pthread_create(&thread, nullptr, threadFunc, nullptr) != 0) {
std::cerr << "Failed to create thread" << std::endl;
return 1;
}
// 等待线程完成
pthread_join(thread, nullptr);
// raise(SIGSEGV); // 发送 SIGSEGV 信号引发段错误
return 0;
}
最后编译并运行。
$ g++ test_pr_set_name.cc -o test_pr_set_name -lpthread
$ ulimit -c unlimited && ./test_pr_set_name
Thread name is: test_pr_set_nam
Thread name set to: TestThread
[1] 13480 segmentation fault (core dumped) ./test_pr_set_name
最后产生的coredump名称为core_TestThread_10757_1724462863
,可以看到%e
被替换成了设置的线程名称TestThread
。而如果将raise放在主线程中执行,则会产生core_test_pr_set_nam_18496_1724463124
名称的coredump,原因也很好理解,主线程没有设置线程名称,所以是默认的test_pr_set_nam
可执行文件名。而且也说明了线程名最长为15byte+1byte(\0)。