C标准库函数与系统调用。哪个是“open()”?

C标准库函数与系统调用。哪个是“open()”?,c,linux,linux-kernel,operating-system,C,Linux,Linux Kernel,Operating System,我知道fopen()在C标准库中,因此我完全可以在C程序中调用fopen()函数。我感到困惑的是为什么我也可以调用open()函数open()应该是一个系统调用,因此它不是标准库中的C函数。由于我成功地调用了open()函数,我是在调用C函数还是系统调用 尽管“系统调用”和“库函数”之间的区别是需要记住的一个有用的区别,但有一个问题是您必须能够以某种方式调用系统调用。一般来说,每个系统调用都存在于C库中——作为一个精简的库函数,它除了向系统调用进行传输外,什么也不做(不管它是如何实现的) 因此,

我知道
fopen()
在C标准库中,因此我完全可以在C程序中调用
fopen()
函数。我感到困惑的是为什么我也可以调用
open()
函数
open()
应该是一个系统调用,因此它不是标准库中的C函数。由于我成功地调用了
open()
函数,我是在调用C函数还是系统调用

尽管“系统调用”和“库函数”之间的区别是需要记住的一个有用的区别,但有一个问题是您必须能够以某种方式调用系统调用。一般来说,每个系统调用都存在于C库中——作为一个精简的库函数,它除了向系统调用进行传输外,什么也不做(不管它是如何实现的)


因此,是的,如果愿意,可以从C代码调用
open()
。(在某个地方,也许在一个名为
fopen.c
的文件中,您的c库的作者可能也调用了它,在
fopen()
的实现中)

EJP对这个问题的评论,并且非常中肯:
open()
既是一个系统调用,也是标准c库中的一个函数
fopen()
是标准C库中的一个函数,它设置一个文件句柄——一种类型为
file
的数据结构,包含可选缓冲之类的附加内容——并在内部调用
open()


为了进一步理解,我将展示
hello.c
,这是一个示例hello-world-在64位x86(x86-64又称AMD64体系结构)上用c为Linux编写的程序,它根本不使用标准c库

首先,
hello.c
需要定义一些带有内联汇编的宏,以便我们能够调用系统调用。这些都非常依赖于体系结构和操作系统,这就是为什么这只适用于x86-64体系结构上的Linux:

/* Freestanding Hello World example in Linux on x86_64/x86.
 * Compile using
 *      gcc -march=x86-64 -mtune=generic -m64 -ffreestanding -nostdlib -nostartfiles hello.c -o hello
*/
#define STDOUT_FILENO 1
#define EXIT_SUCCESS  0

#ifndef __x86_64__
#error  This program only works on x86_64 architecture!
#endif

#define SYS_write    1
#define SYS_exit    60

#define SYSCALL1_NORET(nr, arg1) \
    __asm__ ( "syscall\n\t" \
            : \
            : "a" (nr), "D" (arg1) \
            : "rcx", "r11" )

#define SYSCALL3(retval, nr, arg1, arg2, arg3) \
    __asm__ ( "syscall\n\t" \
            : "=a" (retval) \
            : "a" (nr), "D" (arg1), "S" (arg2), "d" (arg3) \
            : "rcx", "r11" )
文件开头的注释中的
独立的
指的是“独立的执行环境”;当根本没有可用的C库时就是这种情况。例如,Linux内核也是以同样的方式编写的。顺便说一下,我们熟悉的正常环境称为“托管执行环境”

接下来,我们可以围绕系统调用定义两个函数或“包装器”:

static inline void my_exit(int retval)
{
    SYSCALL1_NORET(SYS_exit, retval);
}

static inline int my_write(int fd, const void *data, int len)
{
    int retval;

    if (fd == -1 || !data || len < 0)
        return -1;

    SYSCALL3(retval, SYS_write, fd, data, len);

    if (retval < 0)
        return -1;

    return retval;
}
请注意,上述代码中的任何地方都没有使用
NULL
。这是因为它是由C库定义的宏。相反,我依赖于“逻辑空”:
(!pointer)
为真当且仅当
指针
为零指针时,这就是Linux中所有体系结构上的
null
。我本可以自己定义
NULL
,但我没有,希望有人会注意到它的缺乏

最后,
main()。
\u start
由托管运行时环境提供,它初始化C库数据结构,并进行其他类似的准备工作。我们的示例程序非常简单,我们不需要它,因此我们只需将简单的主程序部分放入
\u start
即可:

void _start(void)
{
    const char *msg = "Hello, world!\n";
    my_write(STDOUT_FILENO, msg, my_strlen(msg));
    my_exit(EXIT_SUCCESS);
}
如果您将以上所有内容放在一起,并使用

gcc -march=x86-64 -mtune=generic -m64 -ffreestanding -nostdlib -nostartfiles hello.c -o hello
根据文件开头的注释,您将得到一个小的(大约2千字节)静态二进制文件,在运行时

./hello
输出

Hello, world!
您可以使用
文件hello
检查文件的内容。如果文件大小非常重要,可以运行
strip hello
删除所有(不需要的)符号,将文件大小进一步减小到大约1.5 KB。(但是,这会降低对象转储的趣味性,因此在执行此操作之前,请先查看下一步。)

我们可以使用
objdump-x hello
检查文件中的部分:

hello:     file format elf64-x86-64
hello
architecture: i386:x86-64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00000000004001e1

Program Header:
    LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21
         filesz 0x00000000000002f0 memsz 0x00000000000002f0 flags r-x
    NOTE off    0x0000000000000120 vaddr 0x0000000000400120 paddr 0x0000000000400120 align 2**2
         filesz 0x0000000000000024 memsz 0x0000000000000024 flags r--
EH_FRAME off    0x000000000000022c vaddr 0x000000000040022c paddr 0x000000000040022c align 2**2
         filesz 0x000000000000002c memsz 0x000000000000002c flags r--
   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .note.gnu.build-id 00000024  0000000000400120  0000000000400120  00000120  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .text         000000d9  0000000000400144  0000000000400144  00000144  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .rodata       0000000f  000000000040021d  000000000040021d  0000021d  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .eh_frame_hdr 0000002c  000000000040022c  000000000040022c  0000022c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .eh_frame     00000098  0000000000400258  0000000000400258  00000258  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .comment      00000034  0000000000000000  0000000000000000  000002f0  2**0
                  CONTENTS, READONLY
SYMBOL TABLE:
0000000000400120 l    d  .note.gnu.build-id     0000000000000000 .note.gnu.build-id
0000000000400144 l    d  .text  0000000000000000 .text
000000000040021d l    d  .rodata        0000000000000000 .rodata
000000000040022c l    d  .eh_frame_hdr  0000000000000000 .eh_frame_hdr
0000000000400258 l    d  .eh_frame      0000000000000000 .eh_frame
0000000000000000 l    d  .comment       0000000000000000 .comment
0000000000000000 l    df *ABS*  0000000000000000 hello.c
0000000000400144 l     F .text  0000000000000016 my_exit
000000000040015a l     F .text  000000000000004e my_write
00000000004001a8 l     F .text  0000000000000039 my_strlen
0000000000000000 l    df *ABS*  0000000000000000 
000000000040022c l       .eh_frame_hdr  0000000000000000 __GNU_EH_FRAME_HDR
00000000004001e1 g     F .text  000000000000003c _start
0000000000601000 g       .eh_frame      0000000000000000 __bss_start
0000000000601000 g       .eh_frame      0000000000000000 _edata
0000000000601000 g       .eh_frame      0000000000000000 _end
.text
部分包含我们的代码和
.rodata
不可变常量;这里,只需<代码>你好,世界字符串文本。其余部分是链接器添加和系统使用的内容。我们可以看到,我们有
f
(hex)=15字节的只读数据,和
d9
(hex)=217字节的代码;文件的其余部分(大约1 KB)是链接器添加的ELF内容,供内核在执行此二进制文件时使用

我们甚至可以通过运行
objdump-d hello
来检查
hello
中包含的实际汇编代码:

hello:     file format elf64-x86-64


Disassembly of section .text:

0000000000400144 <my_exit>:
  400144:       55                      push   %rbp
  400145:       48 89 e5                mov    %rsp,%rbp
  400148:       89 7d fc                mov    %edi,-0x4(%rbp)
  40014b:       b8 3c 00 00 00          mov    $0x3c,%eax
  400150:       8b 55 fc                mov    -0x4(%rbp),%edx
  400153:       89 d7                   mov    %edx,%edi
  400155:       0f 05                   syscall 
  400157:       90                      nop
  400158:       5d                      pop    %rbp
  400159:       c3                      retq   

000000000040015a <my_write>:
  40015a:       55                      push   %rbp
  40015b:       48 89 e5                mov    %rsp,%rbp
  40015e:       89 7d ec                mov    %edi,-0x14(%rbp)
  400161:       48 89 75 e0             mov    %rsi,-0x20(%rbp)
  400165:       89 55 e8                mov    %edx,-0x18(%rbp)
  400168:       83 7d ec ff             cmpl   $0xffffffff,-0x14(%rbp)
  40016c:       74 0d                   je     40017b <my_write+0x21>
  40016e:       48 83 7d e0 00          cmpq   $0x0,-0x20(%rbp)
  400173:       74 06                   je     40017b <my_write+0x21>
  400175:       83 7d e8 00             cmpl   $0x0,-0x18(%rbp)
  400179:       79 07                   jns    400182 <my_write+0x28>
  40017b:       b8 ff ff ff ff          mov    $0xffffffff,%eax
  400180:       eb 24                   jmp    4001a6 <my_write+0x4c>
  400182:       b8 01 00 00 00          mov    $0x1,%eax
  400187:       8b 7d ec                mov    -0x14(%rbp),%edi
  40018a:       48 8b 75 e0             mov    -0x20(%rbp),%rsi
  40018e:       8b 55 e8                mov    -0x18(%rbp),%edx
  400191:       0f 05                   syscall 
  400193:       89 45 fc                mov    %eax,-0x4(%rbp)
  400196:       83 7d fc 00             cmpl   $0x0,-0x4(%rbp)
  40019a:       79 07                   jns    4001a3 <my_write+0x49>
  40019c:       b8 ff ff ff ff          mov    $0xffffffff,%eax
  4001a1:       eb 03                   jmp    4001a6 <my_write+0x4c>
  4001a3:       8b 45 fc                mov    -0x4(%rbp),%eax
  4001a6:       5d                      pop    %rbp
  4001a7:       c3                      retq   

00000000004001a8 <my_strlen>:
  4001a8:       55                      push   %rbp
  4001a9:       48 89 e5                mov    %rsp,%rbp
  4001ac:       48 89 7d e8             mov    %rdi,-0x18(%rbp)
  4001b0:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
  4001b7:       48 83 7d e8 00          cmpq   $0x0,-0x18(%rbp)
  4001bc:       75 0b                   jne    4001c9 <my_strlen+0x21>
  4001be:       b8 ff ff ff ff          mov    $0xffffffff,%eax
  4001c3:       eb 1a                   jmp    4001df <my_strlen+0x37>
  4001c5:       83 45 fc 01             addl   $0x1,-0x4(%rbp)
  4001c9:       48 8b 45 e8             mov    -0x18(%rbp),%rax
  4001cd:       48 8d 50 01             lea    0x1(%rax),%rdx
  4001d1:       48 89 55 e8             mov    %rdx,-0x18(%rbp)
  4001d5:       0f b6 00                movzbl (%rax),%eax
  4001d8:       84 c0                   test   %al,%al
  4001da:       75 e9                   jne    4001c5 <my_strlen+0x1d>
  4001dc:       8b 45 fc                mov    -0x4(%rbp),%eax
  4001df:       5d                      pop    %rbp
  4001e0:       c3                      retq   

00000000004001e1 <_start>:
  4001e1:       55                      push   %rbp
  4001e2:       48 89 e5                mov    %rsp,%rbp
  4001e5:       48 83 ec 10             sub    $0x10,%rsp
  4001e9:       48 c7 45 f8 1d 02 40    movq   $0x40021d,-0x8(%rbp)
  4001f0:       00 
  4001f1:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  4001f5:       48 89 c7                mov    %rax,%rdi
  4001f8:       e8 ab ff ff ff          callq  4001a8 <my_strlen>
  4001fd:       89 c2                   mov    %eax,%edx
  4001ff:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  400203:       48 89 c6                mov    %rax,%rsi
  400206:       bf 01 00 00 00          mov    $0x1,%edi
  40020b:       e8 4a ff ff ff          callq  40015a <my_write>
  400210:       bf 00 00 00 00          mov    $0x0,%edi
  400215:       e8 2a ff ff ff          callq  400144 <my_exit>
  40021a:       90                      nop
  40021b:       c9                      leaveq 
  40021c:       c3                      retq  
你好:文件格式elf64-x86-64
第节的分解。正文:
0000000000400144 :
400144:55%按需付费
400145:48 89 e5 mov%rsp,%rbp
400148:89 7d fc mov%edi,-0x4(%rbp)
40014b:b8 3c 00 mov$0x3c,%eax
400150:8b 55 fc mov-0x4(%rbp),%edx
400153:89 d7 mov%edx,%edi
400155:0f 05系统调用
400157:90无
400158:5d pop%rbp
400159:c3 retq
0000000000 40015A:
40015a:55%推送rbp
40015b:4889E5MOV%rsp%rbp
40015e:89 7d ec mov%edi,-0x14(%rbp)
400161:48 89 75 e0 mov%rsi,-0x20(%rbp)
400165:89 55 e8 mov%edx,-0x18(%rbp)
400168:83 7d ec ff cmpl$0xffffffff,-0x14(%rbp)
40016c:74 0d je 40017b
40016e:48 83 7d e0 00 cmpq$0x0,-0x20(%rbp)
400173:74 06 je 40017b
400175:83 7d e8 00 cmpl$0x0,-0x18(%rbp)
400179:79 07 jns 400182
40017b:b8 ff ff mov$0xffffffff,%eax
400180:eb 24
hello:     file format elf64-x86-64


Disassembly of section .text:

0000000000400144 <my_exit>:
  400144:       55                      push   %rbp
  400145:       48 89 e5                mov    %rsp,%rbp
  400148:       89 7d fc                mov    %edi,-0x4(%rbp)
  40014b:       b8 3c 00 00 00          mov    $0x3c,%eax
  400150:       8b 55 fc                mov    -0x4(%rbp),%edx
  400153:       89 d7                   mov    %edx,%edi
  400155:       0f 05                   syscall 
  400157:       90                      nop
  400158:       5d                      pop    %rbp
  400159:       c3                      retq   

000000000040015a <my_write>:
  40015a:       55                      push   %rbp
  40015b:       48 89 e5                mov    %rsp,%rbp
  40015e:       89 7d ec                mov    %edi,-0x14(%rbp)
  400161:       48 89 75 e0             mov    %rsi,-0x20(%rbp)
  400165:       89 55 e8                mov    %edx,-0x18(%rbp)
  400168:       83 7d ec ff             cmpl   $0xffffffff,-0x14(%rbp)
  40016c:       74 0d                   je     40017b <my_write+0x21>
  40016e:       48 83 7d e0 00          cmpq   $0x0,-0x20(%rbp)
  400173:       74 06                   je     40017b <my_write+0x21>
  400175:       83 7d e8 00             cmpl   $0x0,-0x18(%rbp)
  400179:       79 07                   jns    400182 <my_write+0x28>
  40017b:       b8 ff ff ff ff          mov    $0xffffffff,%eax
  400180:       eb 24                   jmp    4001a6 <my_write+0x4c>
  400182:       b8 01 00 00 00          mov    $0x1,%eax
  400187:       8b 7d ec                mov    -0x14(%rbp),%edi
  40018a:       48 8b 75 e0             mov    -0x20(%rbp),%rsi
  40018e:       8b 55 e8                mov    -0x18(%rbp),%edx
  400191:       0f 05                   syscall 
  400193:       89 45 fc                mov    %eax,-0x4(%rbp)
  400196:       83 7d fc 00             cmpl   $0x0,-0x4(%rbp)
  40019a:       79 07                   jns    4001a3 <my_write+0x49>
  40019c:       b8 ff ff ff ff          mov    $0xffffffff,%eax
  4001a1:       eb 03                   jmp    4001a6 <my_write+0x4c>
  4001a3:       8b 45 fc                mov    -0x4(%rbp),%eax
  4001a6:       5d                      pop    %rbp
  4001a7:       c3                      retq   

00000000004001a8 <my_strlen>:
  4001a8:       55                      push   %rbp
  4001a9:       48 89 e5                mov    %rsp,%rbp
  4001ac:       48 89 7d e8             mov    %rdi,-0x18(%rbp)
  4001b0:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
  4001b7:       48 83 7d e8 00          cmpq   $0x0,-0x18(%rbp)
  4001bc:       75 0b                   jne    4001c9 <my_strlen+0x21>
  4001be:       b8 ff ff ff ff          mov    $0xffffffff,%eax
  4001c3:       eb 1a                   jmp    4001df <my_strlen+0x37>
  4001c5:       83 45 fc 01             addl   $0x1,-0x4(%rbp)
  4001c9:       48 8b 45 e8             mov    -0x18(%rbp),%rax
  4001cd:       48 8d 50 01             lea    0x1(%rax),%rdx
  4001d1:       48 89 55 e8             mov    %rdx,-0x18(%rbp)
  4001d5:       0f b6 00                movzbl (%rax),%eax
  4001d8:       84 c0                   test   %al,%al
  4001da:       75 e9                   jne    4001c5 <my_strlen+0x1d>
  4001dc:       8b 45 fc                mov    -0x4(%rbp),%eax
  4001df:       5d                      pop    %rbp
  4001e0:       c3                      retq   

00000000004001e1 <_start>:
  4001e1:       55                      push   %rbp
  4001e2:       48 89 e5                mov    %rsp,%rbp
  4001e5:       48 83 ec 10             sub    $0x10,%rsp
  4001e9:       48 c7 45 f8 1d 02 40    movq   $0x40021d,-0x8(%rbp)
  4001f0:       00 
  4001f1:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  4001f5:       48 89 c7                mov    %rax,%rdi
  4001f8:       e8 ab ff ff ff          callq  4001a8 <my_strlen>
  4001fd:       89 c2                   mov    %eax,%edx
  4001ff:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  400203:       48 89 c6                mov    %rax,%rsi
  400206:       bf 01 00 00 00          mov    $0x1,%edi
  40020b:       e8 4a ff ff ff          callq  40015a <my_write>
  400210:       bf 00 00 00 00          mov    $0x0,%edi
  400215:       e8 2a ff ff ff          callq  400144 <my_exit>
  40021a:       90                      nop
  40021b:       c9                      leaveq 
  40021c:       c3                      retq