某模块作为前台进程独立运行时,运行命令携带命令行参数;作为某平台下守护进程子进程运行时,需要将命令行参数固化在代码里。类似如下写法:

char *argv[] = {"./DslDriver", "-t", "/bin/VdslModemSco.bin"};

int argc = sizeof(argv) / sizeof(argv[0]);

随后,调用basename函数(头文件为libgen.h)解析argv[0],即"./DslDriver"。实测发现,在Linux原生系统中解析正常,在某平台下解析时则会发生段错误。

合理的想法自然是怀疑两种环境下basename函数的实现不同。Linux原生函数源码未找到,但某平台uclibc源码中可以找到basename函数的实现:

 /* Return final component of PATH.
This is the weird XPG version of this function. It sometimes will
modify its argument. Therefore we normally use the GNU version (in
<string.h>) and only if this header is included make the XPG
version available under the real name. */
extern char *__xpg_basename (char *__path) __THROW;
#define basename __xpg_basename

Libgen.h

先不管注释内容,直接找到__xpg_basename定义处:

 char *__xpg_basename(register char *path)
{
static const char null_or_empty[] = ".";
char *first;
register char *last; first = (char *) null_or_empty; if (path && *path) {
first = path;
last = path - ; do {
if ((*path != '/') && (path > ++last)) {
last = first = path;
}
} while (*++path); if (*first == '/') {
last = first;
}
last[] = ; //注意此句!
} return first;
}

Wstring.c

可见该函数对path参数尾部增加了结束符('\0')。此刻真相大白,basename("./DslDriver")传入的是只读字符串,导致basename函数内试图修改只读数据区,当然会发生段错误。而运行命令中,命令行参数并非只读数据(实为字符数组),因此不会发生段错误。

若要在某平台下安全使用basename函数,有两种改法:

1) 传入字符数组。如下:

char argv[][sizeof("/bin/VdslModemSco.bin")] = {"./DslDriver", "-t", "/bin/VdslModemSco.bin"};

2) 改用GNU的实现版本(头文件为string.h)。GNU版本绝不会更改它的参数,因此可正确处理静态字符串。

【思考】

回想一下,basename内有必要对path增加结束符吗?个人觉得没必要,这样反会限制函数的应用场景。理论上讲,若path为只读字符串,说明调用者确知path内容,手工剥离即可,无需调用basename函数(源码的“合理”之处)。只是本模块的使用方式较为特殊。另一方面,该缺陷也警醒编码者,不要"自作多情"地memset入参内容(使之失去累积性),或对入参字符串添加结束符。这些细节还是留待调用者自由发挥罢~

再举一例说明接口函数的设计,如下消息发送函数:

FUNC_STATUS SendSsynMsg(VOID* pvInMsg, INT16U wInMsgLen, VOID* pvOutMsg, INT16U* pwOutMsgLen);

原始接口要求pvInMsg指针不为空(说明消息体有内容)时,wInMsgLen不能为0。同时,pvOutMsg和pwOutMsgLen指针不能为空,且pwOutMsgLen指向的值(出参长度)不能为0,用以向调用者反馈信息(如Ack)。

其实根据实际使用场景,并不需要限制出参。若调用者不想获得反馈,则应允许pvOutMsg和pwOutMsgLen指针为空。这样,可降低调用的复杂度和出错率。此外,允许pvInMsg指针不为空时wInMsgLen为0,并在接口内按照缓冲区的最大长度拷贝pvInMsg,也可降低调用的出错率(尤其是当wInMsgLen参数为变量而非标量时)——消息接收方必然知道相应消息体内容的长度(否则如何解析?)。

【Tips

如何查找库函数的原型定义所在处?

很简单,在调用处"改写"函数声明。例如,期望函数原型为int Func(char *)——通常可由调用方式猜出,则在调用前声明为int Func(int)。编译器会通过conflicting types错误来指示previous declaration of TheLibFunc

关于Linux系统basename函数缺陷的思考的更多相关文章

  1. Unix/Linux系统时间函数API

    首先说明关于几个时间的概念: 世界时:起初,国际上的标准时间是格林尼治标准时间,以太阳横穿本初子午线的时刻为标准时间正午12点.它根据天文环境来定义,就像古代人们根据日晷来计时一样,如下图: 原子时: ...

  2. Linux系统时间函数

    先来说说自己在做工程过程中的一些理解: 1, 输入time_t,输出tm格式的函数 loctaltime(time_t) / gmtime(time_t) 其中localtime会受时区和夏令时影响, ...

  3. Linux系统学习优缺点

    Linux是一套操作系统,按照鸟哥的说法Linux提供了一个完整的操作系统当中最底层的硬件控制与资源管理的完整架构,这个架构是沿袭Unix良好的传统而来的,功能强大而且稳定性卓越.其实Torvalds ...

  4. Linux系统运维相关的面试题 (问答题)

    这里给大家整理了一些Linux系统运维相关的面试题,有些问题没有标准答案,希望要去参加Linux运维面试的朋友,可以先思考下这些问题.   一.Linux操作系统知识 1.常见的Linux发行版本都有 ...

  5. linux 系统函数之 (dirname, basename)【转】

    转自:http://blog.csdn.net/peter_cloud/article/details/9308333 版权声明:本文为博主原创文章,未经博主允许不得转载. 除非你的原件考虑跨平台. ...

  6. linux 系统函数 basename和dirname

    在linux系统中有这样两个系统函数,basename 和  dirname 1.basename 用于 获取文件名, 1.1 当给定扩展名作为参数之后,甚至可以直接获取文件名 2.与basename ...

  7. Linux系统编程-setitimer函数

    功能:linux系统编程中,setitimer是一个经常被使用的函数,可用来实现延时和定时的功能. 头文件:sys/time.h 函数原型: int setitimer(int which, cons ...

  8. exit()与_exit()函数的区别(Linux系统中)

    注:exit()就是退出,传入的参数是程序退出时的状态码,0表示正常退出,其他表示非正常退出,一般都用-1或者1,标准C里有EXIT_SUCCESS和EXIT_FAILURE两个宏,用exit(EXI ...

  9. Linux系统编程(1)——文件与I/O之C标准I/O函数与系统调用I/O

    Linux系统的I/O也就是一般所说的低级I/O--操作系统提供的基本IO服务,与os绑定,特定于Linux平台.而标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头 ...

随机推荐

  1. 切记CMYK图片格式在IE中将无法显示

    目前为止微软的Internet Explorer 浏览器IE6,IE7,IE8都不支持CMYK颜色模式图像 ,除IE外其他浏览器均能支持!所以大家要注意了 要选择RGB颜色模式,就可以了.

  2. Chart控件,把Y轴设置成百分比

    这次所有属性设置都用代码(就当整理便于以后查询). 在窗体放置一个Chart控件,未做任何设置:然后编写代码: //设置 chart2.Legends[ ].Enabled = false;//不显示 ...

  3. 答CsdnBlogger问-关于定时和后台服务问题

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 前段时间写了不少博客,在问答页面也陆续回答几十个问题,之后Csdn乙同学找到我,说要推荐我参加问答类 ...

  4. Java Socket Option

    选项 public final static int TCP_NODELAY = 0x0001; public final static int SO_REUSEADDR = 0x04; public ...

  5. openssh-server 安装

    sudo apt-get update sudo apt-get install openssh-server 1:ssh-keygen -t rsa -f ~/.ssh/id_rsa 这里会提示输入 ...

  6. 修改PHP的memory_limit限制

    在运行PHP程序,通常会遇到“Fatal Error: Allowed memory size of xxxxxx bytes exhausted”的错误, 这个意味着PHP脚本使用了过多的内存,并超 ...

  7. Educational Codeforces Round 16---部分题解

    710A. King Moves 给你图中一点求出它周围有几个可达的点: 除边界之外都是8个,边界处理一下即可: #include<iostream> #include<cstdio ...

  8. Selenium2学习-028-WebUI自动化实战实例-026-获取页面元素值或者元素属性值

    在自动化脚本编写过程中,经常需要获取页面元素的文本进行判断,以便对于不同的文本进行不同的处理.比如:很多的购物网站,加入购物车的按钮是有多个状态的(加入购物车.到货通知.暂不销售等),那么在实际的操作 ...

  9. ar1020 驱动移植 无效

    移植ar1020的spi驱动.驱动移植过来后,在原来的板子上都能够正常运行,而在新的板子却没有效果. 最后查看新旧板子的AR1020的电路,发现M2引脚连接不同.M2高电平连接的touch是5线的接口 ...

  10. Linux命令行–更多bash shell命令(转)

    4.1.1 探查程序 ps 命令 默认情况下,ps命令只会显示运行在当前控制台下的属于当前用户进程的进程 显示的当前进程的项目 进程号 运行在哪个终端(tty) 进程占用的CPU时间 Linux系统支 ...