linux 系统编程--系统调用概念,出错处理(一)

铁律

无论何时,只要执行了系统调用,都要检查调用的返回状态以确定是否调用成功!

概念

系统调用是内核入口,借助于这一机制,程序员可以请求内核去执行某些动作,称为应用程序编程接口API。
无论是linux还是windows,这一概念并没有区别。

① 系统调用将处理器从用户态切换到核心态,以便cpu访问受到保护的内核内存。

② 系统调用的组成是固定的,每个系统调用在内部都有一个唯一的数字来标识。

③ 每个系统调用可辅之以一套参数,对用户空间与内核空间传递的信息加以规范。

从编程的角度看,系统调用和c语言函数的调用很相似。但在执行系统调用时,幕后会经历诸多步骤:

① 应用程序通过c语言函数库中的外壳(wrapper)函数,来发起系统调用。

② 对系统调用中断处理来说,外壳函数必须保证所有的系统函数调用参数可用,外壳函数将可用参数复制到寄存器。

③由于所有系统调用进入内核方式相同,因此外壳函数必须将系统调用编号复制到一个特殊的cpu寄存器(%eax)中。

④外壳函数执行一条中断机器指令(int 0x80),引发cpu从用户态切换到核心态,并执行系统中断0x80的中断向量所指向的代码。
教新的x86-32 硬件平台实现了sysenter指令,较之传统的int 0x80中断指令,sysenter指令进入内核的速度更快。
linux 2.6内核及glibc 2.3.2 以后的版本都支持sysenter指令。

⑤为响应中断0x80,内核会调用system_call()例程来处理中断
Ⅰ 在内核栈中保存寄存器值

Ⅱ 审核系统调用编号的有效性

Ⅲ 以系统调用编号对存放所有调用服务例程的列表(sys_call_table)进行索引,发现并调用相应例程。
如果调用带有参数,会首先检查参数的有效性。
执行必要的任务,最后将结果状态返回给system_call()例程

Ⅳ 从内核栈中恢复各寄存器值,并将系统调用返回值置于栈中。
Ⅵ 返回至外壳函数,同时将处理器切换回用户态。

⑥ 如果系统调用的返回值表明有误,外壳函数会使用该值来设置全局变量errno。
然后外壳函数会返回到调用程序,并同时返回一个整型值,以表明系统调用是否成功。
在linux上,系统调用服务遵循的惯例是——调用成功返回正值,调用失败返回负值。

综上可见,即便对于一个简单的系统调用,仍要完成相当多的工作,因此系统调用的开销虽小,却不容忽视。
同时,从C语言编程的角度看,调用C语言函数库的外壳(wrapper)函数等同于调用相应的系统服务。

系统调用出错处理

在linux中,因为外壳函数会使用一个值来设置全局变量errno。
如果调用成功,errno为正值,调用失败,errno为负值。

所以它提供了一个叫做errno.h的头文件描述对应的错误。
errno是一个整型变量,在errno.h的头文件定义一系列字符串常量来描述错误。
毕竟数字不好记忆,因此还提供了两个函数perror()和strerror()来输出错误。

perror的功能是根据全局变量errno的值来获取对应的字符串,再显示出来。
strerror的功能差不多,区别在于它不会直接显示,而是把对应的字符串保存起来返回。
后者比前者更加灵活,还可以对这个字符串进行处理,配合printf函数显示。

#include <stdio.h>
#include <string.h>     //for strerror()

int main()
{
    int tmp ;
    for(tmp = 0; tmp <=256; tmp++)
    {
        printf("errno: %2d\t%s\n",tmp,strerror(tmp));
    }
    return 0;
}

在这里插入图片描述
通常情况下,要使用errno,perror()和strerror()函数,应该包含头文件errno.h。
上面的例子没有用,是因为在 <string.h>中已经包含了errno.h。

errno的值也是随不同操作系统和硬件架构的不同有所变化。
在我的ubuntu里最大errno值是133.超过之后无法识别错误(unknown error nnn),在某些其他实现中,这种情况strerror会返回NULL。

注意,errno保存的是上一次调用失败的值! 所以在实际使用中,必须坚持检查函数的返回值是否表明出错,这个时候才再检查errno确定错误原因。
所以一个常用方法是在一开始把errno设置为零,以免混淆。

而在win10 64位 vs2015平台上,同样的代码提示:
在这里插入图片描述
说strerror不安全,用strerror_s替代。
运行效果:
在这里插入图片描述
在这里插入图片描述
有定义的只有42个,比起linux少得多。其对应的错误号也有细微差别。
以上仅仅是错误的提示,代码中还需要使用exit(EXIT_FAILURE)来真正退出程序。

库函数错误处理

不同的库在调用发生错误时,返回的数据类型和值各不相同。

① 某些库函数返回错误信息的方式与系统调用完全相同。即返回值为-1

② 某些库函数出错会返回-1之外的其他值,当然也会设置errno来表明具体的出错情况。比如fopen()出错返回NULL指针。

③某些库函数根本不使用errno,对此类函数来说,只能去参考相应的手册,不应使用errno,perror()或者strerror()来诊断错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值