find没有/ proc / self / exe的当前可执行文件的path

在我看来,Linux使用/ proc / self / exe很容易。 但是我想知道是否有一个方便的方式来find当前应用程序的目录在C / C + +与跨平台接口。 我已经看到一些argv [0]的项目,但它似乎并不完全可靠。

如果你曾经需要支持Mac OS X(没有/ proc /),你会怎么做? 使用#ifdefs隔离平台特定的代码(例如NSBundle)? 或尝试从argv [0],$ PATH推导可执行文件的path,以及在边缘情况下发现错误的风险?

一些操作系统特定的接口:

  • Mac OS X: _NSGetExecutablePath() ( man 3 dyld )
  • Linux: readlink /proc/self/exe
  • Solaris: getexecname()
  • FreeBSD: sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1
  • FreeBSD 如果有procfs: readlink /proc/curproc/file (FreeBSD默认没有procfs)
  • NetBSD: readlink /proc/curproc/exe
  • DragonFly BSD: readlink /proc/curproc/file
  • Windows: GetModuleFileName()hModule = NULL

便携式(但不太可靠)的方法是使用argv[0] 。 尽pipe它可以被调用程序设置为任何东西,但是按照惯例,它被设置为可执行文件的path名或使用$PATHfind的名称。

一些shell(包括bash和ksh)在执行前将环境variables“ _ ”设置为可执行文件的完整path。 在这种情况下,您可以使用getenv("_")来获取它。 然而,这是不可靠的,因为并不是所有的shell都这样做,它可以被设置为任何东西,或者是在执行程序之前不会改变它的父进程遗留下来的。

/proc/self/exe是不可移植和不可靠的。 在我的Ubuntu 12.04系统上,您必须是root才能读取/遵循符号链接。 这将使Boost示例和可能的whereami()解决scheme发布失败。

这篇文章很长,但讨论了实际的问题,并提出了实际上与validation对testing套件一起工作的代码。

find你的程序的最好方法是回溯系统使用的相同步骤。 这是通过使用argv[0]parsing文件系统根,pwd,path环境并考虑符号链接和path名规范化完成的。 这是从记忆中,但我已经成功地完成了这一点,并在各种不同的情况下进行testing。 它不能保证工作,但如果没有,你可能有更大的问题,总体上比任何其他方法讨论。 在Unix兼容的系统上有一些情况,在这种情况下argv[0]正确处理不会让你进入你的程序,而是你在一个可以authentication的破坏的环境中执行。 从1970年左右开始,所有Unix衍生系统也是相当便携的,甚至还有一些非Unix派生系统,因为它基本上依赖于libc()标准function和标准的命令行function。 它应该在Linux(所有版本),Android,Chrome操作系统,Minix,原始贝尔实验室Unix,FreeBSD,NetBSD,OpenBSD,BSD xx,SunOS,Solaris,SYSV,HPUX,Concentrix,SCO,Darwin,AIX,OS X, Nextstep等,并可能稍微修改VMS,VM / CMS,DOS / Windows,ReactOS,OS / 2等。如果一个程序直接从GUI环境启动,它应该已经将argv[0]设置为绝对path。

了解几乎所有曾经发布的Unix兼容操作系统上的每一个shell基本上都以相同的方式发现程序,并以几乎相同的方式设置操作环境(带有一些可选的附加组件)。 而任何启动程序的其他程序都应该为该程序创build相同的环境(argv,环境string等),就好像它是从shell运行的一样,还有一些可选的附加组件。 一个程序或者用户可以设置一个偏离这个约定的环境来启动其他的下属程序,但是如果是这样的话,这是一个错误,程序没有理由期望下属程序或者下属能够正常工作。

argv[0]可能值包括:

  • /path/to/executable – 绝对path
  • ../bin/executable – 相对于pwd
  • bin/executable – 相对于pwd
  • ./foo – 相对于pwd
  • executable – basename,在path中查找
  • bin//executable – 相对于pwd,非规范的
  • src/../bin/executable – 相对于pwd,非规范的,回溯
  • bin/./echoargc – 相对于pwd,非规范

你不应该看到的价值观:

  • ~/bin/executable – 在程序运行之前重写。
  • ~user/bin/executable – 在程序运行之前重写
  • alias – 在程序运行之前重写
  • $shellvariable – 在程序运行之前重写
  • *foo* – 通配符,在程序运行之前重写,不是很有用
  • ?foo? – 通配符,在你的程序运行之前重写,不是很有用

另外,这些可能包含非规范path名称和多层符号链接。 在某些情况下,同一个程序可能有多个硬链接。 例如, /bin/ls/bin/ps/bin/chmod/bin/rm等可能是硬链接到/bin/busybox

要find自己,请按照以下步骤操作:

  • 将pwd,PATH和argv [0]保存到您的程序(或初始化您的程序库),因为它们可能稍后会更改。

  • 可选:特别是对于非Unix系统,分离出来,但不要丢弃path名主机/用户/驱动器前缀部分(如果存在); 经常在冒号之前的部分或者跟在最初的“//”之后。

  • 如果argv[0]是绝对path,则以此为起点。 绝对path可能以“/”开头,但在某些非Unix系统上,可能以“\”开头,或者是驱动器号或名称前缀后跟一个冒号。

  • 否则,如果argv[0]是一个相对path(包含“/”或“\”但不以它开头,比如“../../bin/foo”,则将pwd +“/”+ argv [ 0](从程序启动时使用当前的工作目录,不是最新的)。

  • 否则,如果argv [0]是一个普通的基本名称(不是斜杠),然后将其与PATH环境variables中的每个条目依次结合,并尝试使用第一个成功。

  • 可选:否则请尝试平台特定的/proc/self/exe/proc/curproc/file (BSD)和(char *)getauxval(AT_EXECFN)dlgetname(...)如果存在)。 您甚至可以在基于argv[0]的方法之前尝试这些方法,如果这些方法可用并且您没有遇到权限问题。 在不太可能发生的情况下(当您考虑所有系统的所有版本)时,它们是存在的并且不会失败,它们可能更具权威性。

  • 可选:使用命令行参数检查传入的path名。

  • 可选:检查包装脚本显式传入的环境中的path名(如果有的话)。

  • 可选:作为最后的手段尝试环境variables“_”。 它可能完全指向一个不同的程序,例如用户shell。

  • parsing符号链接,可能有多个图层。 有无限循环的可能性,但如果它们存在,你的程序可能不会被调用。

  • 通过parsing像“/foo/../bar/”到“/ bar /”的子string来规范文件名。 请注意,如果您穿过networking挂载点,这可能会改变含义,所以封装并不总是好事。 在networking服务器上,可以使用符号链接中的“..”来遍历服务器上下文中的另一个文件的path,而不是在客户端上。 在这种情况下,您可能需要客户端上下文,所以canonicalization是可以的。 也可以将“/./”等模式转换为“/”和“//”为“/”。 在shell中, readlink --canonicalize将parsing多个符号链接并规范化名称。 大通可能会做类似的,但没有安装。 realpath()canonicalize_file_name() (如果存在)可能会有所帮助。

如果realpath()在编译时不存在,那么可以从获得许可的库发行版中借用一份副本,然后自行编译它,而不是重新发明轮子。 修复潜在的缓冲区溢出(如果要使用小于PATH_MAX的缓冲区,则传入sizeof输出缓冲区,请考虑strncpy()vs strcpy())。 只是使用重命名的私人副本而不是testing它是否存在可能更容易。 从android / darwin / bsd许可许可副本: https : //android.googlesource.com/platform/bionic/+/f077784/libc/upstream-freebsd/lib/libc/stdlib/realpath.c

请注意,多次尝试可能会成功或部分成功,并且可能不会全部指向相同的可执行文件,因此请考虑validation您的可执行文件; 但是,您可能没有读取权限 – 如果您无法阅读,请不要将其视为失败。 或者在您的可执行文件附近validation一些东西,例如您正在尝试查找的“../lib/”目录。 您可能有多个版本,打包和本地编译的版本,本地和networking版本,以及本地和USB驱动器的可移植版本等,您可能会从不同的定位方法获得两个不兼容的结果。 而“_”可能只是指向错误的程序。

使用execve的程序可以故意将argv[0]设置为与用于加载程序的实际path不兼容,并且破坏PATH,“_”,pwd等,虽然通常没有太多理由这样做。 但是如果你有脆弱的代码,忽略了你的执行环境可以用多种方式改变,包括但不限于这个(chroot,fuse文件系统,硬链接等等)用于shell命令来设置PATH,但无法导出它。

您不一定需要为非Unix系统编写代码,但是了解一些特性是个不错的主意,所以您可以编写代码,以便稍后人们可以轻松地编写代码。 请注意,某些系统(DEC VMS,DOS,URL等)可能具有驱动器名称或以冒号(例如“C:\”,“sys $ drive:[foo] bar”和“file :///富/酒吧/巴兹”。 旧的DEC VMS系统使用“[”和“]”来包围path的目录部分,尽pipe如果你的程序是在POSIX环境中编译的话,这个可能已经改变了。 某些系统(如VMS)可能具有文件版本(最后以分号分隔)。 有些系统在“// drive / path / to / file”或“user @ host:/ path / to / file”(scp命令)或“file:// hostname / path / to / file”中使用了两个连续的斜杠。 (URL)。 在某些情况下(DOS,windoze),PATH可能有不同的分隔符 – “;” vs“:”和“\”vs“/”作为path分隔符。 在csh / tsh中有“path”(用空格分隔)和“PATH”用冒号分隔,但是你的程序应该接收PATH,所以你不必担心path。 DOS和其他一些系统可以具有以驱动器前缀开始的相对path。 C:foo.exe是指C驱动器上当前目录中的foo.exe,因此您需要在C:上查找当前目录并将其用于pwd。

我的系统上的符号链接和包装的一个例子:

 /usr/bin/google-chrome is symlink to /etc/alternatives/google-chrome which is symlink to /usr/bin/google-chrome-stable which is symlink to /opt/google/chrome/google-chrome which is a bash script which runs /opt/google/chome/chrome 

请注意,用户帐单在HP上发布了一个链接,用于处理argv[0]的三个基本案例。 不过需要做一些修改:

  • 有必要重写所有的strcat()strcpy()来使用strncat()strncpy() 。 即使variables声明的长度是PATHMAX,长度为PATHMAX-1的input值加上连接string的长度也是> PATHMAX,长度为PATHMAX的input值将被终止。
  • 它需要被重写为一个库函数,而不仅仅是打印结果。
    • 它不能规范化名称(使用上面链接的实际path代码)
    • 它无法parsing符号链接(使用真实path代码)

因此,如果将HP代码和实际path代码结合起来并修复两者以抵抗缓冲区溢出,那么应该有一些能正确解释argv[0]

以下说明了在Ubuntu 12.04上调用相同程序的各种方式的argv[0]实际值。 是的,该程序被无意中命名为echoargc而不是echoargv。 这是使用脚本来完成复制,但在shell中手动完成将得到相同的结果(除非明确启用它们,否则别名在脚本中不起作用)。

 cat ~/src/echoargc.c #include <stdlib.h> #include <stdio.h> #include <unistd.h> main(int argc, char **argv) { printf(" argv[0]=\"%s\"\n", argv[0]); sleep(1); /* in case run from desktop */ } tcc -o ~/bin/echoargc ~/src/echoargc.c cd ~ /home/whitis/bin/echoargc argv[0]="/home/whitis/bin/echoargc" echoargc argv[0]="echoargc" bin/echoargc argv[0]="bin/echoargc" bin//echoargc argv[0]="bin//echoargc" bin/./echoargc argv[0]="bin/./echoargc" src/../bin/echoargc argv[0]="src/../bin/echoargc" cd ~/bin *echo* argv[0]="echoargc" e?hoargc argv[0]="echoargc" ./echoargc argv[0]="./echoargc" cd ~/src ../bin/echoargc argv[0]="../bin/echoargc" cd ~/junk ~/bin/echoargc argv[0]="/home/whitis/bin/echoargc" ~whitis/bin/echoargc argv[0]="/home/whitis/bin/echoargc" alias echoit=~/bin/echoargc echoit argv[0]="/home/whitis/bin/echoargc" echoarg=~/bin/echoargc $echoarg argv[0]="/home/whitis/bin/echoargc" ln -s ~/bin/echoargc junk1 ./junk1 argv[0]="./junk1" ln -s /home/whitis/bin/echoargc junk2 ./junk2 argv[0]="./junk2" ln -s junk1 junk3 ./junk3 argv[0]="./junk3" gnome-desktop-item-edit --create-new ~/Desktop # interactive, create desktop link, then click on it argv[0]="/home/whitis/bin/echoargc" # interactive, right click on gnome application menu, pick edit menus # add menu item for echoargc, then run it from gnome menu argv[0]="/home/whitis/bin/echoargc" cat ./testargcscript 2>&1 | sed -e 's/^/ /g' #!/bin/bash # echoargc is in ~/bin/echoargc # bin is in path shopt -s expand_aliases set -v cat ~/src/echoargc.c tcc -o ~/bin/echoargc ~/src/echoargc.c cd ~ /home/whitis/bin/echoargc echoargc bin/echoargc bin//echoargc bin/./echoargc src/../bin/echoargc cd ~/bin *echo* e?hoargc ./echoargc cd ~/src ../bin/echoargc cd ~/junk ~/bin/echoargc ~whitis/bin/echoargc alias echoit=~/bin/echoargc echoit echoarg=~/bin/echoargc $echoarg ln -s ~/bin/echoargc junk1 ./junk1 ln -s /home/whitis/bin/echoargc junk2 ./junk2 ln -s junk1 junk3 ./junk3 

这些例子说明了这篇文章中描述的技术应该适用于各种各样的情况,为什么有些步骤是必要的。

编辑:现在,打印argv [0]的程序已经更新,实际上发现自己。

 // Copyright 2015 by Mark Whitis. License=MIT style #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <limits.h> #include <assert.h> #include <string.h> #include <errno.h> // "look deep into yourself, Clarice" -- Hanibal Lector char findyourself_save_pwd[PATH_MAX]; char findyourself_save_argv0[PATH_MAX]; char findyourself_save_path[PATH_MAX]; char findyourself_path_separator='/'; char findyourself_path_separator_as_string[2]="/"; char findyourself_path_list_separator[8]=":"; // could be ":; " char findyourself_debug=0; int findyourself_initialized=0; void findyourself_init(char *argv0) { getcwd(findyourself_save_pwd, sizeof(findyourself_save_pwd)); strncpy(findyourself_save_argv0, argv0, sizeof(findyourself_save_argv0)); findyourself_save_argv0[sizeof(findyourself_save_argv0)-1]=0; strncpy(findyourself_save_path, getenv("PATH"), sizeof(findyourself_save_path)); findyourself_save_path[sizeof(findyourself_save_path)-1]=0; findyourself_initialized=1; } int find_yourself(char *result, size_t size_of_result) { char newpath[PATH_MAX+256]; char newpath2[PATH_MAX+256]; assert(findyourself_initialized); result[0]=0; if(findyourself_save_argv0[0]==findyourself_path_separator) { if(findyourself_debug) printf(" absolute path\n"); realpath(findyourself_save_argv0, newpath); if(findyourself_debug) printf(" newpath=\"%s\"\n", newpath); if(!access(newpath, F_OK)) { strncpy(result, newpath, size_of_result); result[size_of_result-1]=0; return(0); } else { perror("access failed 1"); } } else if( strchr(findyourself_save_argv0, findyourself_path_separator )) { if(findyourself_debug) printf(" relative path to pwd\n"); strncpy(newpath2, findyourself_save_pwd, sizeof(newpath2)); newpath2[sizeof(newpath2)-1]=0; strncat(newpath2, findyourself_path_separator_as_string, sizeof(newpath2)); newpath2[sizeof(newpath2)-1]=0; strncat(newpath2, findyourself_save_argv0, sizeof(newpath2)); newpath2[sizeof(newpath2)-1]=0; realpath(newpath2, newpath); if(findyourself_debug) printf(" newpath=\"%s\"\n", newpath); if(!access(newpath, F_OK)) { strncpy(result, newpath, size_of_result); result[size_of_result-1]=0; return(0); } else { perror("access failed 2"); } } else { if(findyourself_debug) printf(" searching $PATH\n"); char *saveptr; char *pathitem; for(pathitem=strtok_r(findyourself_save_path, findyourself_path_list_separator, &saveptr); pathitem; pathitem=strtok_r(NULL, findyourself_path_list_separator, &saveptr) ) { if(findyourself_debug>=2) printf("pathitem=\"%s\"\n", pathitem); strncpy(newpath2, pathitem, sizeof(newpath2)); newpath2[sizeof(newpath2)-1]=0; strncat(newpath2, findyourself_path_separator_as_string, sizeof(newpath2)); newpath2[sizeof(newpath2)-1]=0; strncat(newpath2, findyourself_save_argv0, sizeof(newpath2)); newpath2[sizeof(newpath2)-1]=0; realpath(newpath2, newpath); if(findyourself_debug) printf(" newpath=\"%s\"\n", newpath); if(!access(newpath, F_OK)) { strncpy(result, newpath, size_of_result); result[size_of_result-1]=0; return(0); } } // end for perror("access failed 3"); } // end else // if we get here, we have tried all three methods on argv[0] and still haven't succeeded. Include fallback methods here. return(1); } main(int argc, char **argv) { findyourself_init(argv[0]); char newpath[PATH_MAX]; printf(" argv[0]=\"%s\"\n", argv[0]); realpath(argv[0], newpath); if(strcmp(argv[0],newpath)) { printf(" realpath=\"%s\"\n", newpath); } find_yourself(newpath, sizeof(newpath)); if(1 || strcmp(argv[0],newpath)) { printf(" findyourself=\"%s\"\n", newpath); } sleep(1); /* in case run from desktop */ } 

这里的输出结果表明,在以前的每一个testing中,它确实发现了它自己。

 tcc -o ~/bin/echoargc ~/src/echoargc.c cd ~ /home/whitis/bin/echoargc argv[0]="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" echoargc argv[0]="echoargc" realpath="/home/whitis/echoargc" findyourself="/home/whitis/bin/echoargc" bin/echoargc argv[0]="bin/echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" bin//echoargc argv[0]="bin//echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" bin/./echoargc argv[0]="bin/./echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" src/../bin/echoargc argv[0]="src/../bin/echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" cd ~/bin *echo* argv[0]="echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" e?hoargc argv[0]="echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" ./echoargc argv[0]="./echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" cd ~/src ../bin/echoargc argv[0]="../bin/echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" cd ~/junk ~/bin/echoargc argv[0]="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" ~whitis/bin/echoargc argv[0]="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" alias echoit=~/bin/echoargc echoit argv[0]="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" echoarg=~/bin/echoargc $echoarg argv[0]="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" rm junk1 junk2 junk3 ln -s ~/bin/echoargc junk1 ./junk1 argv[0]="./junk1" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" ln -s /home/whitis/bin/echoargc junk2 ./junk2 argv[0]="./junk2" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" ln -s junk1 junk3 ./junk3 argv[0]="./junk3" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" 

上面描述的两个GUI启动也正确地find程序。

有一个潜在的陷阱。 如果程序在testing之前设置了setuid,那么access()函数就会丢弃权限。 如果程序可以作为高级用户而不是常规用户,那么可能会出现这些testing失败的情况,尽pipe在这种情况下程序实际上不可能被执行。 可以使用euidaccess()来代替。 然而,它有可能会在实际用户的path上find一个不可访问的程序。

查看Gregory Pakosz(只有一个C文件)的whereami库; 它允许您在各种平台上获取当前可执行文件的完整path。 目前,它在github上作为回购。

Linux上使用/proc/self/exe或者argv[0]的另一种方法是使用由glibc提供的ELF解释器传递的信息,例如:

 #include <stdio.h> #include <sys/auxv.h> int main(int argc, char **argv) { printf("%s\n", (char *)getauxval(AT_EXECFN)); return(0); } 

请注意, getauxval是一个glibc扩展名,为了保持健壮性,您应该检查它,以便它不返回NULL (表明ELF解释器没有提供AT_EXECFN参数),但我不认为这实际上是一个问题在Linux上。

如果你曾经需要支持Mac OS X(没有/ proc /),你会怎么做? 使用#ifdefs隔离平台特定的代码(例如NSBundle)?

是的,使用#ifdefs隔离平台特定的代码是这样做的传统方式。

另一种方法是拥有一个干净的#ifdef -less头文件,其中包含函数声明并将实现放在平台特定的源文件中。 例如,看看Poco C ++库如何为他们的Environment类做类似的事情。

跨平台可靠地工作需要使用#ifdef语句。

下面的代码在Windows,Linux,MacOS,Solaris或FreeBSD中查找可执行文件的path(尽pipeFreeBSD未经testing)。 它使用boost > = 1.55.0来简化代码,但如果你愿意的话,它很容易删除。 只需使用像_MSC_VER和__linux这样的定义作为操作系统和编译器的需求。

 #include <string> #include <boost/predef/os.h> #if (BOOST_OS_WINDOWS) # include <stdlib.h> #elif (BOOST_OS_SOLARIS) # include <stdlib.h> # include <limits.h> #elif (BOOST_OS_LINUX) # include <unistd.h> # include <limits.h> #elif (BOOST_OS_MACOS) # include <mach-o/dyld.h> #elif (BOOST_OS_BSD_FREE) # include <sys/types.h> # include <sys/sysctl.h> #endif /* * Returns the full path to the currently running executable, * or an empty string in case of failure. */ std::string getExecutablePath() { #if (BOOST_OS_WINDOWS) char *exePath; if (_get_pgmptr(&exePath) != 0) exePath = ""; #elif (BOOST_OS_SOLARIS) char exePath[PATH_MAX]; if (realpath(getexecname(), exePath) == NULL) exePath[0] = '\0'; #elif (BOOST_OS_LINUX) char exePath[PATH_MAX]; ssize_t len = ::readlink("/proc/self/exe", exePath, sizeof(exePath)); if (len == -1 || len == sizeof(exePath)) len = 0; exePath[len] = '\0'; #elif (BOOST_OS_MACOS) char exePath[PATH_MAX]; uint32_t len = sizeof(exePath); if (_NSGetExecutablePath(exePath, &len) != 0) { exePath[0] = '\0'; // buffer too small (!) } else { // resolve symlinks, ., .. if possible char *canonicalPath = realpath(exePath, NULL); if (canonicalPath != NULL) { strncpy(exePath,canonicalPath,len); free(canonicalPath); } } #elif (BOOST_OS_BSD_FREE) char exePath[2048]; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PATHNAME; mib[3] = -1; size_t len = sizeof(exePath); if (sysctl(mib, 4, exePath, &len, NULL, 0) != 0) exePath[0] = '\0'; #endif return std::string(exePath); } 

上述版本返回包含可执行文件名称的完整path。 如果您想要不带可执行文件名的path,请将#include boost/filesystem.hpp>并将return语句更改为:

 return strlen(exePath)>0 ? boost::filesystem::path(exePath).remove_filename().make_preferred().string() : std::string(); 

您可以使用argv [0]并分析PATH环境variables。 看看: 一个可以find自己的程序样本

根据QNX Neutrino的版本,有不同的方法可以find用于启动运行过程的可执行文件的完整path和名称。 我将过程标识符表示为<PID> 。 尝试以下操作:

  1. 如果文件/proc/self/exefile存在,那么它的内容就是请求的信息。
  2. 如果/proc/<PID>/exefile存在,那么它的内容就是请求的信息。
  3. 如果文件/proc/self/as存在,那么:
    1. open()该文件。
    2. 至less分配一个sizeof(procfs_debuginfo) + _POSIX_PATH_MAX
    3. 将该缓冲区作为devctl(fd, DCMD_PROC_MAPDEBUG_BASE,...inputdevctl(fd, DCMD_PROC_MAPDEBUG_BASE,...
    4. 将缓冲区转换为procfs_debuginfo*
    5. 请求的信息位于procfs_debuginfo结构的path字段。 警告 :由于某些原因,QNX有时会省略文件path的第一个斜杠。 在需要时加上/
    6. 清理(​​closures文件,释放缓冲区等)。
  4. 使用文件/proc/<PID>/as尝试3中的过程。
  5. 尝试dladdr(dlsym(RTLD_DEFAULT, "main"), &dlinfo)其中dlinfo是一个Dl_info结构,其dli_fname可能包含请求的信息。

我希望这有帮助。

AFAIK,没有这样的方式。 而且还有一个模糊的问题:如果同一个可执行文件有多个硬链接“指向”的话,你想得到什么答案? (硬链接实际上不是“指向”,它们相同的文件,只是在FS层次结构中的另一个地方)。一旦execve()成功执行一个新的二进制文件,关于它的参数的所有信息都将丢失。

更便携的方式获取可执行映像的path名称:

ps可以给你的可执行文件的path,因为你有进程ID。 另外ps是一个POSIX实用程序,所以它应该是可移植的

所以如果进程ID是249297那么这个命令只给你path名。

  ps -p 24297 -o comm --no-heading 

参数的解释

-p – select给定的过程

-o comm – 显示命令名(-o cmdselect整个命令行)

– 不标题 – 不显示标题行,只是输出。

AC程序可以通过popen运行。

如果你使用C,你可以使用getwd函数:

 int main() { char buf[4096]; getwd(buf); printf(buf); } 

这将在标准输出上打印可执行文件的当前目录。

一个程序的绝对值path是在主函数的envp的PWD中,在C中也有一个叫getenv的函数,所以就是这样。