在 Linux 上,应用程序可以通过查询
/proc/self/exe
轻松获取其绝对路径。 在 FreeBSD 上,它更加复杂,因为你必须构建一个 sysctl 调用:
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
char buf[1024];
size_t cb = sizeof(buf);
sysctl(mib, 4, buf, &cb, NULL, 0);
但这仍然是完全可行的。 但我无法找到一种方法来确定 OS X 上命令行应用程序的这一点。 如果您从应用程序包中运行,则可以通过运行
[[NSBundle mainBundle] bundlePath]
来确定它,但由于命令行应用程序不在包中,因此这没有帮助。
(注意:咨询
argv[0]
不是一个合理的答案,因为,如果从符号链接启动,argv[0]
将是该符号链接,而不是所调用的可执行文件的最终路径。如果愚蠢的应用程序,argv[0]
也可能撒谎使用 exec()
调用并忘记正确初始化 argv,这是我在野外看到的。)
_NSGetExecutablePath
将返回可执行文件的完整路径(GUI 或非 GUI)。 该路径可能包含符号链接、“..
”等,但如果需要,可以使用 realpath
函数来清理这些链接。 请参阅 man
3
dyld
了解更多信息。
char path[1024];
uint32_t size = sizeof(path);
if (_NSGetExecutablePath(path, &size) == 0)
printf("executable path is %s\n", path);
else
printf("buffer too small; need size %u\n", size);
这个函数的秘密在于,Darwin 内核在创建进程时将可执行路径放在进程堆栈上的
envp
数组之后。 动态链接编辑器dyld
在初始化时获取它并保留指向它的指针。 该函数使用该指针。
我相信有更优雅的解决方案,它实际上适用于任何PID,并且还直接返回绝对路径:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <libproc.h>
int main (int argc, char* argv[])
{
int ret;
pid_t pid;
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
pid = getpid();
ret = proc_pidpath (pid, pathbuf, sizeof(pathbuf));
if ( ret <= 0 ) {
fprintf(stderr, "PID %d: proc_pidpath ();\n", pid);
fprintf(stderr, " %s\n", strerror(errno));
} else {
printf("proc %d: %s\n", pid, pathbuf);
}
return 0;
}
看起来答案是你做不到:
我正在努力实现类似的目标 lsof 的功能并收集 一大堆统计数据和信息 关于正在运行的进程。如果 lsof 没那么慢,我很乐意坚持 有了它。
如果你重新实现lsof,你会发现 它很慢,因为它正在做 很多工作。
我想这并不是真的因为 lsof 是用户模式,它更多的是它必须的 扫描任务的地址空间 寻找有支持的事物 外部寻呼机。有没有更快一点的 当我在的时候这样做的方式 内核?
不。 lsof 并不愚蠢;它正在做 它必须做什么。 如果你只是想要一个 其功能的子集,您可能 想要考虑从 lsof 源(可用)和 修剪它以满足您的 要求。
不使用出于好奇,
用于 全部?看起来它被设置为 父母的p_textvp
在p_textvp
中(并且 然后被释放??)但事实并非如此 触摸任何kern_fork
例行公事。kern_exec
。 在达尔文, proc 不是地址的根 空间;任务是。 没有 任务的“vnode”的概念 地址空间,因为它不是 最初必然由 映射一个。p_textvp
如果 exec 要填充 p_textvp,它 会迎合这样的假设 所有进程都由 vnode 支持。 那么程序员会假设它 有可能找到一条通往 vnode,从那里开始是一个短 跳转到假设 vnode的当前路径是路径 它是从那里启动的,并且 对字符串的文本处理可能 导致应用程序包名称... 所有这些都是不可能的 保证无巨额罚金。
这已经晚了,但是
[[NSBundle mainBundle] executablePath]
对于非捆绑的命令行程序来说效果很好。
我认为没有一种有保证的方法。 如果 argv[0] 是符号链接,那么您可以使用 readlink()。 如果命令是通过 $PATH 执行的,那么可以 尝试一些:search(getenv("PATH"))、getenv("_")、dladdr()
为什么不简单地
realpath(argv[0], actualpath);
?确实,realpath 有一些限制(在手册页中记录),但它可以很好地处理符号链接。在 FreeBSD 和 Linux 上测试
% ls -l foobar lrwxr-xr-x 1 bortzmeyer bortzmeyer 22 四月 29 07:39 foobar -> /tmp/get-real-name-exe % ./foobar 我的真实路径:/tmp/get-real-name-exe
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <libgen.h>
#include <string.h>
#include <sys/stat.h>
int
main(argc, argv)
int argc;
char **argv;
{
char actualpath[PATH_MAX + 1];
if (argc > 1) {
fprintf(stderr, "Usage: %s\n", argv[0]);
exit(1);
}
realpath(argv[0], actualpath);
fprintf(stdout, "My real path: %s\n", actualpath);
exit(0);
}
如果程序是通过PATH启动的,请参阅pixelbeat的解决方案。
如果有人正在寻找问题中提到的所有 UNIX 或类 UNIX 操作系统的跨平台解决方案:macOS、Linux、FreeBSD 以及其他一些操作系统。
dladdr(3)
可以通过询问 main
在哪里来提供此信息。
#define _GNU_SOURCE // ask for dladdr under Linux/glibc
#include <dlfcn.h> // dladdr, ..
#include <stdlib.h>
#include <stdio.h>
int main()
{
Dl_info info;
if (dladdr(&main, &info) == 0) {
fprintf(stderr, "error: dladdr: %s\n", dlerror());
exit(1);
}
printf("%s\n", info.dli_fname);
}
如果相关代码无法直接向
main
获取地址,则 dladdr(3)
可以询问其地址。
#define _GNU_SOURCE // ask for dladdr under Linux/glibc
#include <dlfcn.h> // dladdr, ..
#include <stdlib.h>
#include <stdio.h>
int main()
{
void* pmain = dlsym(RTLD_DEFAULT, "main");
if (!pmain) {
fprintf(stderr, "error: dlsym: %s\n", dlerror());
exit(1);
}
Dl_info info;
if (dladdr(pmain, &info) == 0) {
fprintf(stderr, "error: dladdr: %s\n", dlerror());
exit(1);
}
printf("%s\n", info.dli_fname);
}