如何查看errno的值?

How to check the value of errno?

我正在使用一个系统调用,如果它失败了,我需要对不同的 errnos 做不同的事情。

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

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

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

至于strerro - 如果它是我需要的,我不知道如何使用它,因为here它说实际的字符串与错误不一样。引用手册页:“(例如,如果 errnum 是 EINVAL,则返回的描述将是 "Invalid argument")”。

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

我发现我没有很好地解释自己。

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

包括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

我假设您正在使用 Linux,并且我假设您不会直接使用系统调用,而是一些(简单的)包装器(来自syscalls(2). Notice that some weird system calls are not wrapped by the C library (a well known example of unwrapped system call would be sigreturn(2) which you probably should never use). Quite often the C library is GNU glibc, but it might be musl-libc etc. Notice also that kernel raw system calls have different calling conventions than ordinary C function (so in practice a libc wrapper is required, and is in charge of dealing with errno). Notice also that errno(3) 中列出的您的 C 库)通常是一个宏(几乎表现为某个变量)。

msgrcv(2) 手册页记录 errno 可能是 E2BIGEACCESEFAULT ... ENOMSG 之一ENOSYS ...(请参阅该手册页以获取所有可能错误的列表)。

所以你会编写类​​似

的代码
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); 
  };
};

Is the statement if (errno == ENOMSG) .... valid?

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

Is there such a variable errno?

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

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

您可能想要阅读可免费下载的 Advanced Linux Programming (an old book; you can buy something better & newer) and/or all the man pages reachable from intro(2) & syscalls(2) & intro(3)

如何查看errno的值:

  1. 您将需要 #include <errno.h>
  2. 是的,您绝对可以说 if(errno == ENOENT) { ... } 之类的话,这是常见的推荐方式。
  3. 一般来说,做使用errno来判断是发生了错误。检查函数的 return 值,如果 return 值指示错误,则检查 errno 以查看错误是什么。 (更多内容见下文。)
  4. errno 看起来像一个变量,但实际上不是。只要你只是说 if(errno == ENOENT) { ... } 这样的话,这与你无关。但是你可能不应该尝试做像 int errno_ptr = &errno;.
  5. 这样的事情
  6. 您可以使用 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 合理地正常工作。