errno与其应用

一、errno的由来
    在C编程中,errno是个不可缺少的变量,特别是在网络编程中。如果你没有用过errno,那只能说明你的程序不够健壮。当然,如果你是WIN32平台的GetLastError(),效果也是一样的。
    为什么会使用errno呢?个人认为,这是系统库设计中的一个无奈之举,他更多的是个技巧,而不是架构上的需要。我们观察下函数结构,可以发现,函数的参 数返回值只有一个,这个返回值一般可以携带错误信息,比如负数表示错误,而正数表述正确的返回值,比如recv函数。但是对于一些返回指针的函数, 如:char *get_str();这个方法显然没有用的。NULL可以表示发生错误,但是发生什么错误却毫无办法。于是,errno就诞生了。全局变量errno可 以存放错误原因,当错误发生时,函数的返回值是可以通过非法值来提示错误的发生。

二、errno的线程安全
    errno是全局变量,但是在多线程环境下,就会变得很恐怖。当你调用一个函数时,发现这个函数发生了错误,但当你使用错误原因时,他却变成了另外一个线程的错误提示。想想就会觉得是件可怕的事情。
    将errno设置为线程局部变量是个不错的主意,事实上,GCC中就是这么干的。他保证了线程之间的错误原因不会互相串改,当你在一个线程中串行执行一系列过程,那么得到的errno仍然是正确的。

    看下,bits/errno.h的定义:

# ifndef __ASSEMBLER__
/* Function to get address of global `errno' variable.  */
extern int *__errno_location (void) __THROW __attribute__ ((__const__));
 
#  if !defined _LIBC || defined _LIBC_REENTRANT
/* When using threads, errno is a per-thread value.  */
#   define errno (*__errno_location ())
#  endif
# endif /* !__ASSEMBLER__ */

而 errno.h中是这样定义的:

/* Declare the `errno' variable, unless it's defined as a macro by
   bits/errno.h.  This is the case in GNU, where it is a per-thread
   variable.  This redeclaration using the macro still works, but it
   will be a function declaration without a prototype and may trigger
   a -Wstrict-prototypes warning.  */
#ifndef errno
extern int errno;
#endif

显然,errno实际上,并不是我们通常认为的是个整型数值,而是通过整型指针来获取值的。这个整型就是线程安全的。

三、errno的实现
static pthread_key_t key;
static pthread_once_t key_once = PTHREAD_ONCE_INIT;

static void make_key()
{
(void) pthread_key_create(&key, NULL);
}

int *_errno()
{
int *ptr ;

(void) pthread_once(&key_once, make_key);
if ((ptr = pthread_getspecific(key)) == NULL) 
{
ptr = malloc(sizeof(int)); 
(void) pthread_setspecific(key, ptr);
}

return ptr ;
}

 四、errno的应用
    errno在库中得到广泛的应用,但是,错误编码实际上不止那么多。我们需要在自己的系统中增加更多的错误编码。一种方式就是直接利用errno,另外一种方式就是定义自己的user_errno。
   使用errno,strerror可能无法解析,这需要自己解决。但errno使用线程变量的方式值得借鉴。

  

 Linux中errno使用

当linux中的C api函数发生异常时,一般会将errno变量(需include
errno.h)赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因,在实际编程中用这一招解决了不少原本看来莫名其妙的问题。但是
errno是一个数字,代表的具体含义还要到errno.h中去阅读宏定义,而每次查阅是一件很繁琐的事情。有下面几种方法可以方便的得到错误信息 
(1)void perror(const char *s) 
函数说明 
perror ( )用来将上一个函数发生错误的原因输出到标准错误(stderr),参数s 所指的字符串会先打印出,后面再加上错误原因 字符串。此错误原因依照全局变量 errno 的值来决定要输出的字符串。 
(2) char *strerror(int errno) 
将错误代码转换为字符串错误信息,可以将该字符串和其它的信息组合输出到用户界面例如 
fprintf(stderr,"error in CreateProcess %s, Process ID %d ",strerror(errno),processID) 
注:假设processID是一个已经获取了的整形ID 
(3)printf("%m", errno); 
另外不是所有的地方发生错误的时候都可以通过error获取错误代码,例如下面的代码段 
/*注:下面的头文件使用""而没有直接使用尖括号是因为博客大巴中尖括号当作html符号,所以其内部的头文件名字会被直接忽略*/ 
#include"stdio.h" 
#include "stdlib.h" 
#include "errno.h" 
#include "netdb.h" 
#include "sys/types.h" 
#include "netinet/in.h" 
int main (int argc, char *argv[]) 

struct hostent *h; 
if (argc != 2) 

fprintf (stderr ,"usage: getip address\n"); 
exit(1); 

/* 取得主机信息 */ 
if((h=gethostbyname(argv[1])) == NULL) 

/* 如果gethostbyname 失败,则给出错误信息 */ 
herror(“gethostbyname”); 
exit(1); 

/* 列印程序取得的信息 */ 
printf(“Host name : %s\n”, h->h_name); 
printf(“IP Address : %s\n”, inet_ntoa (*((struct in_addr *)h->h_addr))); 
return 0; 

/*************************************/ 
通过上面的代码可以看到:使用gethostbyname()函数,你不能使用perror()来输出错误信息(因为错误代码存储在 h_errno 中而不是errno 中。所以,你需要调用herror()函数。 

简单的传给gethostbyname() 一个机器名(“bbs.tsinghua.edu.cn”),然后就从返回的结构struct
hostent 中得到了IP 等其他信息.程序中输出IP 地址的程序需要解释一下:h->h_addr
是一个char*,但是inet_ntoa()函数需要传递的是一个struct in_addr 结构。所以上面将h->h_addr
强制转换为struct in_addr*,然后通过它得到了所有数据。 
转: http://wzgyantai.blogbus.com/logs/24470871.html
Linux/Unix C编程之的perror函数,strerror函数,errno 
2009-07-10 17:20 
#include // void perror(const char *msg); 
#include // char *strerror(int errnum); 
#include //errno 
errno是错误代码,在errno.h头文件中 
void perror(const char *s) 
perror是错误输出函数,在标准输出设备上输出一个错误信息。 
参数s一般是参数错误的函数 
例如perror("fun"),其输出为:fun:后面跟着错误信息(加上一个换行符) 
char *strerror(int errnum);通过参数errnum(也就是errno),返回错误信息 
以下是测试程序: 
//程序名:errtest.c,环境为linux 
#include 
#include 
#include 
#include 
int main(int argc,char *argv[]){ 
FILE *fp; 
char *buf; 
if((fp=fopen(argv[1],"r"))==NULL) 

perror("perror"); 
printf("sterror:%s\n",strerror(errno)); 
exit(1); 

perror("perror"); 
errno=13; 
printf("strerror:%s\n",strerror(errno)); 
fclose(fp); 
return 0; 

============================== 
编译为errtest 
如果输入这样的命令格式:./errtest 111.c(其中111.c不存在) 
输出为: 
perror: No such file or directory 
sterror:Illegal seek 
就是两个都是输出到屏幕上来了。而且sterror函数通过errno得到错误代码 
如果命令格式为:./errtest 111.c > out.c(其中111.c不存在) 
把输出重定位到out.c文件中,会发现屏幕输出为: 
perror: No such file or directory 
就是说函数perror始终输出到标准输出设备上。而printf输出到文件中了 
如果命令格式为:./errtest 222.c(其中222.c存在) 
屏幕输出为: 
perror: Success 
strerror: Permission denied(通过errno=12得到的一个信息)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值