大多数系统调用以相同方式出错
由于系统调用依赖于程序以外的东西,所以它们一旦出错,就没办法控制。为了解决这个问题,系统调用总是以相同方式出错。
就拿execle()
调用来说,判断exec()
有没有出错很容易:如果exec()
调用成功,当前程序就会停止运行。一旦程序运行了exec()
以后的代码,就说明出了问题。
但仅仅告诉用户系统调用失败与否是不够的,通常你想知道系统调用为什么失败,因此几乎所有系统调用都遵循“失败黄金法则”。
errno
变量是定义在errno.h中的全局变量,和它定义在一起的还有很多标准错误码,如:
这样你就可以拿errno
和这些值比较,也可以用string.h中的strerror()
的函数查询标准错误消息:
当系统找不到你想运行的程序时就会把errno
变量设置为ENOENT
,以上代码就会显示这条消息:
没有该文件或目录
练习
你可以在不同的机器上用不同命令查看网络配置。在Linux和Mac上,你可以用一个叫
/sbin/ifconfig
的程序;而在Windows上可以用ipconfig
的命令,它的路径保存在命令路径中。下面这个程序试图运行
/sbin/ifconfig
程序,如果失败就运行ipconfig
命令。你不用传递任何参数给这两条命令,仔细考虑需要使用什么类型的exec()
命令?
练习解答
你可以在不同的机器上用不同命令查看网络配置。在Linux和Mac上,你可以用一个叫
/sbin/ifconfig
的程序;而在Windows上可以用ipconfig
的命令,它的路径保存在命令路径中。下面这个程序试图运行
/sbin/ifconfig
程序,如果失败就运行ipconfig
命令。你不用传递任何参数给这两条命令,仔细考虑将使用什么类型的exec()
命令?
这里没有蠢问题
问:
system()
不是比exec()
简单吗?答:是的,但操作系统必须解释你传给
system()
的字符串,这可能引发错误,尤其当你动态创建命令字符串时。问:为什么有那么多的
exec()
函数?答:人们想以不同的方式创建进程,于是创建了不同版本的
exec()
来提高灵活性。问:为什么一定要检查系统调用的返回值?这样程序岂不是会很长?
答:如果在进行系统调用时不检查错误,代码是短了,但可能引发更多的错误。最好在最初写代码时就考虑到错误,以后找起错来也简单。
问:调用了
exec()
函数以后还能做其他事吗?答:不能,只要让
exec()
函数执行成功,就会修改进程。它会运行新程序替代你的程序。也就是说,只要exec()
函数一运行,你的程序就会停止运行。
要点
系统调用是操作系统中的函数。
当进行系统调用时,相当于调用你程序外面的代码。
system()
系统调用可以运行命令字符串。
system()
用起来方便,但也容易出错。
exec()
系统调用在运行程序时给了你更多控制权。
exec()
系统调用有很多版本。系统调用出错时通常会返回-1,但不是绝对的。
系统调用在出错的同时将
errno
变量设为错误码。
弄乱的消息
星巴克的员工写了一个新的订单生成程序,他们管它叫coffee
:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *w = getenv("EXTRA");
if (!w)
w = getenv("FOOD");
if (!w)
w = argv[argc - 1];
char *c = getenv("EXTRA");
if (!c)
c = argv[argc - 1];
printf("%s with %s\n", c, w);
return 0;
}
为了检验程序,他们创建了这个测试程序。你能把代码片段和它们对应的输出结果连接起来吗?
弄乱的消息解答
星巴克的员工写了一个新的订单生成程序,他们管它叫coffee
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *w = getenv("EXTRA");
if (!w)
w = getenv("FOOD");
if (!w)
w = argv[argc - 1];
char *c = getenv("EXTRA");
if (!c)
c = argv[argc - 1];
printf("%s with %s\n", c, w);
return 0;
}
为了检验程序,他们创建了这个测试程序。你能把代码片段和它们对应的输出结果连接起来吗?