【问题标题】:What's the best way to check return value of read and write system calls?检查读写系统调用的返回值的最佳方法是什么?
【发布时间】:2020-03-09 00:29:49
【问题描述】:

我有兴趣找出在 Linux 上检查读取和写入系统调用的返回值的最佳方法。根据手册页,例如写入:

成功完成后,write() 和 pwrite() 将返回 实际写入与 fildes 关联的文件的字节数。 此数字不得大于 nbyte。否则,-1 应为 返回并设置 errno 以指示错误。

供阅读:

成功时,返回读取的字节数(零表示结束 文件),并且文件位置由这个数字提前。它不是 如果此数字小于请求的字节数,则会出错; 例如,这可能会发生,因为实际上可用的字节更少 现在(可能是因为我们接近文件结尾,或者因为我们 正在从管道或终端读取),或者因为 read() 是 被一个信号打断。出错时,返回 -1,并设置 errno 适当地。在这种情况下,未指定文件是否 位置(如果有)发生变化。

这让我想知道:

if (ret < 0) {
    // see strerror
}

if (ret == -1) {
    // see strerror
}

哪一个是要走的路,为什么?每个手册页,在我看来,从功能的角度来看,这些是完全相同的。这样对吗?我猜唯一的区别是第一个语句应该使用比较器,这需要更多资源。我对这个假设是否正确?请与我分享你的想法。谢谢

【问题讨论】:

  • 我更喜欢第一个,因为它稍微灵活一些(例如,如果规范发生了演变,并且用另一个负值定义了另一种错误),但我认为这只是口味问题因此我认为这个问题不属于...
  • -1 明确定义为错误返回值。所以你应该与 -1 进行比较。如果有 #define FILEIO_ERROR -1,您将与 FILEIO_ERROR 进行比较。
  • 因为定义得这么明确,所以没有防御性编程的情况,你会比较&lt;0
  • 我也更喜欢ret &lt; 0 支票。比较&lt; 0可能会产生更少的机器代码,因为它只需要检查符号,而不必与特定值进行比较。对于readwrite,您还应该将返回值与您想要写入或读取的数据量进行比较,因为函数不需要在一次调用中读取或写入所有数据。
  • @Bodo 我认为 Paul 喜欢 -1 check

标签: c coding-style


【解决方案1】:

Maxine 在他的评论中回答了 IMO 的风格问题。

效率也更好,特别是如果编译器更积极地优化代码

https://godbolt.org/z/MrTdqX

【讨论】:

  • 在像write 系统调用这样的巨大性能瓶颈旁边谈论一些额外移动指令的开销是荒谬的......
【解决方案2】:

首先我会考虑性能,使用 gcc 9.2 (x86-64):

#include <stdio.h>

int main(int argc, char **args, char **env) {
   if (argc < 0) {
       printf("ERROR!");
   }

   return 0;
}

会产生类似的东西

.LC0:
        .string "ERROR!"
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 32
        mov     DWORD PTR [rbp-4], edi
        mov     QWORD PTR [rbp-16], rsi
        mov     QWORD PTR [rbp-24], rdx
        cmp     DWORD PTR [rbp-4], 0
        jns     .L2
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
.L2:
        mov     eax, 0
        leave
        ret

#include <stdio.h>

int main(int argc, char **args, char **env) {
   if (argc == -1) {
       printf("ERROR!");
   }

   return 0;
}

生成

.LC0:
        .string "ERROR!"
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 32
        mov     DWORD PTR [rbp-4], edi
        mov     QWORD PTR [rbp-16], rsi
        mov     QWORD PTR [rbp-24], rdx
        cmp     DWORD PTR [rbp-4], -1
        jne     .L2
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
.L2:
        mov     eax, 0
        leave
        ret

所以在性能方面没有任何差异。有了这个编译器和架构。

其他的都是个人意见。我个人会选择“

【讨论】:

  • 我不太了解 x86,但是您确定 cmp 指令不会根据参数导致不同数量的周期吗?带零的cmp 可以将0 作为8 位参数,但-1 的情况会强制它为32 位。
  • 这种情况下cmp是一样的,区别在于jmp。并且两者都只使用一个 cpu 周期。
  • OT:参数argc永远不会小于1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-01
  • 2013-06-22
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
相关资源
最近更新 更多