R..*_*R.. 35
处理exec
失败的问题通常exec
是在子进程中执行,并且您希望在父进程中执行错误处理.但你不能仅仅exit(errno)
因为(1)你不知道错误代码是否适合退出代码,而且(2),你无法区分exec
新程序中的失败和失败退出代码exec
.
我所知道的最佳解决方案是使用管道来传达以下方面的成功或失败exec
:
exec
,则父级读取eof(零长度读取),因为close-on-exec成功exec
关闭管道的写入端.或者,如果exec
失败,父母将读取错误代码并可以相应地继续.无论哪种方式,父母都会阻止,直到孩子打电话exec
.Car*_*rum 20
的
execl()
,execle()
,execlp()
,execvp()
,和execvP()
功能可能会失败并置errno为库函数指定的错误的设定execve(2)
和malloc(3)
.该
execv()
函数可能会失败,并为库函数指定的任何错误设置errnoexecve(2)
.
然后从execve(2)
手册页:
错误
Execve()
如果出现以下情况,将失败并返回调用进程:
[E2BIG]
- 新进程参数列表中的字节数大于系统强制限制.此限制由sysctl(3)
MIB变量指定KERN_ARGMAX
.[EACCES]
- 拒绝路径前缀的组件的搜索权限.[EACCES]
- 新进程文件不是普通文件.[EACCES]
- 新进程文件模式拒绝执行权限.[EACCES]
- 新进程文件位于已禁用执行(MNT_NOEXEC
in<sys/mount.h>
)的文件系统上.[EFAULT]
- 新进程文件的长度不能超过其标头中的大小值.[EFAULT]
- Path,argv或envp指向非法地址.[EIO]
- 从文件系统读取时发生I/O错误.[ELOOP]
- 转换路径名时遇到太多符号链接.这被认为是循环符号链接的指示.[ENAMETOOLONG]
- 路径名的组件超出{NAME_MAX}
字符,或者整个路径名超出{PATH_MAX}
字符.[ENOENT]
- 新的流程文件不存在.[ENOEXEC]
- 新进程文件具有相应的访问权限,但具有无法识别的格式(例如,其标头中的无效幻数).[ENOMEM]
- 新进程需要的虚拟内存超过强制最大值(getrlimit(2)
)所允许的数量.[ENOTDIR]
- 路径前缀的组件不是目录.[ETXTBSY]
- 新的进程文件是一个纯过程(共享文本)文件,当前可供某些进程写入或读取.
malloc()
很简单,只使用ENOMEM
.来自malloc(3) man page
:
如果成功的话,
calloc()
,malloc()
,realloc()
,reallocf()
,和valloc()
函数返回一个指向分配的内存.如果有错误,则返回NULL
指针并设置errno
为ENOMEM
.
在exec()
调用返回后执行的操作取决于上下文 - 程序应该执行的操作,错误是什么以及您可以采取哪些措施来解决问题.
麻烦的一个原因可能是您指定了一个简单的程序名而不是路径名; 也许你可以重试execvp()
,或将命令转换为调用sh -c 'what you originally specified'
.这些是否合理取决于应用.如果涉及重大安全问题,您可能不会再试一次.
如果您指定了路径名并且存在问题(ENOTDIR,ENOENT,EPERM),那么您可能没有任何合理的回退,但您可以有意义地报告错误.
在过去(10多年前),有些系统不支持'#!' shebang表示法,如果您不确定是否正在执行可执行文件或shell脚本,则将其作为可执行文件进行尝试,然后将其作为shell脚本重试.如果您运行的是Perl脚本,那可能会也可能不会起作用,但在那些日子里,您编写了Perl脚本以检测它们是由shell运行并使用Perl重新执行.幸运的是,那些日子已经结束了.
在可能的范围内,重要的是确保进程报告问题以便可以跟踪它 - 将其消息写入日志文件或仅写入stderr(或甚至可能syslog()
),以便那些必须解决问题的人除了倒霉的最终用户的报告"我试过X而且它不起作用"之外,还有更多信息可以帮助他们.至关重要的是,如果没有任何作用,则退出状态不为0,因为这表示成功.即使这可能会被忽略 - 但你做到了你能做到的.