操作系统学习笔记(一)

操作系统学习笔记(一)

抽象:

抽象是计算机科学中一个重要的概念,尤其是在软件和硬件-软件界面的设计过程中,许多问题都可以通过增加一个新的抽象层解决。随着计算机的发展,各种不同型号的电脑开始在市面上流行——不同型号的电脑可能具有不同的*处理器、不同的内存大小和不同的硬件设备(如鼠标、键盘、传感器等),并需要在不同的网络环境中运行。那么,程序员是否需要针对每台电脑不同的硬件组合写出不同的程序呢?答案显然是否定的。为了规避处理具体硬件接口的复杂性,我们需要一个新的抽象层,它可以囊括所有不同的硬件组合,从而方便软件利用硬件资源。这个新的抽象层正是操作系统。

操作系统的抽象包括很多层面。一个最常见的例子是我们经常使用的文件和目录:

操作系统学习笔记(一)

虽然我们习惯于在目录中存储文件,但文件实际只是存储在磁盘某一区域的一段二进制数,如果没有有关文件存储位置、文件大小的数据,我们甚至没有办法知道文件在磁盘上是从哪里开始、至哪里结束的。如果没有操作系统这个抽象层,那么我们就必须以某种形式储存我们的数据在磁盘上的位置,而这是十分复杂的。操作系统为我们管理了磁盘上存储的数据,将一次存储在多个位置的数据结合在一起命名为一个文件,方便我们直接存取磁盘上的信息而不需要知道他们是如何被存储的。不同操作系统对于文件系统的抽象方式是不同的,我们将在课程的最后接触到文件与文件系统这一部分内容,因此我们在这里不做展开,对这部分内容感兴趣的同学们可以在对应章节了解到更多的内容。

个人感觉就像抽象这个东西是高层次的封装,而且这里的意思是添加一个新的抽象层。不是一个函数。

而且不是简单的封装,是进行抽象,归纳出通性来用一个新的层次来管理。

操作系统提供的另一个常见的抽象层是基于进程的抽象。我们在使用计算机时总是感到程序是连续运行的,但是实际由于计算机物理资源的有限性,一个程序在处理器上的运行大多数时候是不连续的(这取决于操作系统进程调度所用的算法,我们会在之后的章节具体探讨这个话题)。计算机只有有限数量的内核,不可能为所有程序提供全部处理器时间,一定会有程序在运行过程中被切换为另一个程序,一段时间后再继续运行。同样地,计算机的有限内存迫使一个程序的一部分内存被留在磁盘中,只有一些数据会实际存在于系统内存中。

因此我们在使用计算机时所认为的连续使用处理器、将全部数据存储在内存中的程序是不存在的,这种独占全部内存和处理器时间运行的程序来源于操作系统对于物理资源的抽象,也就是我们所说的“进程”。这种抽象大大方便了多个程序同时在系统中运行——程序设计者不需要考虑其它软件在内存中所占的大小和位置,也不需要考虑上下文切换(上下文切换即从一个进程切换到另一个进程的过程,我们在讲进程时会更具体地探讨这个过程)时切换内存、保留处理器状态等复杂的过程,而只需要考虑自己所设计的程序在独占全部资源的理想状态下的运行情况。

感觉是通过抽象来简化了客户需要考虑的细节,而是让程序进行自动的管理,处理,客户就只需要告诉程序干什么,至于细节由程序来处理

malloc() 动态分配堆内存

函数原型:

void* malloc(size_t size);

malloc需要一个参数size,用来表明需要分配的堆内存大小;它返回的值是指向分配内存起始地址的指针。我们主要在一个函数要应用另一个函数的返回值时应用该函数:由于函数返回后,其栈所占的内存会被回收,我们必须将需要被其它函数利用的值存放在堆内存中,并返回一个指向该内存的指针。

这段话告诉我们堆内存和栈内存是两个不同的概念,且当一个函数返回时,其栈所占的内存就会被回收。但是堆内存就不会被回收,只有在主函数结束,所以需要主动去回收,避免内存泄露。

介绍另一函数

realloc()动态内存调整

函数原型

void *realloc(void *mem_address, unsigned int newsize);

realloc有可能将分配的堆内存移到了与原指针所指的位置不同的地址,因此必须用realloc的返回值更新test2

因为,当realloc第一个参数所指向的内存空间大小不足够扩大为第二个参数所指定的的大小时,realloc将新分配一段足够大的内存空间,将旧的那段内存空间里的内容拷贝过去然后释放旧的内存空间。

在第一个参数所指向的内存空间大小不足以扩充为第二个参数大小时候,不就是 malloc + memcpy +free ,可以扩充的时候就是直接扩充。

注意:

如果size为0,效果等同于free()。这里需要注意的是只对指针本身进行释放,例如对二维指针**a,对a调用realloc时只会释放一维,使用时谨防内存泄露

常用的字符串函数

strcpy 字符串拷贝函数

函数原型
char* strcpy(char * dest, char* src);

是一个将存放在src里的字符串复制到dest的字符串里的函数;它的返回值是dest。需要注意的是,strcpy在复制字符串时会自动在末尾加上\0因此dest的长度必须比src多至少 1个字符;如果dest的长度小于这个长度,你就会在运行时碰到 段错误(Segmentation Fault)。这个看似很小的问题可能会导致你 debug 到凌晨三点,所以绝不要忘记

注意:

strcpy 作用是把src的内容 拷贝到dest 里的而且 strcpy 会在最后加一个’\0’ 所以dest 长度至少要比src 多一个

内存修改

同在string.h中的函数。

void * memset ( void * ptr, int value, size_t num );
void * memcpy ( void * destination, const void * source, size_t num );

memset函数将从ptr指针所指的位置开始的、大小为num字节的内存中的每个字节设为你所指定的数值value。这个函数在将一段不用的内存全部归零时非常实用。你也许会问,用这个函数和用循环语句把某一段位置都设为零有什么区别吗?一般来讲,memset在系统自带的库文件中被优化过了(因为很多人使用),因此用它的效率往往比用循环简单实现的更高。当你想要把一大段内存设为一个数值的时候,不妨考虑使用这个函数,而不是一般的循环。

memcpymemset类似,也是修改一段连续的内存的函数。它将由source开始的长度为num字节的内存复制到由destination开始的长度为num字节的内存中。与memset类似,它的实现也在编译器中被优化了,因此速度会比较快。

。。被编译器优化了,可以的。。