V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
lolcat
V2EX  ›  Linux

read 函数第三个参数是不带符号的整型,返回值是带符号的整型,这么设计感觉不合理啊

  •  
  •   lolcat · 2020-05-12 10:35:40 +08:00 · 3200 次点击
    这是一个创建于 1682 天前的主题,其中的信息可能已经有所发展或是发生改变。

    ssize_t read(int fd, void *buf, size_t count);

    返回值是带符号的 ssize_t,代表此次 read 实际读取的字节数。 第三个参数是不带符号的 size_t,代表此次希望读取的字节数。

    显然 size_t 的最大值是 ssize_t 的两倍,可以假设 size_t 的最大值是 255,ssize_t 的最大值是 127,如果在 read 函数的第三个参数中填 200,是不是代表 read 最多只能读取 127 字节数据?如果是的话,感觉 linux 这么设计有点不合理啊,感觉和所见即所得原则有点违背。

    第 1 条附言  ·  2020-05-12 21:20:20 +08:00
    我想表达的是,一个人只能吃一碗饭,却用一个盆去打饭,虽然也可以,但感觉有点奇怪。
    15 条回复    2020-05-13 03:17:59 +08:00
    guyeu
        1
    guyeu  
       2020-05-12 10:57:01 +08:00
    可能是 count 不允许小于 0,但是返回值有可能小于 0,才这么设计 API 吧
    amaranthf
        2
    amaranthf  
       2020-05-12 10:57:15 +08:00   ❤️ 2
    我觉得应该是有两个考量吧:
    一是返回的时候需要一个值用来表示“错误”,而 0 和正整数都有其他含义,为了避免再增加一个参数,就只能用负数表示了;
    二是设计者可能认为你没有一下子读出 2GB 以上的东西的需求——在 32 位机器上这的确是很难的事情,而 64 位的话 ssize_t 也要变大,就更不可能了。
    事实上在 Linux 下面,read 函数内部是限制了一次读取的最大值的,这个值是 0x7ffff000 (包括 64 位下面)。
    CRVV
        3
    CRVV  
       2020-05-12 12:27:40 +08:00
    read(2) 里有说,
    On success, the number of bytes read is returned (zero indicates end of file)
    On error, -1 is returned, and errno is set appropriately.
    那么返回值必须是有符号的

    另外,read 不保证把传进去的 buf 读满,给一个长度 200 的,只读取 127 字节或者只读取 1 字节都是可以的。

    这个 API 也不是 Linux 设计的,这是 POSIX 里定义的 API,Linux 只能照着实现。
    momocraft
        4
    momocraft  
       2020-05-12 12:45:30 +08:00
    "所见即所得原则" 是什么
    leido
        5
    leido  
       2020-05-12 12:47:43 +08:00
    上古 API,能用就行
    AngryMagikarp
        6
    AngryMagikarp  
       2020-05-12 12:54:36 +08:00
    首先 read 本来就不保证能读取到 count 长度的数据,你传再大的 count,他也可以返回一个字节给你。

    其次,哪怕是一次读取有符号整数最大值的数据,也足够了。即使把返回值换成无符号数,你依然可以说,一次性最多读取无符号数的最大值,完全不够啊,杠精天下第一。
    AngryMagikarp
        7
    AngryMagikarp  
       2020-05-12 13:00:26 +08:00
    According to POSIX.1, if count is greater than SSIZE_MAX, the result is implementation-defined; see NOTES for the upper limit on Linux.

    On Linux, read() (and similar system calls) will transfer at most 0x7ffff000 (2,147,479,552) bytes, returning the number of bytes actually transferred. (This is true on both 32-bit and 64-bit systems.)

    在 Linux 上,即使 64 位系统中也最多一次性读取 2G 数据。反正我读取文件或者网络 IO,一般情况下都是 4096 读。
    AngryMagikarp
        8
    AngryMagikarp  
       2020-05-12 13:08:01 +08:00
    另外什么是所见即所得。我用 linux 这么久了,早期还看了很多关于 unix 历史和设计理念的书,从来没见过所见即所得这个概念。我只知道富文本编辑器所见即所得。
    qakito
        9
    qakito  
       2020-05-12 13:16:50 +08:00
    1. 常规场景中使用到的 buffer 远小于 SSIZE_MAX
    如果你的代码里出现了一个 4G 大小的 buffer,更应该审视代码的合理性
    2. API 只需保证你的 buffer 不溢出,不保证你的 buffer 全部塞满
    AngryMagikarp
        10
    AngryMagikarp  
       2020-05-12 13:22:11 +08:00
    系统调用只是提供一些基础功能,并不是为了某些特定场景设计的。实际中如果真的要一次性读取大数据,也可以简单地分多次读取实现。

    Go 语言中 ioutil.ReadAll 函数甚至是每次读取 512 长度,通过循环读取实现的。
    nightwitch
        11
    nightwitch  
       2020-05-12 13:26:54 +08:00
    posix 标准诞生的时候硬件规格限制了不可能一次性读满 ssize_t 的
    Unix/Linux 没有所见即所得这个原则
    weyou
        12
    weyou  
       2020-05-12 19:39:55 +08:00 via Android
    读取多少字节函数自己能决定,所以只要函数自己保证返回值不会引起符号溢出就可以了
    lolcat
        13
    lolcat  
    OP
       2020-05-12 21:19:13 +08:00
    @AngryMagikarp 没看懂帖子吧,我说实际读取的最大字节数只有计划读取的最大字节数的一半,就像我只能吃一碗饭,我却用一个盆打饭,没想表达一次读取的字节数太少,怎么就杠了?
    AngryMagikarp
        14
    AngryMagikarp  
       2020-05-12 22:29:15 +08:00
    @lolcat 这里的 count 和返回值并没有对应关系,他们之间的唯一关系是 count 大于等于返回值,那么 count 比返回值大多少是无所谓的。
    真正和 count 有关系的其实是 buf,count 表示的就是 buf 的大小,buf 的大小一定不会是负的。而且你去看看 malloc 的参数也是 size_t,这两者是统一的。
    msg7086
        15
    msg7086  
       2020-05-13 03:17:59 +08:00
    C 语言里的数值就是这么设计的,有符号的就是无符号的一半。
    你可以理解成 count 只能输入 0-2147479552,而返回值只能是-1-2147479552,范围是基本一致的。

    你说
    「可以假设 size_t 的最大值是 255,ssize_t 的最大值是 127,如果在 read 函数的第三个参数中填 200 」
    而其实 read 函数规定你只能填 120,所以 255 也好 200 也好 127 也好都是无关的。

    顺便 size_t 在 64 位下最大可以取值 18446744073709551615,即使这样,read 也只会读取 2147479552 字节。
    你用一个宇宙去打饭,也只给你一小碗。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3372 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 00:45 · PVG 08:45 · LAX 16:45 · JFK 19:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.