系统全局变量 errno 是如何获得 errno.h 中的值的呢?

很多时候我们在使用 errno 的时候都知道它代表的是 errno.h 中的错误值,可是为什么它就是代表那些值的呢?系统在哪里给它赋值了呢?故事就要从源头开始:
1、errno 全局变量是在哪里定义的?
答:既然是 errno,那一定是和 err 有关的,搜索发现就在 <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__ */
#endif /* _ERRNO_H */
其他先不管,首先我们找到了一个头,那就是 errno 就是 (*__errno_location()),说白了就是函数 __errno_location() 的 返回值 ,结合前面知道 errno 是 int 型整数 ,线索慢慢开始了
2、我们开始去 glibc 即 Linux 源代码中查找 __errno_location() 函数,结果在 errno-loc.c 中有了解释,就一段:
#include <errno.h>
#include <hurd/threadvar.h>

int *
__errno_location (void)
{
  return (int *) __hurd_threadvar_location (_HURD_THREADVAR_ERRNO);
}
strong_alias (__errno_location, __hurd_errno_location)
libc_hidden_def (__errno_location)
意思很明确我 __errno_location() 函数的返回值就是 __hurd_threadvar_location (_HURD_THREADVAR_ERRNO) 函数的返回值被强转成 int * 了,那 __hurd_threadvar_location (_HURD_THREADVAR_ERRNO) 又是什么函数呢?
3、同样搜索 __hurd_threadvar_location 函数,结果没找到,怎么办?通过查看 __errno_location() 函数头文件发现 <hurd/threadvar.h> 似乎好像有点关系,直接找到查看发现这么一段:
#include <machine-sp.h>		/* Define __thread_stack_pointer.  */

/* Return the location of the current thread's value for the
   per-thread variable with index INDEX.  */

extern unsigned long int *
__hurd_threadvar_location (enum __hurd_threadvar_index __index) __THROW
     /* This declaration tells the compiler that the value is constant
	given the same argument.  We assume this won't be called twice from
	the same stack frame by different threads.  */
     __attribute__ ((__const__));

_HURD_THREADVAR_H_EXTERN_INLINE unsigned long int *
__hurd_threadvar_location (enum __hurd_threadvar_index __index)
{
  return __hurd_threadvar_location_from_sp (__index,
					    __thread_stack_pointer ());
}
这是先定义了函数同时在下面直接就给出了函数代码,这个函数的又是调用了 __hurd_threadvar_location_from_sp (__index, __thread_stack_pointer ()) 这个函数,还有一点 _HURD_THREADVAR_H_EXTERN_INLINE 这是什么?(这个其实可以忽略的,因为前面声明了函数是 extern unsigned long int *
注*:在前面有定义
#define _HURD_THREADVAR_H_EXTERN_INLINE extern __inline
了解到其实就是 extern __inline ,其中 ___inline 表示函数是内联函数,没其他功效,在这里我们直接忽略
4、我们同样在<hurd/threadvar.h> 中往前看,有这样的代码:
extern unsigned long int *__hurd_threadvar_location_from_sp
  (enum __hurd_threadvar_index __index, void *__sp);
_HURD_THREADVAR_H_EXTERN_INLINE unsigned long int *
__hurd_threadvar_location_from_sp (enum __hurd_threadvar_index __index,
				   void *__sp)
{
  unsigned long int __stack = (unsigned long int) __sp;
  return &((__stack >= __hurd_sigthread_stack_base &&
	    __stack < __hurd_sigthread_stack_end)
	   ? __hurd_sigthread_variables
	   : (unsigned long int *) ((__stack & __hurd_threadvar_stack_mask) +
				    __hurd_threadvar_stack_offset))[__index];
}

真相似乎已经越来越近了,我们只需要弄清楚一下内容,结果就呼之欲出了:
  1. __hurd_threadvar_index __index 是什么?
  2. __sp 是一个 void * 指针?
  3. __hurd_sigthread_stack_base 是什么?
  4. __hurd_sigthread_stack_end 是什么?
  5. __hurd_sigthread_variables 是什么?
  6. __hurd_threadvar_stack_mask 是什么?
  7. __hurd_threadvar_stack_offset 是什么?
5、往前有定义
enum __hurd_threadvar_index
  {
    _HURD_THREADVAR_MIG_REPLY,	/* Reply port for MiG user stub functions.  */
    _HURD_THREADVAR_ERRNO,	/* `errno' value for this thread.  */
    _HURD_THREADVAR_SIGSTATE,	/* This thread's `struct hurd_sigstate'.  */
    _HURD_THREADVAR_DYNAMIC_USER, /* Dynamically-assigned user variables.  */
    _HURD_THREADVAR_MALLOC,	/* For use of malloc.  */
    _HURD_THREADVAR_DL_ERROR,	/* For use of -ldl and dynamic linker.  */
    _HURD_THREADVAR_RPC_VARS,	/* For state of RPC functions.  */
    _HURD_THREADVAR_LOCALE,	/* For thread-local locale setting.  */
    _HURD_THREADVAR_CTYPE_B,	/* Cache of thread-local locale data.  */
    _HURD_THREADVAR_CTYPE_TOLOWER, /* Cache of thread-local locale data.  */
    _HURD_THREADVAR_CTYPE_TOUPPER, /* Cache of thread-local locale data.  */
    _HURD_THREADVAR_MAX		/* Default value for __hurd_threadvar_max.  */
  };
再从前面 __hurd_threadvar_location (_HURD_THREADVAR_ERRNO) 传过来的参数,知道 __hurd_threadvar_location_from_sp (enum __hurd_threadvar_index __index, void *__sp) 函数第一个参数是 _HURD_THREADVAR_ERRNO ,第二个参数在 __sp 指向 __thread_stack_pointer () 函数的返回值,查询 <machine-sp.h> 有如下:

_EXTERN_INLINE void *
__thread_stack_pointer (void)
{
  register void *__sp__;
  __asm__ ("mr %0, 1" : "=r" (__sp__));
  return __sp__;
}
关于以上的返回值,不好意思目前还没学到看不懂,后期接触到了补上。
6、继续 cdefg 中的变量在前面声明了:
extern unsigned long int __hurd_threadvar_stack_mask;
extern unsigned long int __hurd_threadvar_stack_offset;
extern unsigned long int __hurd_threadvar_stack_mask;
extern unsigned long int __hurd_threadvar_stack_offset;
extern unsigned long int __hurd_sigthread_stack_base;
extern unsigned long int __hurd_sigthread_stack_end;
extern unsigned long int *__hurd_sigthread_variables;
以下是上面变量的注释:
/* The per-thread variables are found by ANDing this mask
   with the value of the stack pointer and then adding this offset.

   In the multi-threaded case, cthreads initialization sets
   __hurd_threadvar_stack_mask to ~(cthread_stack_size - 1), a mask which
   finds the base of the fixed-size cthreads stack; and
   __hurd_threadvar_stack_offset to a small offset that skips the data
   cthreads itself maintains at the base of each thread's stack.

   In the single-threaded case, __hurd_threadvar_stack_mask is zero, so the
   stack pointer is ignored; and __hurd_threadvar_stack_offset gives the
   address of a small allocated region which contains the variables for the
   single thread.  */

/* A special case must always be made for the signal thread.  Even when there
   is only one user thread and an allocated region can be used for the user
   thread's variables, the signal thread needs to have its own location for
   per-thread variables.  The variables __hurd_sigthread_stack_base and
   __hurd_sigthread_stack_end define the bounds of the stack used by the
   signal thread, so that thread can always be specifically identified.  */

/* At the location described by the two variables above,
   there are __hurd_threadvar_max `unsigned long int's of per-thread data.  */
没时间翻译了
7、解释一下 return &((__stack >= __hurd_sigthread_stack_base && __stack < __hurd_sigthread_stack_end) ? __hurd_sigthread_variables : (unsigned long int *) ((__stack & __hurd_threadvar_stack_mask) + __hurd_threadvar_stack_offset))[__index] 的意思:
首先返回一个地址
该地址要么是 __hurd_sigthread_variables 要么是 (unsigned long int *) ((__stack & __hurd_threadvar_stack_mask) + __hurd_threadvar_stack_offset))[__index]
如果 __hurd_sigthread_stack_base<=__stack < __hurd_sigthread_stack_end 为真,则取前者的地址,否者取后者的地址
__stack & __hurd_threadvar_stack_mask 这是一个二进制 与 运算
(unsigned long int *) ((__stack & __hurd_threadvar_stack_mask) + __hurd_threadvar_stack_offset))[__index] 等同于 (unsigned long int *)(&__hurd_threadvar_stack_offset[__stack & __hurd_threadvar_stack_mask][_HURD_THREADVAR_ERRNO])






  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: errno.h 是 C 语言的一个标准头文件,它包含了一些常量和宏定义,用于描述系统错误。 在执行一些操作时(如打开一个文件、创建一个线程等),可能会出现错误。如果错误发生,系统会将错误代码存储在一个全局变量 errno errno.h 头文件的常量和宏定义与这些错误代码相关。 例如,如果打开一个文件时发生错误,则可能会将 errno 设置为 ENOENT,这表示文件不存在。在 errno.h ,它是这样定义的: ```#define ENOENT 2 /* No such file or directory */``` 你可以使用 perror 函数来打印 errno 对应的错误信息。例如: ``` if (fp == NULL) { perror("Error opening file"); } ``` 这样,如果 errno 为 ENOENT,则会输出 "Error opening file: No such file or directory"。 你还可以使用 strerror 函数来获取 errno 对应的错误信息字符串。例如: ``` if (fp == NULL) { fprintf(stderr, "Error opening file: %s\n", strerror(errno)); } ``` 这样,如果 errno 为 ENOENT,则会输出 "Error opening file: No such file or directory"。 ### 回答2: C语言的<errno.h>头文件是用来处理和报告错误的标准头文件。它定义了一个整数变量errno,用于存储最近一次发生的错误代码。 errno主要通过系统调用或库函数的返回来判断是否发生了错误。当一个系统调用或库函数执行失败时,它通常会返回一个特殊的,并设置errno以表示具体的错误类型。 <errno.h>头文件定义了许多常量,表示不同类型的错误。其一些常见的错误类型包括: - EPERM:操作不允许 - ENOENT:文件或目录不存在 - ESRCH:没有这个进程 - EINTR:被信号断的系统调用 - EINVAL:无效的参数 - EIO:输入/输出错误 - ENOMEM:内存不足 可以使用errno全局变量来检查和报告错误。常见的做法是,在函数调用失败后,使用errno判断错误类型,并根据具体的错误类型采取相应的措施,例如重新尝试操作、输出错误信息或处理其他错误相关的操作。 具体的用法如下: 1. 在使用errno之前,应该先包含<errno.h>头文件。 2. 当一个系统调用或库函数返回一个指定的错误时,可以通过检查errno来确定具体的错误类型。 3. 可以使用perror()函数来输出描述性错误信息,它会自动解释errno,并结合相关错误信息输出到标准错误流。 4. 如果需要清除errno,可以使用errno的某些特定,例如将其置为0。 总结来说,<errno.h>头文件提供了处理和报告错误的功能,通过设置和检查errno变量的,我们可以更容易地定位和解决C语言程序可能出现的错误。 ### 回答3: errno.h 是 C 语言的一个头文件,用于处理错误码(error code)。在程序运行过程,如果发生了某种错误,C 语言会把相关的错误代码存储到全局变量 errno ,而 errno.h 头文件则定义了一系列宏,用于表示不同的错误代码。 errno.h 头文件的宏定义了一些常见的错误代码,比如 EDOM(数学参数超出范围)、EPERM(操作不允许)等等。这些宏通常以 E 开头,并且对应的错误代码为整数。使用这些宏可以方便地在程序获取和识别错误代码,并根据需要进行相应的处理。 通过包含 errno.h 头文件,我们可以使用 errno 全局变量以及相关的函数来处理错误码。errno 变量在程序开始之前会被设置为 0,表示没有错误发生。当某个函数调用失败时,它会将合适的错误码存储到 errno 。我们可以通过检查 errno来确定发生了什么错误,并根据需要采取适当的措施。 通常,在发生错误之后,我们可以使用 perror 函数来打印出与当前错误码对应的错误消息。perror 函数会自动根据 errno找到对应的错误信息,并将其输出到标准错误流(stderr)。这样可以方便地定位和排查程序出现的错误。 需要注意的是,errno.h 头文件定义的错误码是平台特定的,也就是说在不同的操作系统可能会略有差异。因此,在编写具有平台相关性的代码时,我们应该注意不同操作系统errno.h 头文件定义的错误码的差异。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值