Duck 的回答(使用readlink("/proc/self/exe",...)
)让您确定正在执行的实际二进制文件的(完整路径)。从中读取第一个以 NUL 结尾的字符串/proc/self/cmdline
会给您argv[0]
.
例如,如果程序通过符号链接执行,Duck 的答案将产生实际二进制文件的路径,而/proc/self/cmdline
将产生用于执行二进制文件的原始命令(符号链接或路径)。
读取第一个字符串/proc/self/cmdline
并没有那么复杂:
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
static char *argv0_str = NULL;
const char *argv0(void)
{
int fd, result;
size_t len, max;
char *str, *tmp;
ssize_t n;
/* Already known? */
str = __sync_fetch_and_or(&argv0_str, (char *)0);
if (str)
return (const char *)str;
/* Open /proc/self/cmdline. */
do {
fd = open("/proc/self/cmdline", O_RDONLY);
} while (fd == -1 && errno == EINTR);
if (fd == -1)
return NULL;
str = NULL;
len = max = 0;
while (1) {
/* Make sure there is room in the buffer. */
if (len >= max) {
max = (len | 1023) + 1025;
tmp = realloc(str, max);
if (!tmp) {
free(str);
do {
result = close(fd);
} while (result == -1 && errno == EINTR);
errno = ENOMEM;
return NULL;
}
str = tmp;
}
n = read(fd, str + len, max - len);
if (n > (ssize_t)0)
len += n; /* Read n more chars. */
else
if (n == (ssize_t)0)
break; /* All read. */
else
if (n != (ssize_t)-1 || errno != EINTR) {
/* Error. */
const int err = (n == (ssize_t)-1) ? errno : EIO;
free(str);
do {
result = close(fd);
} while (result == -1 && errno == EINTR);
errno = err;
return NULL;
}
}
/* Close. */
do {
result = close(fd);
} while (result == -1 && errno == EINTR);
if (result == -1) {
const int err = errno;
free(str);
errno = err;
return NULL;
}
/* Nothing? */
if (len < 1) {
free(str);
errno = ENOENT;
return NULL;
}
/* Let's be paranoid: the kernel says the last char
* must be '\0'. Let's make sure. */
str[len-1] = '\0';
/* Then, we only pick the first string. */
len = strlen(str);
/* Optimize. */
max = len + 1;
tmp = realloc(str, max);
if (!tmp) {
free(str);
errno = ENOMEM;
return NULL;
}
/* Replace current argv0_str. */
if (__sync_bool_compare_and_swap(&argv0_str, (char *)0, str))
return (const char *)str;
/* Oops. Another thread already did this work. */
free(str);
/* Return what the other thread did. */
return (const char *)__sync_fetch_and_or(&argv0_str, (char *)0);
}
调用argv0()
(甚至同时从多个线程调用)为您提供argv[0]
. (我使用老式的 GCC atomic 内置插件来确保即使两个线程碰巧同时运行也能正常工作,并且不会泄漏内存等)
最后,还有第三种选择:使用线程名称。
线程名称限制为 15 (16) 个字符,并且可以使用pthread_setname_np(thread, name)
or显式设置prctl(PR_SET_NAME, name, 0L, 0L, 0L)
(需要内核 2.6.11 或更高版本),但默认为basename(argv[0])
. 可以通过pthread_getname_np()
或获得prctl(PR_GET_NAME,)
:
#include <sys/prctl.h>
static __thread char thread_name_buffer[17] = { 0 };
const char *thread_name(void)
{
/* Try obtaining the thread name.
* If this fails, we'll return a pointer to an empty string. */
if (!thread_name_buffer[0])
prctl(PR_GET_NAME, thread_name_buffer, 0L, 0L, 0L);
return (const char *)thread_name_buffer;
}
由于文档不同意(在pthread_getname_np()
和之间prctl()
)尾随 NUL 字节是否包含在 16 个字符的限制中,我认为最好使用初始化为全零的 17 字符缓冲区。(由于名称是特定于线程的,因此缓冲区也是特定于线程的。)
希望您觉得这个有帮助。