【问题标题】:How to check the value of errno?如何检查errno的值?
【发布时间】:2018-02-11 07:07:20
【问题描述】:

我正在使用系统调用,如果它失败,我需要为不同的错误做不同的事情。

我需要编写如下所示的代码:

int res;
res = systemCall();
if (res == -1)
{
    if (errno == ENOMSG)
    {
        doSomething();
    }
    else
    {
        doSomethingElse();
    }
}

perror 没有帮助,因为它只打印值。

至于 strerro - 如果它是我需要的,我不知道如何使用它,因为 here 它表示实际字符串与错误不同。从手册页引用:“(例如,如果 errnum 为 EINVAL,则返回的描述将为“无效参数”)。

我正在使用 Linux。 系统调用:msgsend 和 msgrcv (https://linux.die.net/man/2/msgrcv)。我不确定你问的是什么 C 库。

我明白我没有很好地解释自己。

if (errno == ENOMSG) 语句有效吗?有没有这样的变量errno?基本上我的问题是:为了测试 errno,if 语句中应该包含什么内容?

【问题讨论】:

  • 在什么操作系统上?什么确切的系统调用?什么C库?请编辑您的问题以改进它
  • 每个系统调用都有一个可能的 errno 值列表(参见它的 man 页面),这会让你知道你应该测试什么。
  • 你写的很好。请记住,没有库函数将errno 设置为零,除非函数指示错误并且记录为设置errno,否则不应对其进行测试。请注意,许多 pthreads 函数不设置 errno,例如;他们改为返回错误号。此外,即使成功,函数也可以将 errno 设置为非零值。例如,在 Solaris 上,如果标准输出不是终端,即使 printf() 调用成功,errno 也可以设置为 ENOTTY。

标签: c system-calls errno


【解决方案1】:

如何查看errno的值:

  1. 您需要#include <errno.h>
  2. 是的,您绝对可以说if(errno == ENOENT) { ... } 之类的话,这是常用且推荐的方式。
  3. 一般情况下,不要使用errno 来确定发生了错误。检查函数的返回值,如果返回值指示错误,则检查errno 以查看错误是什么。 (更多内容见下文。)
  4. errno 看起来像一个变量,但实际上不是。只要您说if(errno == ENOENT) { ... } 之类的内容,您就不必担心。但你可能不应该尝试做类似int errno_ptr = &errno; 的事情。
  5. 您可以使用perror()strerror() 等函数来获取与errno 值相对应的人类可读错误字符串。但是,是的,您得到的字符串通常是“没有这样的文件或目录”之类的。我知道没有将 errno 值 ENOENT 转换为字符串 "ENOENT" 的好方法。 (附录:有关一些答案,请参阅 this question。)

多说一点关于#3。有时会很想说出类似的话

errno = 0;
printf("Hello, world!\n");
if(errno != 0) {
    fprintf(stderr, "printf failed!\n");
}

但不要那样做。而是这样做

int retval = printf("Hello, world!\n");
if(retval < 0) {
    fprintf(stderr, "printf failed!\n");
}

原因是,在完成工作的过程中,printf 可能做了一些导致错误的事情,设置了errno,但printf 可能已经从错误中恢复并继续成功完成。

如果没有错误,保证不会接触 errno 的库函数很少(我认为一个例子可能是 atoi),但总的来说,这是一回事你必须小心。

多说一点关于#4 的内容。 errno 看起来像一个变量,更具体地说,它看起来像一个全局变量。但是当然全局变量是不好的。但是errno 一直存在;有数千万行代码在使用它;基本上还是很方便的; “修复”它为时已晚。所以,相反,如果你偷看幕后,你会发现大多数实现都做了类似的事情

extern int __errno_pointer;
#define errno (*__errno_pointer)

extern int *__errno_pointer_function();
#define errno (*__errno_function())

通过这种方式,他们可以安排errno 合理地正常工作,即使在多线程代码中也是如此。

【讨论】:

    【解决方案2】:

    我假设您使用的是 Linux,并且我假设您不直接使用系统调用,但syscalls(2) 中列出的一些(简单)包装器(来自您的 C 库) .请注意,C 库没有包装一些奇怪的系统调用(一个众所周知的未包装系统调用示例是 sigreturn(2),您可能永远不应该使用它)。 C 库通常是GNU glibc,但也可能是musl-libc 等。还要注意内核原始系统调用与普通C 函数有不同的calling conventions(因此实际上需要一个libc 包装器,并负责处理errno)。另请注意,errno(3) 通常是一个宏(几乎表现为某个变量)。

    msgrcv(2) 手册页文档,errno 可以是 E2BIGEACCESEFAULT ... ENOMSGENOSYS ... 之一(请参阅该手册页以获取所有可能的错误列表)。

    所以你会编写类​​似的代码

    ssize_t siz = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
    if (siz<0) { // msgrcv failed and has set errno
      if (errno == ENOMSG) 
        dosomething();
      else if (errno == EAGAIN) 
        dosomethingelse();
      /// etc
      else {  
         syslog(LOG_DAEMON|LOG_ERR, "msgrcv failure with %s\n",
                strerror(errno)); 
         exit(EXIT_FAILURE); 
      };
    };
    

    声明if (errno == ENOMSG) .... 有效吗?

    是的,是的;您只想在某些系统调用失败后测试errno(例如,当siz&lt;0 时)。

    errno这样的变量吗?

    没有了。请仔细阅读errno(3) 文档。你不应该声明 extern int errno;(这在 1980 年代是可能的,而不是在 21st 世纪)但你应该总是 #include &lt;errno.h&gt; 并使用 errno 就好像它是一个变量,但它几乎总是一些宏(其定义出现在/usr/include/bits/errno.h 中,包含在/usr/include/errno.h 中)。

    顺便说一句,SysV 风格的设施往往会变得过时并且并不总是可用。我推荐使用 POSIX 消息队列工具,阅读mq_overview(7)

    您可能想阅读可免费下载的Advanced Linux Programming(一本旧书;您可以购买更好和更新的东西)和/或所有可从intro(2)syscalls(2)intro(3) 访问的手册页。

    【讨论】:

    • 高级 Linux 编程下载链接可能已损坏或已过期。似乎不适合我。
    • 已更正,今天有一些随机链接可用。事实上,如果你能负担得起,最好读一本新书。
    • 感谢您的提醒!我会寻找更新的东西。如果有什么想法,很高兴听到建议。
    • 我确实提供了 ALP 副本的链接,今天可以下载该副本。您可以重新分发它(但 IANAL)
    • 强烈推荐 Linux 编程接口 (man7.org/tlpi)。还建议查看 warn(3) 和 err(3)。很多时候,当发生错误时,您要做的就是发送一条关于错误的消息,带有一些上下文(例如文件名),并且可能只是退出。
    【解决方案3】:

    包括errno.h

    一些例子:

    // Error codes
    #define EPERM        1  /* Operation not permitted */
    #define ENOENT       2  /* No such file or directory */
    #define ESRCH        3  /* No such process */
    #define EINTR        4  /* Interrupted system call */
    #define EIO          5  /* I/O error */
    #define ENXIO        6  /* No such device or address */
    #define E2BIG        7  /* Argument list too long */
    #define ENOEXEC      8  /* Exec format error */
    #define EBADF        9  /* Bad file number */
    #define ECHILD      10  /* No child processes */
    #define EAGAIN      11  /* Try again */
    #define ENOMEM      12  /* Out of memory */
    #define EACCES      13  /* Permission denied */
    #define EFAULT      14  /* Bad address */
    #define ENOTBLK     15  /* Block device required */
    #define EBUSY       16  /* Device or resource busy */
    #define EEXIST      17  /* File exists */
    #define EXDEV       18  /* Cross-device link */
    #define ENODEV      19  /* No such device */
    #define ENOTDIR     20  /* Not a directory */
    #define EISDIR      21  /* Is a directory */
    #define EINVAL      22  /* Invalid argument */
    #define ENFILE      23  /* File table overflow */
    #define EMFILE      24  /* Too many open files */
    #define ENOTTY      25  /* Not a typewriter */
    #define ETXTBSY     26  /* Text file busy */
    #define EFBIG       27  /* File too large */
    #define ENOSPC      28  /* No space left on device */
    #define ESPIPE      29  /* Illegal seek */
    #define EROFS       30  /* Read-only file system */
    #define EMLINK      31  /* Too many links */
    #define EPIPE       32  /* Broken pipe */
    #define EDOM        33  /* Math argument out of domain of func */
    #define ERANGE      34  /* Math result not representable */
    

    您的实现可能包含更多 errno,例如 /usr/include/asm-generic/errno.h

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-26
      • 1970-01-01
      • 2010-10-30
      • 2013-07-04
      相关资源
      最近更新 更多