查看< unistd.h>中定义的read()函数代码时遇到麻烦. [英] I have a trouble with looking into the read() function code defined in <unistd.h>

查看:576
本文介绍了查看< unistd.h>中定义的read()函数代码时遇到麻烦.的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我现在通过研究实际的代码实现来尝试了解read(2)函数的工作原理,首先,我尝试了解它在#include头文件中是如何定义的.

I am now trying to understand how read(2) function works by looking into the actual code implementation and first, I try to see how it is defined in #include header file.

在那个文件中,我找到了这个:

In that file, I found this :

ssize_t  read(int, void *, size_t) __DARWIN_ALIAS_C(read);

然后,我用谷歌搜索找到实际的read()函数声明.

And then, I googled to find the actual read() function declaration.

然后

https://github.com/lattera/glibc/blob /master/io/read.c

我发现了这个.在这段代码中,

I found this. In this code,

/* Read NBYTES into BUF from FD.  Return the number read or -1.  */
ssize_t
__libc_read (int fd, void *buf, size_t nbytes)
{
  if (nbytes == 0)
    return 0;
  if (fd < 0)
    {
      __set_errno (EBADF);
      return -1;
    }
  if (buf == NULL)
    {
      __set_errno (EINVAL);
      return -1;
    }

  __set_errno (ENOSYS);
  return -1;
}

这是我的问题.

  1. read之前的__libc_是什么? 为什么需要它?当用户调用read(2)时,如何调用此函数?

  1. what is __libc_ before read ? why is it needed? And when user calls read(2), how this function can be called?

从我的角度来看,这段代码与从文件描述符读取缓冲区无关,而只是处理可能的错误的代码:fd< 0或buff为NULL等. 那么,代码实际在哪里实现了read(2)函数的实际功能?

The way I see it, this code has nothing to do with reading the buffer from file descriptor, rather it has only the code dealing with the possible errors : fd < 0 or buff is NULL , etc. So, where is the code actually implement the actual function of read(2) function?

我在寻找和发现错误的方式或来源吗?

Am I look and found in the wrong way or source?

推荐答案

read(传统上,Unix手册第2节"中定义的所有功能-这就是(2)的含义) 系统调用.这意味着大部分工作是由操作系统内核完成的,而不是由您自己进程中的代码完成的. C库仅包含一个系统调用包装程序,该程序执行一条特殊的指令,该指令将控制权转移到内核.

read (and, traditionally, all of the functions defined in "section 2" of the Unix manual -- that's what that (2) means) is a system call. That means most of the work is done by the operating system kernel, not by code in your own process. The C library only contains a system-call wrapper that executes a special instruction that transfers control to the kernel.

您找到的代码是一个占位符,而不是系统调用包装器.如您所料,它实际上并未实现read.它只能在不具有名为read的系统调用的操作系统的不完整端口中临时使用.您正在查看的C库中没有完整的端口实际使用该代码.相反,他们使用了真正的系统调用包装器.这个C库在构建时会自动生成系统调用包装器,因此我无法链接到实际代码,但是可以向您展示一个示例,说明为系统调用包装器生成的代码是什么样的. (注意:这不是我熟悉的任何操作系统上使用的实际代码.我故意消除了一些复杂性.)

The code you found is a placeholder, not a system-call wrapper. As you surmised, it doesn't actually implement read. It would only ever be used temporarily, in an incomplete port to an operating system that doesn't have a system call named read. None of the complete ports in the C library you are looking at actually use that code. They instead use a real system-call wrapper. This C library automatically generates system-call wrappers at build time, so I can't link to actual code, but I can show you an example of what the generated code for a system-call wrapper might look like. (Note: this is NOT the actual code used on any operating system I am familiar with. I deliberately removed some complications.)

    .text
    .globl read
    .type read, @function
read:
    movl $SYS_read, %eax
    syscall
    testq %rax
    js .error
    ret
.error:
    negl %eax
    movq errno@gottpoff(%rip), %rdx
    movl %eax, %fs:(%rdx)
    movq $-1, %rax
    ret

我故意用x86汇编语言编写了此示例,因为无法从普通C语言获得特殊的syscall指令.某些C库对syscall指令使用汇编插入"扩展名并编写其余部分C语言中的包装语言,但是对于您想要了解的内容,汇编语言是您应该考虑的.

I wrote this example in x86 assembly language on purpose, because there's no way to get the special syscall instruction from plain C. Some C libraries use an "assembly insert" extension for the syscall instruction and write the rest of the wrapper in C, but for what you're trying to understand, the assembly language is what you should think about.

在内核内部,有一个特殊的陷阱处理程序",可从syscall指令接收控制.它查看%eax中的值,看到它是系统调用号 SYS_read(实际数字值可能因操作系统而异),并调用实际实现操作.

Inside the kernel, there's a special "trap handler" that receives control from the syscall instruction. It looks at the value in %eax, sees that it is the system call number SYS_read (the actual numeric value may vary from OS to OS), and calls the code that actually implements the read operation.

系统调用返回后,包装程序将测试是否返回负数.如果是这样,则表明存在错误. (注意:这是消除一些复杂性的地方之一.)它将数字的符号翻转,将其复制到errno(这比mov %eax, errno更为复杂,因为errno

After the system call returns, the wrapper tests whether it returned a negative number. If so, that indicates an error. (Note: this is one of the places where I removed some complications.) It flips the sign of that number, copies it into errno (which is more complicated than just mov %eax, errno because errno is a thread-local variable), and returns −1. Otherwise the value returned is the number of bytes read and it returns that directly.

另一个答案链接到read的实现,但不幸的是,它来自流行但又复杂且难以理解的OS内核.而且我很遗憾地说我没有更好的教学示例可以指出.

The other answer links to an implementation of read but unfortunately it's from an OS kernel that's popular but complicated and difficult to understand. And I regret to say I don't have a better teaching example to point you at.

存在read占位符实现上的__libc_前缀,因为在此C库中,实际上read有三个不同的名称:read__read__libc_read.正如其他答案所指出的那样,在您引用的代码下方有一些特殊的宏,它们将它们全部命名为同一函数的名称.自动为read生成的真实系统调用包装器也将具有所有这些名称.

The __libc_ prefix on the read placeholder implementation is there because there are actually three different names for read in this C library: read, __read, and __libc_read. As the other answer points out, there's some special macros below the code you quoted that arrange for them all to be names for the same function. The auto-generated real system-call wrapper for read will also have all of those names.

这是一种实现命名空间整洁"的技巧,如果您打算实施完全成熟且完全符合标准的C库,则只需担心.简短的版本是C库中有许多函数需要调用read,但是它们不能使用 name read来调用它,因为技术上允许C程序定义一个名为read的函数本身.

This is a hack to achieve "namespace cleanliness", which you only need to worry about if you ever set out to implement a full-fledged and fully standards compliant C library. The short version is that there are many functions in the C library that need to call read, but they cannot use the name read to call it, because a C program is technically allowed to define a function named read itself.

顺便说一句,您需要注意查看属于 same C库的标头和实现代码.您的计算机上似乎有Mac OS上的unistd.h,但是找到的read代码属于GNU C库,这是一个完全不同的实现. read

Incidentally, you need to take care to look at headers and implementation code belonging to the same C library. You appear to have the unistd.h from MacOS on your computer, but the read code you found belongs to the GNU C Library, which is a completely different implementation. The basic declaration of read,

ssize_t read(int, void *, size_t);

由POSIX标准指定,因此两者都相同,但是之后的__DARWIN是MacOS C库的一个怪癖. GNU库中有一个声明具有不同的怪癖:

is specified by the POSIX standard, so it will be the same in both, but the __DARWIN thing after that is a quirk of the MacOS C library. The GNU library has a declaration with different quirks:

extern ssize_t read (int __fd, void *__buf, size_t __nbytes) __wur;

这篇关于查看&lt; unistd.h&gt;中定义的read()函数代码时遇到麻烦.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆