问题的起因是要把一个东东从Windows移植到基于Linux的嵌入式系统上。移植过程中,遇到了GetModuleFileName的问题。为了解决这个问题,花了不少的时间,也走了不少弯路。下面是整理的结果。
首先摘录一段文字,来源《UNIX Programming FAQ 中文版》
1.14. 我怎样找到进程的相应可执行文件?
这个问题可以作为'常见未回答问题'('Frequently Unanswered Questions')的一个好候选,因为事实上提出这个问题经常意味着程序的设计有缺陷。:)
你能作的'最佳猜测'('best guess')是通过审视'argv[0]'的值而获得。如果它包括一个'/',那么它可能是可执行程序的绝对或相对(对于在程序开始时的当前目录而言)路径。如果不包括,那么你可以仿效shell对于'PATH'变量的查询来查找这个程序。但是,不能保证成功,因为有可能执行程序时'argv[0]'是一些任意值,也不排除这个可执行文件在执行后可能已经被更名或删除的情况。
如果所有你想做的只是能打印一个和错误消息一起出现的合适的名字,那么最好的方法在'main()'函数中将'argv[0]'的值保存在全局变量中以供整个程序使用。虽然没有保证说'argv[0]'的值总是有意义,但在大多数情况下它是最好的选择。
人们询问这个问题的最普通原因是意图定位他们程序的配置文件。这被认为是不好的形式;包含可执行文件的目录应当*只*包含可执行文件,而且基于管理的要求经常试图将配置文件放置在和可执行文件不同的文件系统。
试图做这个的一个比较不普通但更正规的理由是允许程序调用'exec()'执行它自己;这是一种用来完全重新初始化进程(比如被用于一些'sendmail'的版本)的办法(比如当一个守护程序捕获一个'SIGHUP'信号)。
完全同意上面的观点!所以并不建议在Linux下去实现GetModuleFileName,不过出于技术的角度,讨论一下这个问题也是可以的。
好,下面说说茴字的四种写法。哦,不,是GetModuleFileName的四种写法。
GetModuleFileName的四种写法
方法一:从PATH入手
说明:上文提供的思路
int GetModuleFileName1( char* sModuleName, char* sFileName, int nSize)
{
int ret = -1;
if( strchr( sModuleName,'/' ) != NULL )
strcpy( sFileName, sModuleName );
else
{
char* sPath = getenv("PATH");
char* pHead = sPath;
char* pTail = NULL;
while( pHead != NULL && *pHead != '/x0' )
{
pTail = strchr( pHead, ':' );
if( pTail != NULL )
{
strncpy( sFileName, pHead, pTail-pHead );
sFileName[pTail-pHead] = '/x0';
pHead = pTail+1;
}
else
{
strcpy( sFileName, pHead );
pHead = NULL;
}
int nLen = strlen(sFileName);
if( sFileName[nLen] != '/' )sFileName[nLen] = '/';
strcpy( sFileName+nLen+1,sModuleName);
if( 0 == access( sFileName, F_OK ) )
{
ret = 0;
break;
}
}
}
return ret;
}
方法二:利用which命令
说明: 与方法一相比,完全是换汤不换药,之所以放在这里,是因为其中用到了一个小技巧:即利用popen()实现在代码中执行一段command,并得到其执行的结果。
int GetModuleFileName2( char* sModuleName, char* sFileName, int nSize)
{
int ret = 0;
if( strchr( sModuleName,'/' ) != NULL )
strcpy( sFileName, sModuleName );
else
{
char sBuffer[256] = { 0, };
char sCommand[256] = { 0, };
FILE* fp = NULL;
sprintf( sCommand, "which %s", sModuleName );
if((fp = popen(sCommand,"r")) == NULL){
ret = -1;
}
else
{
sFileName[0] = '/x0';
while(!feof(fp)){
if(fgets(sBuffer,nSize-1,fp) == NULL){
continue;
}
strcat( sFileName, sBuffer );
}
}
int nLen = strlen( sFileName );
if( 0 == nLen )
ret = -1;
else
if( sFileName[nLen-1] = '/n' ) sFileName[nLen-1] = '/x0';
}
return ret;
}
方法二:获取环境变量"_"
int GetModuleFileName3( char* sModuleName, char* sFileName, int nSize)
{
int ret = -1;
char* p = getenv("_");
if( p != NULL && strstr( p, sModuleName ) != NULL )
{
ret = 0;
strcpy( sFileName, p );
}
return ret;
}
方法四:访问proc文件系统,读取/proc/self/maps
说明: 细节请参阅 http://autopackage.org/docs/binreloc/
int GetModuleFileName4( char* sFileName, int nSize)
{
int ret = -1;
char sLine[1024] = { 0 };
void* pSymbol = (void*)"";
FILE *fp;
char *pPath;
fp = fopen ("/proc/self/maps", "r");
if ( fp != NULL )
{
while (!feof (fp))
{
unsigned long start, end;
if ( !fgets (sLine, sizeof (sLine), fp))
continue;
if ( !strstr (sLine, " r-xp ") || !strchr (sLine, '/'))
continue;
sscanf (sLine, "%lx-%lx ", &start, &end);
if (pSymbol >= (void *) start && pSymbol < (void *) end)
{
char *tmp;
size_t len;
/* Extract the filename; it is always an absolute path */
pPath = strchr (sLine, '/');
/* Get rid of the newline */
tmp = strrchr (pPath, '/n');
if (tmp) *tmp = 0;
/* Get rid of "(deleted)" */
//len = strlen (pPath);
//if (len > 10 && strcmp (pPath + len - 10, " (deleted)") == 0)
//{
// tmp = pPath + len - 10;
// *tmp = 0;
//}
ret = 0;
strcpy( sFileName, pPath );
}
}
fclose (fp);
}
return ret;
}
测试代码:
int main ( int argc, char** argv ) {
char buffer[256]={0};
getchar();
printf ("ModuleFileName1 is: %s/n", GetModuleFileName1( argv[0], buffer, 256)==-1?"Not found!":buffer);
printf ("ModuleFileName2 is: %s/n", GetModuleFileName2( argv[0], buffer, 256)==-1?"Not found!":buffer);
printf ("ModuleFileName3 is: %s/n", GetModuleFileName3( argv[0], buffer, 256)==-1?"Not found!":buffer);
printf ("ModuleFileName4 is: %s/n", GetModuleFileName4( buffer, 256)==-1?"Not found!":buffer);
return 0;
}
测试结果:
正常输出结果:
ModuleFileName1 is: /home/coldcrane/bin/hello
ModuleFileName2 is: /home/coldcrane/bin/hello
ModuleFileName3 is: /home/coldcrane/bin/hello
ModuleFileName4 is: /home/coldcrane/bin/hello
程序执行时目录被移动
$ mv ~/bin ~/nib
输出结果:
ModuleFileName1 is: Not found!
ModuleFileName2 is: Not found!
ModuleFileName3 is: /home/coldcrane/bin/hello
ModuleFileName4 is: /home/coldcrane/nib/hello
程序执行时文件被改名
$ mv ~/bin/hello ~/bin/hh
输出结果:
ModuleFileName1 is: Not found!
ModuleFileName2 is: Not found!
ModuleFileName3 is: /home/coldcrane/bin/hello
ModuleFileName4 is: /home/coldcrane/bin/hh
程序执行时文件被删除
$ rm ~/bin/hello
输出结果:
ModuleFileName1 is: Not found!
ModuleFileName2 is: Not found!
ModuleFileName3 is: /home/coldcrane/bin/hello
ModuleFileName4 is: /home/coldcrane/bin/hello (deleted)
相对而言,方法四是最强的,能有的信息都有了,但依然不能解决所有的问题。当然,针对不同的环境,上面的代码还是有一定的参考价值的。