系统
谈谈硬件
冯·诺依曼体系结构(von Neumann architecture)
冯·诺依曼体系结构的特点和原则包括:
-
存储程序:冯·诺依曼体系结构将指令和数据存储在同一个存储器(称为存储器或内存)中,并以相同的方式进行处理。这使得程序可以被存储,读取和执行。
-
指令执行:计算机按照存储器中存储的指令序列来执行操作。每条指令包含一个操作码和零个或多个操作数,计算机根据指令的操作码执行不同的操作。
-
顺序执行:计算机按照指令在存储器中的顺序逐条执行。程序计数器(Program Counter,PC)记录了当前正在执行的指令的地址,执行完一条指令后自动转到下一条指令。
-
单一总线:冯·诺依曼体系结构使用单一总线来连接主要组件,如中央处理器、内存和输入/输出设备。总线用于传输指令、数据和控制信号。
-
存储器访问:指令和数据通过存储器地址进行访问。存储器地址表示存储器中每个单元的位置,可以通过读取或写入该地址来访问数据和指令。
硬件
包括电子器件、电路板、存储设备、处理器等。它是计算机系统的实体部分,用于执行和处理数据的操作。
-
中央处理器(CPU):中央处理器是计算机系统的核心部分,负责执行指令、处理数据和控制计算机的操作。它包含算术逻辑单元(ALU)、控制单元(CU)和寄存器。
-
存储设备(外存与内存):存储设备用于保存和检索数据,包括内存、硬盘驱动器(HDD)、固态驱动器(SSD)和闪存等。内存用于临时存储数据和程序,而硬盘驱动器和固态驱动器则用于长期存储数据。
-
输入设备:输入设备用于向计算机系统输入数据和命令,包括键盘、鼠标、扫描仪和触摸屏等。
-
输出设备:输出设备用于向用户显示计算机处理的结果,包括显示器、打印机、音频设备和投影仪等。
-
主板:主板是计算机的主要电路板,用于连接和支持各种硬件组件的通讯和协同工作。
-
扩展卡:扩展卡是硬件组件,用于增加计算机的功能和性能。常见的扩展卡包括显卡、声卡、网卡和USB扩展卡等。
CPU
CPU(Central Processing Unit,即中央处理器)通常由以下几个主要部分组成:
-
控制单元(Control Unit,CU):控制单元负责指令的解析和执行,控制数据流的传递和操作的顺序。它从内存中获取指令,解码指令,并将相应的控制信号发送给其他部件。
-
算术逻辑单元(Arithmetic Logic Unit,ALU):算术逻辑单元执行各种算术和逻辑操作,如加法、减法、逻辑与、逻辑或等。它执行算术运算(如加减乘除)和逻辑运算(如与或非)。
-
寄存器:寄存器是CPU中用于存储和处理数据的高速存储器,其中包括通用寄存器、程序计数器(Program Counter,PC)和指令寄存器(Instruction Register,IR)等。通用寄存器用于暂时存储和处理数据,而程序计数器用于保存当前正在执行的指令的地址,指令寄存器存储当前正在执行的指令。
CPU的工作方式可以总结为以下几个步骤:
-
取指令(Fetch):控制单元从内存中获取指令,并将其存储在指令寄存器中。
-
解码指令(Decode):控制单元解码指令,确定要执行的指令类型和相应的操作。
-
执行指令(Execute):ALU执行指令所需的算术和逻辑操作,并将结果存储在寄存器中。
-
访问内存(Memory Access):CPU可能需要访问内存来获取或存储数据。这包括从内存中读取数据或将结果写回内存。
-
写回(Writeback):如果需要,CPU将结果写回内存或寄存器中,以便后续的指令或操作使用。
这些步骤循环进行,直到所有的指令被执行完毕。
CPU的性能取决于多个因素,如处理器的时钟频率、核心数量、缓存大小和架构设计等。
外存 磁盘 SSD
外存
外存(External storage)指的是计算机系统中用于存储数据和程序的物理介质,通常位于计算机主机外部,并通过接口与主机相连。外存设备通常具有大容量、高速度和可靠性等特点,可以提供持久性的数据存储和访问。
常见的磁盘类型包括硬盘驱动器(HDD)、固态硬盘(SSD)、移动硬盘、U盘等。
除了磁盘之外,外存还包括其他类型的存储介质,如光盘、磁带、SD卡、Memory Stick等。这些介质通常用于存储数据和应用程序,并且可以通过不同的接口连接到计算机主机。
外存设备通常用于存储操作系统、应用程序、数据和文件等。操作系统视他们为持久性存储设备,通过文件系统来管理文件和目录,并提供良好的文件访问接口。外存设备还可以用于备份和恢复数据,以便在计算机系统出现故障时保护数据。
磁盘 SSD
磁盘(Disk)是计算机系统中常见的外部存储设备之一,用于永久存储和检索数据。磁盘通常由一个或多个盘片和一个或多个磁臂(磁头)组成,每个盘片都有两个表面。每个表面都具有用于存储数据的磁性涂层。
磁盘的工作原理是通过磁性材料的磁性特性来存储数据。数据以磁性在磁性涂层上编码,磁头通过读/写操作与磁性涂层交互。当读取数据时,磁头感应磁性涂层中的磁场,将其转换为电信号,并传送给计算机。当写入数据时,磁头通过施加磁场改变磁性涂层的状态,从而写入数据。
磁盘是一种机械设备,其读写操作需要搬动磁头和旋转盘片等机械运动。因此,与固态存储器相比,磁盘的读写速度相对较慢。此外,磁盘也容易受到物理冲击和损坏的影响,应该小心对待。
磁盘的物理结构:
磁盘通常由以下几个基本部分组成:物理上是圆形的
-
盘片(Platters):磁盘通常包含一个或多个盘片,也称为碟片。盘片是圆形的,由金属或玻璃等材料制成,上面涂有一个磁性涂层。每个盘片都有两个表面,可以用来存储数据。
-
磁臂(Actuator Arm):磁盘上的磁臂是一个机械臂,类似于唱片机中的手臂。它通过驱动装置在磁盘上进行旋转,并具有一个磁头。
-
磁头(Read/Write Head):磁盘上的磁头是磁臂的一部分,负责读取和写入数据。每个盘片有两个表面,每个表面上都有一个独立的磁头。
-
磁道(Track):盘片上被划分为同心圆的环状轨道,称为磁道。每个磁道对应一个特定的物理位置,用于存储数据。
-
扇区(Sector):磁盘上的每个磁道被划分为多个扇区,通常是512字节或4KB。扇区是存储磁盘上最小数据单元的地方。
-
控制器(Controller):磁盘的控制器负责管理读取和写入磁头的操作,以及处理数据传输和磁盘操作的相关任务。
磁盘的逻辑结构:
磁盘的逻辑结构指的是磁盘上的数据存储在物理介质上的一种组织方式。磁盘逻辑结构的主要目的是为了方便用户和操作系统对磁盘上的数据进行管理,而不是反映磁盘物理结构的真实情况。
磁盘的逻辑结构主要包括以下几个部分:逻辑上是线性的
-
分区(Partition):磁盘上的分区是指将磁盘划分为多个逻辑块,每个逻辑块都独立管理,可以独立格式化、分配空间和安装操作系统等。在磁盘上,分区通常以一定的大小和位置进行划分,以满足不同的需求。
-
分区表(Partition Table):分区表是磁盘上存储分区信息的地方,记录了磁盘上分区的数量、大小、位置和类型等信息。分区表通常位于磁盘的启动区(Boot Sector)中,是操作系统在启动时读取的重要信息之一。
-
文件系统(File System):文件系统是管理磁盘上文件和目录的组织和存储方式。不同的文件系统具有不同的特点和性能,如FAT、NTFS、EXT4等。文件系统负责管理磁盘上的空闲空间、分配文件和目录等,以确保数据的完整性和安全性。
-
(Files)和目录(Directories):文件系统将磁盘上的数据组织成文件和目录,以便用户和操作系统进行访问和管理。文件和目录通常具有自己的属性、权限和时间戳等信息,可以进行创建、删除、修改等操作。
写入磁盘的过程
当数据写入磁盘时,涉及以下物理过程:
-
传输数据到磁盘控制器:首先,计算机通过总线将待写入的数据传输到磁盘控制器。通常情况下,数据会暂时存储在计算机的内存中,等待写入磁盘。
-
定位磁头:在数据写入之前,磁头需要准确定位到要写入数据的磁道。磁盘控制器会控制磁头的移动,将其定位到正确的磁道位置上。
-
调整磁头高度:磁头在定位到目标磁道后,需要根据盘片的高度进行微调以保证与磁性涂层的正确接触。
-
磁性状态改变:一旦磁头定位到正确的位置,磁盘控制器会发送电流到磁头,以改变磁性涂层中对应位置的磁性状态。写入数据时,通常使用磁化的方式来编码数据。
-
磁性状态稳定下来:数据写入后,磁性涂层中的磁性状态会逐渐稳定下来,并保持在被写入的状态。这个过程需要一定的时间,保证数据被正确地存储在磁性涂层上。
-
非易失性存储:与易失性存储器(如RAM)不同,磁盘是一种非易失性存储介质,它能在断电或重新启动之后保持数据的存储状态。
值得注意的是,上述过程是简化的描述,实际的写入过程可能因磁盘技术和具体实现方式的不同而有所差异。此外,现代的磁盘驱动器在写入数据时通常会采用更加复杂的技术和算法来提高写入速度和数据可靠性。
内存 三级缓存 寄存器
内存
分为随机访问内存(Random Access Memory,简称RAM)和只读存储器(Read-Only Memory,简称ROM)两种类型。
RAM是一种可以随时读写和更改的存储器,因此它主要用于存储临时数据和程序。
ROM是一种只能读取数据的存储器,通常用于存储计算机的操作系统、固件和程序等重要数据,这些数据在计算机启动时就已经被加载到其中了。
三级缓存
三级缓存(Level 3 Cache,L3 Cache)是计算机中的一级高速缓存(Cache)层次结构,用于提高数据访问速度和性能。它位于处理器(CPU)和主存储器(Main Memory)之间,作为处理器缓存和主存储器之间的中介。
三级缓存通常是多核处理器(Multi-core Processor)中的共享缓存,被多个处理核心共享。每个处理核心都可以使用三级缓存中的数据,这样可以减少对主存储器的访问次数,从而提高计算机系统的性能。
三级缓存相对于一级缓存(L1 Cache)和二级缓存(L2 Cache)通常具有更大的容量,但访问速度相对较慢。这是因为三级缓存位于较远的位置,与CPU之间有更长的距离和延迟。
三级缓存的存在有助于减少处理器对主存储器的访问次数。当处理器需要某个数据时,首先会查找一级缓存(非常快但容量较小),如果数据不在一级缓存中,会继续在二级缓存中查找。如果数据不在二级缓存中,才会访问三级缓存。如果数据在三级缓存中找到,则将其加载到一级或二级缓存中,以供后续快速访问。
三级缓存的大小会因处理器设计和制造商而有所不同,一般在几兆字节或更多。较大的三级缓存可以容纳更多的数据,减少对主存储器的访问频率,提高计算机系统的整体性能和响应速度。
寄存器
寄存器(Register)是计算机内部的高速存储器,位于中央处理器(CPU)内部。它用于临时存储和处理指令、数据和地址等信息,是CPU在执行指令和计算运算时的重要组成部分。
寄存器具有非常快速的读写速度,远高于内存访问速度,因此在计算机内部被广泛应用。它在指令执行期间提供了临时存储位置,供CPU进行操作和计算。寄存器由一组二进制位(位数)组成,每个位存储一个二进制值(0或1)。
寄存器可分为多种类型,包括:
-
通用寄存器(General Purpose Registers):通用寄存器是最常用的寄存器类型,用于存储指令和数据。它们具有多个,并且在程序执行期间可用于存储各种数据和临时结果。通用寄存器的数量和位数取决于CPU的设计,常见的包括A、B、C等。
-
程序计数器(Program Counter):程序计数器是一个特殊的寄存器,用于存储当前正在执行的指令的地址。它指向下一条将要执行的指令的地址,并在指令执行后自动递增。
-
栈指针(Stack Pointer):栈指针是一个特殊的寄存器,用于指向栈的顶部,即栈中最新压入的数据的位置。它在函数调用和返回、局部变量和临时数据的存储中起着关键作用。
-
标志寄存器(Flag Register):标志寄存器用于存储特定标志位,记录CPU运算的结果或状态信息,如进位标志、零标志、溢出标志等。这些标志位被用于控制和判断条件分支、条件跳转等操作。
寄存器的数量、位数和命名方式根据不同的CPU架构和设计而异。较新的处理器通常具有更多和更大的寄存器,以提高计算机的处理能力和性能。寄存器的使用和管理由硬件电路和指令集体系结构(Instruction Set Architecture)控制。
内存与外存的区别
内存(Memory)和外存(Storage)
-
容量和成本:内存的容量通常较小,通常以GB(Gigabytes)为单位,而外存的容量往往更大,以TB(Terabytes)为单位。内存价格相对较高,而外存价格通常更低。
-
速度:内存的访问速度非常快,可以达到纳秒级别,而外存的访问速度相对较慢,一般以毫秒或更长时间为单位。这使得CPU能够更快地访问和处理内存中的数据。
-
持久性:内存是易失性存储器,也就是说,一旦断电或重新启动,内存中的数据将丢失。而外存是非易失性存储器,它可以长期保持数据,并在断电后仍然保留数据。
-
访问方式:内存可以通过内存地址直接访问数据,而外存通常需要通过文件系统或其他I/O接口来管理和访问数据。
-
使用方式:内存主要用于存储正在运行的程序、操作系统和临时数据,以提供快速的读写访问。外存则用于长期存储数据、程序和文件等,以及进行备份和持久性存储。
磁盘与物理内交互的基本单位
磁盘和内存交互的基本单位是块(Block)。块是磁盘和内存之间进行数据交换的基本单位,大小通常由操作系统和硬件设备决定(4kb)。在操作系统中,块的大小通常用于对磁盘进行分区和格式化,以及在内存管理中进行数据块的读写操作。当程序需要从磁盘读取或写入数据时,通常会以块为单位进行操作,这样可以提高磁盘的读写效率。在内存和磁盘之间的数据交换过程中,块是作为传输单位来进行的。在页表的数据结构中,页帧对应磁盘,页框对应物理内存。(页表在进程地址空间介绍)
问题:那么假如访问100b的内容,也会交互4kb的内容到物理内存,不会浪费空间吗
磁盘访问速度远低于内存访问速度,因此在设计系统时,会采用一种称为“缓存”(Caching)的机制来优化这种速度差异。
当系统需要访问100字节(B)的数据时,并不会直接从磁盘加载4千字节(KB)的数据到物理内存中。相反,操作系统会根据缓存机制,尝试从物理内存中已经加载的块中查找这100字节的数据。如果数据已经在内存中,那么就不会发生磁和内存之间的交互。这种情况下,内存访问是非常高效的。
如果100字节的数据不在物理内存中的缓存块中,那么系统会执行一个称为“缓存未命中”(Cache Miss)的操作。在这种情况下,操作系统会从磁盘加载包含这100字节数据的块到内存中的缓存位置。这个块的大小通常远大于100字节,但这样做最大限度地减少磁盘访问次数,因为一旦整个块被加载到内存中,就可以为后续可能访问的任何数据提供快速的访问速度。
这体现了系统的局部性原理
局部性原理(Locality Principle),也被称为存储局部性(Temporal Locality)和空间局部性(Spatial Locality)。
局部性原理指出,在程序的执行过程中,有许多数据和指令会被重复访问。这包括时间上的局部性,即相同的数据和指令在短时间内被多次访问;以及空间上的局部性,即相邻的数据和指令在空间上也很可能被频繁访问。
缓存机制利用了这种局部性原理,通过将最常用的数据和指令存储在快速访问的内存中,从而提高系统的性能。当系统发生缓存未命中时,通过一次较慢的磁盘访问加载一个较大的块到缓存中,以期望未来对该块中的其他数据的访问能够命中缓存。这样,连续的访问将会在内存中快速进行,从而减少了对慢速磁盘的访问次数。
内存访问磁盘的介质:寄存器
访问磁盘时,通常需要使用寄存器作为数据传输和缓存的介质。内存和磁盘之间的数据传输需要通过CPU来完成,其中寄存器的作用非常重要。
当应用程序需要从磁盘读取数据时,CPU会将磁盘上的数据加载到寄存器中,并通过总线传输将数据从磁盘转移到内存中。类似地,当应用程序需要将数据写入磁盘时,CPU会将数据从内存加载到寄存器,然后通过总线传输将数据写入磁盘。
参与的寄存器
涉及到的寄存器主要包括以下几种:
-
数据寄存器(Data Registers):这些寄存器用于存储从磁盘读取或写入的数据。例如,x8架构中的EAX、EBX、ECX和EDX寄存器可以用来存储数据。
-
地址寄存器(Address Registers):这些寄存器用于存储内存地址或磁盘地址。例如,x86架构中的EIP(指令指针)寄存器用于存储下一条指令的内存地址,而磁盘I/O操作时,会使用特定的地址寄存器来指定磁盘上的位置。
-
控制寄存器(Control Registers):这些寄存器用于控制内存和磁盘操作。例如,x86架构中的CR0和CR3寄存器包含控制flush the cache(缓存刷新)和访问内存映射寄存器(如页表)的位。
-
状态寄存器(Status Registers):这些寄存器用于操作的结果和状态信息。例如,x86架构中的EFLAGS寄存器包含了条件码和标志位,用于表示运算的结果和状态。
-
指令寄存器(Instruction Registers):这些存器用于存储当前正在执行的指令。例如,x86架构中的EIP寄存器用于存储下一条要执行的指令的地址。
当CPU执行内存访问指令时,它会使用地址寄存器来指定目标内存地址,并使用数据寄存器来传输数据。如果操作涉及到磁盘,CPU会通过磁盘控制接口(如IDE、SATA或SCSI)与磁盘驱动器通信,使用磁盘的I/O指令来读取或写入数据在这个过程中,磁盘的地址通常是通过磁盘控制器的寄存器来指定的。
CPU与内存、磁盘之间的数据传输是通过系统总线(System Bus)来完成的,而系统总线与各种寄存器之间的连接由计算机的硬件架构来定义。
总线
总线(Bus)是计算机中各个组件之间传输数据的通道,它负责在计算机内部传输数据、地址和控制信号。总线可以分为三类:数据总线、地址总线和控制总线。
-
数据总线(Data Bus):数据总线负责在计算机内部传输数据。它包括多条信号线,每个信号线负责传输一位数据。数据总线可以是单向的,也可以是双向的,具体取决于计算机体系结构。
-
地址总线(Address Bus):地址总线负责在计算机内部传输内存地址。它包括多条信号线,每个信号线负责传输一位地址。地址总线用于指定计算机内存中的某个位置,以便将数据读入或写入该位置。
-
控制总线(Control Bus):控制总线负责传输控制信号,用于控制计算机内部组件的操作。它包括多条信号线,每个信号线负责传输一个控制信号。控制总线用于控制数据和地址的传输,以及计算机内部组件的操作。
谈谈软件
操作系统OS
操作系统(Operator System)是一种软件,它是计算机系统中的核心组件,负责管理和协调计算机的硬件和其他软件资源,为用户和应用程序提供接口和环境。
操作系统提供了一组核心功能,包括进程管理、内存管理、文件系统管理和设备驱动程序等。它负责调度和管理计算机上运行的进程,确保它们以公平和高效的方式共享CPU时间片。操作系统还管理计算机的内存资源,为进程分配和回收内存空间。
另外,操作系统还提供了文件系统管理,用于组织和存储文件,并提供对文件的读写和访问的功能。操作系统还负责管理和控制计算机的各种硬件设备,例如硬盘驱动器、打印机、网络接口卡等,通过设备驱动程序与外部设备进行通信。
操作系统为了保证自己数据安全,提供了用户接口,以便用户可以与计算机进行交互。这包括命令行界面(CLI)和图形用户界面(GUI),使用户能够执行命令、运行程序、浏览文件等。所有命令都是最终由操作系统调用的.
绝对路径与相对路径
-
绝对路径:绝对路径是从根目录(树的顶部)开始到目标文件或目录的完整路径。它以斜杠 "/" 开头,以文件名或目录名结束,每个目录都由斜杠分隔。举个例子,
/home/username/Documents
就是一个绝对路径,表示在根目录下的home
目录下的username
目录下的Documents
目录。 -
相对路径:相对路径是从当前目录出发到目标文件或目录的路径。相对路径没有以斜杠 "/" 开头,而是以当前目录作为起点,使用 “.” 表示当前目录,使用 “..” 表示上级目录。例如,假设当前目录为
/home/username
,则相对路径Documents
表示当前目录下的Documents
目录,而相对路径../Pictures
表示上级目录中的Pictures
目录。
特点:
绝对路径与相对路径之间的主要区别在于起点位置。绝对路径总是从根目录开始,可以准确定位到目标文件或目录。而相对路径则是相对于当前目录的位置来描述目标位置。
使用绝对路径通常更精确,但路径会更长。相对路径相对简洁,适用于描述相对关系和在当前目录中进行导航。
文件初识
在Linux中,文件是存储在磁盘上的数据集合。Linux文件系统将所有文件和目录组织成一个层次结构,类似于一个树状目录结构。每个文件和目录都有一个唯一的路径来标识其在文件系统中的位置。
Linux中的文件可以分为以下几种类型:
-
普通文件(Regular File):最常见的文件类型,用于存储数据。例如,文本文件、程序文件等。
-
目录文件(Directory):用于存储其他文件和目录的文件。目录文件本身也是一个文件,但其内容是指向其他文件和目录的链接。
-
链接文件(Symbolic Link):也称为符号链接,是一种特殊类型的文件,其内容是另一个文件的路径。链接文件可以用来指向其他文件或目录的快捷方式。
-
块设备文件(Block Device):用于表示存储设备,如硬盘驱动器。这些文件通常用于磁盘分区和文件系统。
-
字符设备文件(Character Device):用于表示字符设备,如键盘和打印机。这些文件用于输入和输出操作。
-
管道文件(Pipe):用于在进程之间传递数据的特殊文件。文件是一种特殊的文件类型,用于实现进程间的通信。
-
套接字文件(Socket):用于网络通信的特殊的文件类型。套接字文件用于在网络中的不同主机之间的进程间通信。
在Linux中,每个文件和目录都有一个唯一的inode(索引节点)编号,用于标识文件系统中的每个文件。inode包含了文件的元数据,如文件的大小、创建时间、最后修改时间、文件所有者、所属组等信息。
文件的属性
文件 = 文件内容 + 文件属性
通过stat命令查看文件属性
access更新的策略 touch 文件 会刷新时间
目录
冯·诺依曼体系结构(von Neumann architecture)
问题:那么假如访问100b的内容,也会交互4kb的内容到物理内存,不会浪费空间吗
重定向 < > >> 管道| tee && ||
在Linux中,重定向(Redirection)是一种用于改变标准输入(stdin)、标准输出(stdout)和标准错误(stderr)的传递方向的操作。
以下是在Linux中常用的重定向操作符:
-
>
:输出重定向符号(Output Redirection),将命令的标准输出重定向到文件中。例如,ls > file.txt
将ls
命令的输出保存到file.txt
文件中,如果文件不存在则创建,如果文件存在则覆盖它。 -
>>
:追加重定向符号(Append Redirection),类似于>
,将命令的标准输出追加到文件末尾。例如,echo "Hello" >> file.txt
将字符串 "Hello" 追加到file.txt
文件末尾。 -
<
:输入重定向符号(Input Redirection),将文件的内容作为命令的标准输入。例如,sort < file.txt
将file.txt
文件中的内容作为sort
命令的输入进行排序。 -
2>
:标准错误重定向符号(Standard Error Redirection),将命令的标准错误输出重定向到文件中。例如,command 2> error.txt
将command
命令的标准错误输出保存到error.txt
文件中。 -
2>>
:标准错误追加重定向符号(Append Standard Error Redirection),类似于2>
,将命令的标准错误输出追加到文件末尾。 -
|
:管道符号(Pipe),将一个命令的标准输出作为另一个命令的标准输入。例如,command1 | command2
将command1
命令的输出传递给command2
命令进行处理。 -
&&
:如果第一个命令执行成功(返回值非零),则执行第二个命令;如果第一个命令执行失败(返回值为零),则不会执行第二个命令。 这里的关键点是,&&
操作符会等待第一个命令完成执行后,再执行第二个命令。如果第一个命令失败,那么第二个命令就不会被执行,整个&&
操作符连接的表达式的返回值将是第一个命令的返回值。 -
||
: 如果第一个命令执行失败,则执行第二个命令;如果第一个命令执行成功,则不会执行第二个命令。
缓冲区
缓冲区(buffer)是一个存储空间,通常用于临时存放数据,以便在两个相关的操作之间进行交换。在计算机科学中,缓冲区常见于输入/输出操作、网络编程、数据转换等场景。它们的主要目的是减少相关操作的频率,提高效率,降低延迟,或者在某些情况下,同时处理多个操作。
以下是一些缓冲区的具体应用和细节:
-
输入/输出缓冲:这是最常见的缓冲形式,主要用于提高I/O操作的效率。当操作系统从磁盘读取数据或向磁盘写入数据时,数据首先被存储在内存中的缓冲区中,然后再从缓冲区写入磁盘或从磁盘读取数据到缓冲区。这种模式可以减少磁盘操作的数量,提高效率。
-
网络缓冲:在网络编程中,缓冲区用于存储发送到网络的数据包。这些数据包被添加到缓冲区中,直到缓冲区满或者发送请求被处理。这样可以减少网络传输的数据包数量,同时允许程序在数据包到达网络之前进行一些处理。
-
数据转换缓冲:在数据转换过程中,缓冲区也常常被使用。例如,在图像处理中,输入和输出图像的数据类型可能不同。这时,就需要将输入图像的数据从一种格式转换到另一种格式,并在转换过程中使用缓冲区。
-
处理队列:某些情况下,缓冲区也可以用来管理需要处理的元素队列。例如,在处理大量用户请求时,可以将请求放入缓冲区,然后按顺序进行处理,而不是对每个请求立即进行处理。
然而,缓冲区也带来了一些问题,如内存泄漏和内存溢出。因此,在使用缓冲区时,必须确保正确管理内存空间,以避免这些问题。通常,这需要编写一些特殊的代码来检查缓冲区的使用情况,并在必要时释放缓冲区。例如适用fflash(stdout)刷新输出流缓冲区
此外,使用内存映射文件也是一种有效的方式来解决一些常见的I/O问题。内存映射文件可以将文件映射到进程的地址空间中,使其可以像普通内存一样进行访问。这种方法的好处是可以直接操作文件内容而无需进行额外的内存管理操作。
main函数的返回值
函数终止无外乎就三种情况:
-
代码运行完毕,结果正确。
-
代码运行完毕,结果不正确。
-
代码异常终止。
return 0 这里的0叫做进程的退出码,表征进程的运行结果是否正确,0表示success.return 其他非0数字就表示不同的结果错误的原因。
通过strerror(int)函数可以查看错误码对应的信息。
有一个在error.h头文件中的全局变量 errno ,保存了最近的错误码
$?
保存最近一次程序的退出码
这个退出码是给关心他的用户或者父进程提供的。
所以我们的main函数是这么写的:
#include<stdlib.h> #include<stdio.h> #include<string.h> #include<unistd.h> #include<errno.h> int main() { int ret =0; char *p = (char)malloc(1000*1000*1000); if(p == NULL){ printf("malloc error\n"); ret = 1; } else{ printf("malloc successful\n"); } return ret; } 通过errno全局变量来返回值 int main() { int ret =0; char *p = (char)malloc(1000*1000*1000); if(p =NULL) printf("malloc error,%d,%s\n",errno,strerror(errno)); ret = errno; } else{ printf("malloc successful\n"); } return ret; }
命令行参数
是什么
命令行参数(也称为命令行选项或标志)是传递给命令的附加信息或附加参数。这些参数可以是任何类型的数据,如文件名、路径、选项等
在main函数中的参数
int main(int argc, char *argv[],char *env[])
参数argc
和argv
用于接收命令行参数,env是父进程将环境变量整理成表传递给子进程。
-
argc
(argument count)是一个整数,表示命令行参数的个数,包括程序名称本身。当程序没有接收到任何命令行参数时,argc
的值为1。 -
argv
(argument vector)是一个字符指针数组,每个指针指向一个命令行参数的字符串。argv[0]
表示程序的名称,argv[1]
、argv[2]
等表示后续的命令行参数。最后一个参数argv[argc]
为NULL指针。 -
env
是一个字符指针数组,每个指针指向一个环境变量。最后一个参数为NULL指针。
例子:
#include <stdio.h> int main(int argc,char*argv[],char*env[]) { printf("The program name is: %s \n", argv[0]); for(int i = 1; i < argc; i++) { printf("Argument %d: %s \n", i, argv[i]); } while (*env != NULL) { printf("Environment variable: %s\n", *env); env++; // 移动到下一个环境变量 } return 0; }
使用上述代码,并在命令行中输入./program arg1 arg2
,则输出如下:
The program name is: ./program Argument 1: arg1 Argument 2: arg2 Environment variable: XDG_SESSION_ID=1 Environment variable: HOSTNAME=MYCAT Environment variable: SHELL=/bin/bash Environment variable: TERM=xterm Environment variable: HISTSIZE=1000 Environment variable: SSH_CLIENT=192.168.170.1 1245 22 Environment variable: SSH_TTY=/dev/pts/0 Environment variable: USER=lzh .......
变量
本地变量
包含自定义变量以及环境变量
通过set指令查看所有的本地变量
环境变量
在Linux操作系统中,环境变量是指在程序执行过程中可以被其访问的变量,这些变量包含了程序运行所需的信息,如路径、文件名、用户名、语言设置等。
在Linux中,环境变量通常以键值对的形式存储在内存中,每个键值对由一个变量名和一个变量值组成。这些环境变量可以在程序启动时被设置,也可以在系统启动时通过启动脚本进行设置。
在Linux中,环境变量可以分为全局环境变量和局部环境变量(可以使用putenv
函数来设置局部环境变量)两种。全局环境变量在系统范围内有效,而局部环境变量仅在某个进程或线程内有效。
以下是一些常见的Linux环境变量:
-
PATH
:系统用于查找可执行文件的路径。 -
HOME
:用户的主目录路径。 -
USER
:当前用户的用户名。 -
HOSTNAME
:当前系统的主机。 -
PWD
:当前工作目录路径。 -
OLDPWD
:先前工作的目录。 -
LANG
:系统的语言设置。 -
TERM
:当前终端的类型。 -
MAILPATH
:用户邮箱的路径。 -
PS1
:命令提示符的格式。
指令:
set env&printenv echo export unset
#查看所有的本地变量 set #查看所有的环境变量 printenv env #查看PATH环境变量 echo $PATH #/usr/local/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin #增加PATH环境变量 PATH=$PATH:/home/lzh/tmp #export设置环境变量 export VARIABLE_NAME=value #unset删除环境变量 unset VARIABLE_NAME
在C语言中的函数:
getenv()
getenv
函数是在C语言中用于获取环境变量值的函数。
getenv
函数的原型通常如下:
#include<stdlib.h> char *getenv(const char *name);
这个函数接受一个指向环境变量名称的指针,并返回一个指向环境变量值的指针。如果环境变量不存在,则返回NULL。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { char *path = getenv("PATH"); if (path != NULL) { printf("PATH = %s\n", path); } else { printf("Environment variable PATH not found.\n"); } return 0; }
需要注意的是,getenv
函数返回的是指向环境变量值的静态缓冲区的指针。这意味着该缓冲区的内容在函数调用结束后可能不会被释放,因此应该避免在程序中使用该指针来存储或传递环境变量的值。如果需要存储环境变量的值,应该使用动态分配的内存来存储它。
char ** environ
在<unistd.h>头文件中 也可以获取环境变量
#include <stdio.h> #include <unistd.h> int main() { extern char **environ; char** env =environ;// 获取环境变量数组指针 while (*env != NULL) { printf("Environment variable: %s\n", *env); env++; // 移动到下一个环境变量 } return 0; }
putenv()
int putenv(char *string);
参数说明:
-
string
:指向包含环境变量键值对的字符串。这个字符串应该具有格式"KEY=VALUE"
,其中KEY
是环境变量的名称,VALUE
是环境变量的值。
putenv
函数会添加或修改当前进程的环境变量列表。如果 KEY
已经存在,则其对应的 VALUE
会被更新。如果 KEY
不存在,则环境变量会被添加到列表中。
使用 putenv
函数的例子:
#include <stdio.h> #include <unistd.h> int main() { // 设置环境变量 MY_VARIABLE 的值为 "my_value" putenv("MY_VIABLE=my_value"); // 打印环境变量 MY_VARIABLE 的值 printf("Value of MY_VARIABLE: %s\n", getenv("MY_VARIABLE")); return 0; }
在这个例子中,我们使用 putenv
函数设置了环境变量 MY_VARIABLE
的值为 "my_value"
。然后,我们使用 getenv
函数获取了同一个环境变量的值并打印出来。
需要注意的是,putenv
函数的修改对当前进程及其子进程都是有效的。如果程序有多个线程,则修改环境变量的操作应该是线程安全的,或者确保线程不会同时修改同一环境变量。
debug 与 release
Debug模式和Release模式是软件开发中常见的两种构建和部署方式,它们在编译、优化和调试等方面有所不同。
Debug模式:
-
编译时:在Debug模式下,编译器会生成额外的调试信息(如符号表),使得调试器能够更方便地将源代码和可执行文件关联起来。这会增加可执行文件的大小。
-
优化:Debug模式下不进行优化,以便开发人员能够在调试期间准确地查看变量值和程序状态,以及进行单步调试。
-
可读性:Debug模式生成的可执行文件通常包含易于阅读的代码,包括变量名、注释和调试选项。
-
性能:Debug模式下,程序的执行速度可能会较慢,因为没有进行优化,并且生成了额外的调试信息。
Release模式:
-
编译时:在Release模式下,编译器会进行优化,以减小可执行文件的大小并提高程序的执行效率。
-
优化:Release模式进行各种优化,例如函数内联、循环展开和无效代码删除等,以降低程序的执行时间和内存占用。
-
可读性:Release模式生成的可执行文件通常没有调试信息,变量名和注释等易读性较差。
-
性能:Release模式下,程序的执行速度可能更快,并且占用较少的内存,因为进行了各种优化。
程序的一些概念
上下文
当我们谈论一个程序的上下文时,我们指的是程序在执行过程中所处的环境和状态。程序的上下文包括了许多方面的信息,它们对程序的执行和行为产生影响。下面是一些程序上下文的常见方面:
-
程序的代码:包括了程序的指令和函数定义。程序的代码描述了程序应该执行的操作和逻辑。
-
程序的数据:包括程序运行过程中使用的变量、常量和数据结构。程序的数据存储了程序在执行过程中需要处理的信息。
-
寄存器状态:寄存器是计算机硬件中的一些临时存储区域,用于存储程序的运行状态和临时变量。寄存器状态包括了程序中各个寄存器的值和配置。
-
堆栈:堆栈是用于存储程序执行时的局部变量、函数调用和返回地址等信息的一种数据结构。它跟踪函数的调用和返回过程,并存储程序执行的上下文信息。
-
打开的文件和文件描述符:程序可能会打开和操作各种文件,包括输入文件、输出文件和临时文件。程序的文件上下文包括了打开的文件集合、当前位置以及相关的文件描述符。
-
环境变量:环境变量是程序执行时使用的一些设置值,如路径、语言设置等。环境变量的值影响了程序的执行环境以及其行为。
-
网络连接和状态:如果程序涉及到网络通信,它可能会建立和维护与其他主机的连接和状态。网络连接和状态信息也属于程序的上下文。
这些方面的上下文信息共同决定了程序的执行和行为。了解程序的上下文对于正确理解和分析程序的行为非常重要。在某些情况下,我们可能会手动保存和恢复程序的上下文信息,以实现特定的功能和需求。