什么会导致exec失败?接下来发生什么?

pyt*_*hor 24 c unix linux exec

exec(execl,execlp等)可能失败的原因是什么?如果您调用exec并返回,除了恐慌和调用exit之外,还有其他最佳实践吗?

R..*_*R.. 35

处理exec失败的问题通常exec是在子进程中执行,并且您希望在父进程中执行错误处理.但你不能仅仅exit(errno)因为(1)你不知道错误代码是否适合退出代码,而且(2),你无法区分exec新程序中的失败和失败退出代码exec.

我所知道的最佳解决方案是使用管道来传达以下方面的成功或失败exec:

  1. 在分叉之前,在父进程中打开一个管道.
  2. 在分叉之后,父级关闭管道的写入端并从读取端读取.
  3. 孩子关闭阅读结束并为写作结束设置close-on-exec标志.
  4. 孩子叫exec.
  5. 如果exec失败,则子进程使用管道将错误代码写回父进程,然后退出.
  6. 如果子项成功执行exec,则父级读取eof(零长度读取),因为close-on-exec成功exec关闭管道的写入端.或者,如果exec失败,父母将读取错误代码并可以相应地继续.无论哪种方式,父母都会阻止,直到孩子打电话exec.
  7. 父关闭管道的读取端.

  • 阅读`fcntl`的文档.这不是你如何使用它,不幸的是,可变参数API阻止编译器告诉你你使用它错了... (2认同)

Car*_*rum 20

exec(3)手册页:

execl(),execle(),execlp(),execvp(),和execvP()功能可能会失败并置errno为库函数指定的错误的设定execve(2)malloc(3).

execv()函数可能会失败,并为库函数指定的任何错误设置errno execve(2).

然后从execve(2)手册页:

错误

Execve() 如果出现以下情况,将失败并返回调用进程:

  • [E2BIG] - 新进程参数列表中的字节数大于系统强制限制.此限制由sysctl(3)MIB变量指定KERN_ARGMAX.
  • [EACCES] - 拒绝路径前缀的组件的搜索权限.
  • [EACCES] - 新进程文件不是普通文件.
  • [EACCES] - 新进程文件模式拒绝执行权限.
  • [EACCES]- 新进程文件位于已禁用执行(MNT_NOEXECin <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指针并设置errnoENOMEM.


Jon*_*ler 8

exec()调用返回后执行的操作取决于上下文 - 程序应该执行的操作,错误是什么以及您可以采取哪些措施来解决问题.

麻烦的一个原因可能是您指定了一个简单的程序名而不是路径名; 也许你可以重试execvp(),或将命令转换为调用sh -c 'what you originally specified'.这些是否合理取决于应用.如果涉及重大安全问题,您可能不会再试一次.

如果您指定了路径名并且存在问题(ENOTDIR,ENOENT,EPERM),那么您可能没有任何合理的回退,但您可以有意义地报告错误.

在过去(10多年前),有些系统不支持'#!' shebang表示法,如果您不确定是否正在执行可执行文件或shell脚本,则将其作为可执行文件进行尝试,然后将其作为shell脚本重试.如果您运行的是Perl脚本,那可能会也可能不会起作用,但在那些日子里,您编写了Perl脚本以检测它们是由shell运行并使用Perl重新执行.幸运的是,那些日子已经结束了.

在可能的范围内,重要的是确保进程报告问题以便可以跟踪它 - 将其消息写入日志文件或仅写入stderr(或甚至可能syslog()),以便那些必须解决问题的人除了倒霉的最终用户的报告"我试过X而且它不起作用"之外,还有更多信息可以帮助他们.至关重要的是,如果没有任何作用,则退出状态不为0,因为这表示成功.即使这可能会被忽略 - 但你做到了你能做到的.