我对流的了解:
从我要告诉我们的操作系统,以便所有程序都可以使用预先连接的I / O和错误流;他们只需要访问/调用。在C中,我们有stdio.h,它是一个头文件,包含使用这些流的各种函数和变量;并建立流和我们的程序之间的联系。流始终存在,但头文件stdio.h为我们的程序提供指令/生成函数以利用它们。这一切都正确吗?在我们必须手动建立这些流之前,这些流是如何建立的?
什么让我感到困惑:
来自维基百科的1 - " Unix的一些突破性进展之一是抽象设备,这使得程序无需知道或关心它正在通信什么类型的设备与" "在Unix之前的大多数操作系统中,程序必须显式连接到适当的输入和输出设备......较旧的操作系统强制程序员采用记录结构,并且经常是非正交数据语义和设备控制。 "
以上是什么意思?
来自维基百科的2 - "另一个Unix突破是默认情况下分别自动将输入和输出关联到终端键盘和终端显示 - 程序(和程序员)绝对没有做任何事情为典型的输入过程输出程序建立输入和输出(除非它选择了不同的范例)。 相比之下,以前的操作系统通常需要一些通常很复杂的作业控制语言来建立联系,或者等同的负担必须由程序来协调。"
你能扩展一下吗?
TLDR-我对流的理解是否正确?您能否更深入地解释在Unix之前如何建立流?最后,有这些问题的人应该通过什么资源来进一步理解流?
答案 0 :(得分:1)
我不太确定你在问什么。
如果你真的对STREAMS,他们的类型,历史等感兴趣,我推荐 这篇来自Linux Journal的文章:LiS: Linux STREAMS。
我们有stdio.h这是一个包含各种函数和变量的头文件 与这些溪流;并在流和我们的程序之间建立联系。
不完全,stdio.h
是一个声明结构(如FILE
),函数(fprintf
)的头文件,
全局变量(stdin
)等,请参阅man 0p stdio.h整体
此头文件的文档。打开,阅读,写作,结束,所有这些
事情是由程序完成的,而不是头文件。这些都是
在你的标准C库中实现,对于glibc来说就是Linux。什么时候
你建立一个程序,你通常将它与glibc链接起来
提供所有这些功能和更多。
看看这个简单的程序:
#include <stdio.h>
int main(void)
{
fprintf(stdin, "Hello world\n");
return 0;
}
在C中,程序的入口点是main
函数,但是这是
不当新程序出现时,操作系统要调用的第一个函数
执行。 objdump
显示:
$ objdump -S printf
printf: file format elf64-x86-64
Disassembly of section .init:
0000000000000550 <_init>:
550: 48 83 ec 08 sub $0x8,%rsp
554: 48 8b 05 85 0a 20 00 mov 0x200a85(%rip),%rax # 200fe0 <__gmon_start__>
55b: 48 85 c0 test %rax,%rax
55e: 74 02 je 562 <_init+0x12>
560: ff d0 callq *%rax
562: 48 83 c4 08 add $0x8,%rsp
566: c3 retq
Disassembly of section .plt:
0000000000000570 <.plt>:
570: ff 35 92 0a 20 00 pushq 0x200a92(%rip) # 201008 <_GLOBAL_OFFSET_TABLE_+0x8>
576: ff 25 94 0a 20 00 jmpq *0x200a94(%rip) # 201010 <_GLOBAL_OFFSET_TABLE_+0x10>
57c: 0f 1f 40 00 nopl 0x0(%rax)
0000000000000580 <fwrite@plt>:
580: ff 25 92 0a 20 00 jmpq *0x200a92(%rip) # 201018 <fwrite@GLIBC_2.2.5>
586: 68 00 00 00 00 pushq $0x0
58b: e9 e0 ff ff ff jmpq 570 <.plt>
Disassembly of section .plt.got:
0000000000000590 <__cxa_finalize@plt>:
590: ff 25 62 0a 20 00 jmpq *0x200a62(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
596: 66 90 xchg %ax,%ax
Disassembly of section .text:
00000000000005a0 <_start>:
5a0: 31 ed xor %ebp,%ebp
5a2: 49 89 d1 mov %rdx,%r9
5a5: 5e pop %rsi
5a6: 48 89 e2 mov %rsp,%rdx
5a9: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
5ad: 50 push %rax
5ae: 54 push %rsp
5af: 4c 8d 05 ba 01 00 00 lea 0x1ba(%rip),%r8 # 770 <__libc_csu_fini>
5b6: 48 8d 0d 43 01 00 00 lea 0x143(%rip),%rcx # 700 <__libc_csu_init>
5bd: 48 8d 3d 0c 01 00 00 lea 0x10c(%rip),%rdi # 6d0 <main>
5c4: ff 15 0e 0a 20 00 callq *0x200a0e(%rip) # 200fd8 <__libc_start_main@GLIBC_2.2.5>
5ca: f4 hlt
5cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
5d0: 48 8d 3d 59 0a 20 00 lea 0x200a59(%rip),%rdi # 201030 <stdin@@GLIBC_2.2.5>
5d7: 48 8d 05 59 0a 20 00 lea 0x200a59(%rip),%rax # 201037 <__TMC_END__+0x7>
5de: 55 push %rbp
5df: 48 29 f8 sub %rdi,%rax
5e2: 48 89 e5 mov %rsp,%rbp
5e5: 48 83 f8 0e cmp $0xe,%rax
5e9: 76 15 jbe 600 <_start+0x60>
5eb: 48 8b 05 de 09 20 00 mov 0x2009de(%rip),%rax # 200fd0 <_ITM_deregisterTMCloneTable>
5f2: 48 85 c0 test %rax,%rax
5f5: 74 09 je 600 <_start+0x60>
5f7: 5d pop %rbp
5f8: ff e0 jmpq *%rax
5fa: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
600: 5d pop %rbp
601: c3 retq
602: 0f 1f 40 00 nopl 0x0(%rax)
606: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
60d: 00 00 00
610: 48 8d 3d 19 0a 20 00 lea 0x200a19(%rip),%rdi # 201030 <stdin@@GLIBC_2.2.5>
617: 48 8d 35 12 0a 20 00 lea 0x200a12(%rip),%rsi # 201030 <stdin@@GLIBC_2.2.5>
61e: 55 push %rbp
61f: 48 29 fe sub %rdi,%rsi
622: 48 89 e5 mov %rsp,%rbp
625: 48 c1 fe 03 sar $0x3,%rsi
629: 48 89 f0 mov %rsi,%rax
62c: 48 c1 e8 3f shr $0x3f,%rax
630: 48 01 c6 add %rax,%rsi
633: 48 d1 fe sar %rsi
636: 74 18 je 650 <_start+0xb0>
638: 48 8b 05 b1 09 20 00 mov 0x2009b1(%rip),%rax # 200ff0 <_ITM_registerTMCloneTable>
63f: 48 85 c0 test %rax,%rax
642: 74 0c je 650 <_start+0xb0>
644: 5d pop %rbp
645: ff e0 jmpq *%rax
647: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
64e: 00 00
650: 5d pop %rbp
651: c3 retq
652: 0f 1f 40 00 nopl 0x0(%rax)
656: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
65d: 00 00 00
660: 80 3d d1 09 20 00 00 cmpb $0x0,0x2009d1(%rip) # 201038 <__TMC_END__+0x8>
667: 75 27 jne 690 <_start+0xf0>
669: 48 83 3d 87 09 20 00 cmpq $0x0,0x200987(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
670: 00
671: 55 push %rbp
672: 48 89 e5 mov %rsp,%rbp
675: 74 0c je 683 <_start+0xe3>
677: 48 8b 3d aa 09 20 00 mov 0x2009aa(%rip),%rdi # 201028 <__dso_handle>
67e: e8 0d ff ff ff callq 590 <__cxa_finalize@plt>
683: e8 48 ff ff ff callq 5d0 <_start+0x30>
688: 5d pop %rbp
689: c6 05 a8 09 20 00 01 movb $0x1,0x2009a8(%rip) # 201038 <__TMC_END__+0x8>
690: f3 c3 repz retq
692: 0f 1f 40 00 nopl 0x0(%rax)
696: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
69d: 00 00 00
6a0: 48 8d 3d 41 07 20 00 lea 0x200741(%rip),%rdi # 200de8 <__init_array_end+0x8>
6a7: 48 83 3f 00 cmpq $0x0,(%rdi)
6ab: 75 0b jne 6b8 <_start+0x118>
6ad: e9 5e ff ff ff jmpq 610 <_start+0x70>
6b2: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
6b8: 48 8b 05 29 09 20 00 mov 0x200929(%rip),%rax # 200fe8 <_Jv_RegisterClasses>
6bf: 48 85 c0 test %rax,%rax
6c2: 74 e9 je 6ad <_start+0x10d>
6c4: 55 push %rbp
6c5: 48 89 e5 mov %rsp,%rbp
6c8: ff d0 callq *%rax
6ca: 5d pop %rbp
6cb: e9 40 ff ff ff jmpq 610 <_start+0x70>
00000000000006d0 <main>:
#include <stdio.h>
int main(void)
{
6d0: 55 push %rbp
6d1: 48 89 e5 mov %rsp,%rbp
fprintf(stdin, "Hello world\n");
6d4: 48 8b 05 55 09 20 00 mov 0x200955(%rip),%rax # 201030 <stdin@@GLIBC_2.2.5>
6db: 48 89 c1 mov %rax,%rcx
6de: ba 0c 00 00 00 mov $0xc,%edx
6e3: be 01 00 00 00 mov $0x1,%esi
6e8: 48 8d 3d 95 00 00 00 lea 0x95(%rip),%rdi # 784 <_IO_stdin_used+0x4>
6ef: e8 8c fe ff ff callq 580 <fwrite@plt>
return 0;
6f4: b8 00 00 00 00 mov $0x0,%eax
}
6f9: 5d pop %rbp
6fa: c3 retq
6fb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
0000000000000700 <__libc_csu_init>:
700: 41 57 push %r15
702: 41 56 push %r14
704: 41 89 ff mov %edi,%r15d
707: 41 55 push %r13
709: 41 54 push %r12
70b: 4c 8d 25 c6 06 20 00 lea 0x2006c6(%rip),%r12 # 200dd8 <__init_array_start>
712: 55 push %rbp
713: 48 8d 2d c6 06 20 00 lea 0x2006c6(%rip),%rbp # 200de0 <__init_array_end>
71a: 53 push %rbx
71b: 49 89 f6 mov %rsi,%r14
71e: 49 89 d5 mov %rdx,%r13
721: 4c 29 e5 sub %r12,%rbp
724: 48 83 ec 08 sub $0x8,%rsp
728: 48 c1 fd 03 sar $0x3,%rbp
72c: e8 1f fe ff ff callq 550 <_init>
731: 48 85 ed test %rbp,%rbp
734: 74 20 je 756 <__libc_csu_init+0x56>
736: 31 db xor %ebx,%ebx
738: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
73f: 00
740: 4c 89 ea mov %r13,%rdx
743: 4c 89 f6 mov %r14,%rsi
746: 44 89 ff mov %r15d,%edi
749: 41 ff 14 dc callq *(%r12,%rbx,8)
74d: 48 83 c3 01 add $0x1,%rbx
751: 48 39 dd cmp %rbx,%rbp
754: 75 ea jne 740 <__libc_csu_init+0x40>
756: 48 83 c4 08 add $0x8,%rsp
75a: 5b pop %rbx
75b: 5d pop %rbp
75c: 41 5c pop %r12
75e: 41 5d pop %r13
760: 41 5e pop %r14
762: 41 5f pop %r15
764: c3 retq
765: 90 nop
766: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
76d: 00 00 00
0000000000000770 <__libc_csu_fini>:
770: f3 c3 repz retq
Disassembly of section .fini:
0000000000000774 <_fini>:
774: 48 83 ec 08 sub $0x8,%rsp
778: 48 83 c4 08 add $0x8,%rsp
77c: c3 retq
在main
之前有一些函数(程序员的入口点
透视),如_start
(从操作系统的角度来看)。
此函数负责初始化main
之前所需的内容。到底在哪里
像stdin
这样的流被初始化,我不知道,我找不到任何可靠的
有关此事的消息来源。
在这个answer中,有一个很好的链接可以更详细地解释这一点: How the heck do we get to main()?
来自维基百科 - “Unix的一些突破性进展是抽象设备,它已被删除 需要一个程序来了解或关心它与之通信的设备类型“ “在Unix之前的大多数操作系统中,程序必须明确连接到 适当的输入和输出设备......强制程序员使用较旧的操作系统 记录结构和频繁的非正交数据语义和设备控制。“
我认为文章试图说的是,在类Unix系统中,作为程序员你 不必担心通信背后的设备,因为在Unix中 “everything is a file”。你可以做到
fopen("/some/file.txt", "r");
fopen("/dev/sda1", "r"); // if you have reading access
fopen("/dev/some_device", "r");
你的程序不需要知道/dev/sda1
是a的第一个分区
SCSI总线中的硬盘驱动器,或/dev/some_device
实际上是PCI设备。
似乎在Unix之前,作为程序员,你必须知道哪种类型 你想和之交谈的设备。但我太年轻了,无法与这个时代联系,我不是 甚至出生,所以我可能完全错了。
也来自维基百科 - “另一个Unix突破是自动关联输入和输出 到终端键盘和终端显示,默认情况下 - 程序 (和程序员)完全没有为a建立输入和输出 典型的输入 - 输出 - 输出程序(除非它选择了不同的范例)。 相比之下,以前的操作系统通常需要一些通常很复杂的工作 控制语言建立联系,或相当的负担必须 由该计划精心策划。“
这就是我在答案开头时所说的。
最后,有这些问题的人应该通过什么资源来进一步理解他们的流?
我总是非常重视过去的技术,这当然不是坏事 想知道过去的工作方式。如果你真的感兴趣的话 场景,我认为LiS: Linux STREAMS文章非常有趣。
从程序员的角度来看,您需要知道的是stdin
,stdout
,
在您的计划开始时为您提供stderr
您必须使用stdio.h
中的函数来访问它们。