第一周-操作系统概述
理论部分
一、操作系统的基本概念
-
定义与角色
-
操作系统是管理计算机硬件与软件资源、控制程序执行、改善人机界面、提供各种服务的系统软件。它充当了计算机硬件和用户以及应用程序之间的桥梁。
-
例如,当用户打开一个应用程序(如浏览器),操作系统负责将该程序从硬盘加载到内存,分配必要的CPU时间和其他资源(如网络接口等),使得浏览器能够正常运行。
-
-
操作系统的目标
-
包括方便性(为用户提供方便的操作界面和使用方式)、有效性(提高系统资源的利用率)、可扩充性(便于增加新的功能和模块)和开放性(遵循标准接口,便于与其他系统互联)。
-
以智能手机操作系统为例,为了方便性,它们提供了直观的触摸式界面;为了有效性,会合理分配有限的电池电量、内存和CPU资源;随着新功能(如指纹识别、面部识别等)的需求增加,操作系统也需要具备可扩充性;并且要遵循一定的通信标准以便与其他设备互联。
-
二、操作系统的发展历程
-
手工操作阶段
-
在早期计算机时代,没有操作系统,用户通过手工操作计算机。用户需要直接操作计算机硬件,如将纸带插入纸带阅读器来输入程序,然后手动控制计算机的运行。这种方式效率极低,因为计算机的运行速度很快,而人工操作速度很慢,大部分时间计算机都处于闲置状态。
-
-
批处理系统阶段
-
单道批处理系统:将一批作业按顺序依次输入计算机,一个作业运行结束后,下一个作业才开始运行。例如,将多个用户的计算任务(如科学计算程序)集中起来,先把一个程序加载到计算机中运行,运行完成后再加载下一个。
-
多道批处理系统:内存中同时存放多个作业,这些作业并发执行。这提高了计算机系统资源的利用率,因为当一个作业在等待I/O操作(如磁盘读写)时,另一个作业可以使用CPU。
-
-
分时系统阶段
-
多个用户通过终端同时共享计算机资源。操作系统将CPU时间划分成很短的时间片,轮流为各个用户服务。例如,在大学的计算机实验室中,多个学生可以同时使用一台大型计算机,每个学生感觉自己独占了计算机,实际上是操作系统在快速地在不同用户之间切换服务。
-
-
实时系统阶段
-
实时系统能够及时响应外部事件的请求,在规定的时间内完成对该事件的处理。例如,在飞机的飞行控制系统中,操作系统必须实时处理传感器传来的数据,如高度、速度等信息,并及时调整飞机的飞行姿态,任何延迟都可能导致严重后果。
-
三、操作系统的类型
-
批处理操作系统
-
其特点是用户将一批作业提交给操作系统后,就不再干预作业的运行,直到作业全部完成。作业在运行过程中不需要用户的交互。例如,一些大型企业的数据处理中心,每天晚上会对大量的数据进行批处理操作,如数据备份、报表生成等。
-
-
分时操作系统
-
多个用户分时共享计算机资源。每个用户在自己的终端上操作,感觉自己独占了计算机。如UNIX和Linux操作系统都具有很强的分时特性,适合多用户环境下的交互式操作。
-
-
实时操作系统
-
根据应用场景可分为硬实时系统和软实时系统。硬实时系统必须严格满足时间限制,如航空航天、工业控制领域;软实时系统对时间限制的要求相对宽松一些,如多媒体播放系统,偶尔的延迟可能不会造成严重后果,但也需要在一定的时间范围内完成数据处理。
-
-
网络操作系统
-
用于管理网络中的计算机资源,提供网络通信和网络服务。例如,Windows Server操作系统可以管理局域网中的用户账户、共享文件和打印机等资源,同时提供网络安全防护等功能。
-
-
分布式操作系统
-
分布式系统是由多个分散的计算机通过网络连接而成的系统,分布式操作系统负责管理这些分散的资源,使它们像一个整体一样工作。例如,大规模的云计算平台就是基于分布式操作系统构建的,它将众多的服务器资源整合起来,为用户提供计算、存储等服务。
-
四、操作系统的基本功能概述
-
进程管理
-
进程是操作系统进行资源分配和调度的基本单位。了解进程的概念、状态(就绪、运行、阻塞等)以及进程的创建、终止和切换等基本操作。例如,当在计算机上同时打开多个应用程序时,每个应用程序就是一个进程,操作系统要合理地调度这些进程,使它们能够共享CPU资源。
-
-
内存管理
-
操作系统要负责内存的分配、回收和保护等工作。例如,当启动一个新的应用程序时,操作系统要为其分配足够的内存空间;当应用程序关闭时,操作系统要回收其占用的内存。同时,还要防止不同进程之间的内存非法访问。
-
-
文件系统管理
-
包括文件的存储、检索、共享和保护等。例如,在Windows操作系统中,文件系统(如NTFS)负责将文件存储在硬盘上,并提供方便的文件查找(通过文件夹和文件名)、文件共享(设置共享权限)和文件保护(如设置文件的读写权限)等功能。
-
-
设备管理
-
对计算机的各种设备(如键盘、鼠标、打印机、磁盘等)进行管理,包括设备的分配、驱动程序的管理等。例如,当插入一个新的USB设备时,操作系统要识别该设备,加载相应的驱动程序,并根据需要分配资源给该设备。
-
实践部分
我使用的是: Ubuntu操作系统、nano文本编辑器、g++编译器
步骤可参考文章:在Ubuntu中编写第一个c/c++代码并运行的步骤
一、进程管理实践
-
进程创建与终止
-
在Linux系统中,可以使用系统调用函数来创建和终止进程。例如,使用
fork()
系统调用创建新进程。 -
代码示例如下:(代码一)
-
#include <stdio.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid < 0) {// 创建进程失败perror("fork");return 1;} else if (pid == 0) {// 子进程printf("I am the child process, my PID is %d\n", getpid());} else {// 父进程printf("I am the parent process, my PID is %d and my child's PID is %d\n", getpid(), pid);}return 0;
}
在这个示例中,fork()
函数创建一个新的进程。如果返回值为0,表示当前是子进程;如果返回值大于0,表示当前是父进程,返回值为子进程的进程标识符(PID);如果返回值小于0,表示进程创建失败。通过这样的实践,可以深入理解进程的创建过程以及父子进程之间的关系。
-
关于
pid
和PID
的概念澄清- 在这段代码中,
pid
是一个变量名,它用于接收fork
函数的返回值。 - 而
PID
(进程标识符,Process Identifier)是操作系统分配给每个进程的唯一数字标识。 - 在代码中,当调用
getpid
函数时,获取到的是当前进程(父进程或者子进程)的真正的进程标识符(PID)。 - 当在父进程中调用
fork
函数时,fork
的返回值pid
是新创建的子进程的PID(这个PID是操作系统分配给子进程的唯一标识,是一个大于0的值)。在子进程中,fork
的返回值pid
被设置为0,这是一种约定,用于区分父进程和子进程。
- 在这段代码中,
-
举例说明
- 假设我们有一个进程管理系统,就像一个学校管理学生(进程)一样。每个学生(进程)都有一个学号(PID),这个学号是学校(操作系统)分配的唯一标识。
- 当一个班级(父进程)要创建一个新的小组(子进程)时,班级(父进程)会得到这个新小组(子进程)的学号(PID),这个学号被存储在变量
pid
(在父进程中fork
的返回值)中。而新小组(子进程)自己知道自己是新创建的,所以它内部有一个特殊的标识(在子进程中fork
返回0,这个0不是真正的PID,只是用于区分的约定值)。同时,无论是班级(父进程)还是小组(子进程),都可以通过getpid
函数来获取自己真正的学号(PID)。
#include <stdio.h>
#include <unistd.h>int main() {// 使用pid_t类型的变量pid来接收fork函数的返回值// pid_t是一种专门用于表示进程ID的数据类型,通常是整数类型pid_t pid = fork(); // fork函数返回值有三种情况,下面分别进行处理// 如果fork函数返回值小于0,表示进程创建失败if (pid < 0) { // perror函数用于输出错误信息// 它会输出系统错误信息并在前面加上传入的字符串(这里是"fork")perror("fork"); // 返回1,表示程序异常结束return 1; }// 如果fork函数返回值等于0,表示当前是子进程else if (pid == 0) { // 子进程执行以下代码// 使用printf函数输出子进程相关信息// "I am the child process, my PID is %d\n"是格式化字符串// getpid函数用于获取当前进程(即子进程)的进程标识符(PID)// 将获取到的PID值填充到格式化字符串中的%d位置并输出到控制台printf("I am the child process, my PID is %d\n", getpid()); }// 如果fork函数返回值大于0,表示当前是父进程else { // 父进程执行以下代码// 使用printf函数输出父进程相关信息// "I am the parent process, my PID is %d and my child's PID is %d\n"是格式化字符串// getpid函数用于获取当前进程(即父进程)的进程标识符(PID)// pid变量保存着子进程的PID(fork函数在父进程中的返回值就是子进程的PID)// 将父进程和子进程的PID分别填充到格式化字符串中的%d位置并输出到控制台printf("I am the parent process, my PID is %d and my child's PID is %d\n", getpid(), pid); }// 返回0,表示程序正常结束return 0;
}
-
进程调度
-
可以通过编写简单的多进程程序,并设置不同的优先级来观察进程调度的效果。在Linux系统中,可以使用
nice
命令来调整进程的优先级。 -
例如,创建两个简单的计算密集型进程,一个设置较高的优先级,一个设置较低的优先级,然后观察它们在系统中的执行情况。这有助于理解操作系统如何根据优先级等因素来调度进程,合理分配CPU资源。
-
二、内存管理实践
-
内存分配与释放
-
在C语言中,可以使用标准库函数
malloc()
和free()
来模拟操作系统的内存分配和释放机制。 -
示例代码:(代码二)
-
#include <stdio.h>
#include <stdlib.h>int main() {int *ptr;ptr = (int *)malloc(sizeof(int) * 10);if (ptr == NULL) {perror("malloc");return 1;}// 使用分配的内存并输出每个元素的值for (int i = 0; i < 10; i++) {ptr[i] = i;printf("%d ", ptr[i]);}// 换行,使输出更美观printf("\n");// 释放内存free(ptr);return 0;
}
这里,malloc()
函数从堆中分配了一块足够存储10个int
类型数据的内存空间。如果分配成功,返回指向这块内存的指针;如果失败,则返回NULL
。free()
函数用于释放之前通过malloc()
分配的内存。通过这样的实践,能体会到操作系统管理内存空间分配和回收的基本原理。
-
虚拟内存的理解
-
在Linux系统中,可以通过查看
/proc
文件系统中的相关文件来了解虚拟内存的使用情况。例如,/proc/meminfo
文件包含了系统内存的各种信息,如总内存、空闲内存、虚拟内存使用量等。 -
可以编写一个程序,不断申请内存直到系统提示内存不足,同时观察
/proc/meminfo
文件中的虚拟内存相关指标的变化,这有助于理解虚拟内存的概念以及操作系统如何利用磁盘空间来扩展内存。
-
三、文件系统管理实践
-
文件创建、读写和删除
-
在Linux系统中,可以使用系统调用函数如
open()
、read()
、write()
和close()
来操作文件。 -
示例代码:(代码三)
-
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>int main() {int fd;char buffer[] = "Hello, world!";// 创建文件并写入数据printf("正在创建并打开文件test.txt用于写入...\n");fd = open("test.txt", O_WRONLY | O_CREAT, 0644);if (fd < 0) {perror("open");return 1;}printf("正在向文件test.txt写入数据...\n");write(fd, buffer, sizeof(buffer));close(fd);// 读取文件数据printf("正在打开文件test.txt用于读取...\n");fd = open("test.txt", O_RDONLY);if (fd < 0) {perror("open");return 1;}char read_buffer[100];printf("正在从文件test.txt读取数据...\n");read(fd, read_buffer, sizeof(read_buffer));printf("Read data: %s\n", read_buffer);close(fd);// 删除文件printf("正在删除文件test.txt...\n");if (unlink("test.txt") < 0) {perror("unlink");return 1;}return 0;
}
这个示例中,首先使用open()
函数创建一个新文件并以只写模式打开,然后使用write()
函数向文件中写入数据,接着关闭文件。再次使用open()
函数以只读模式打开文件,并用read()
函数读取文件中的数据,最后使用unlink()
函数删除文件。通过这样的操作,可以深入理解文件系统中文件的创建、读写和删除的基本流程。
-
文件权限管理
-
在Linux系统中,可以使用
chmod
命令来修改文件的权限。例如,chmod 755 file.txt
会将file.txt
的权限设置为所有者有读、写、执行权限,组用户和其他用户有读、执行权限。 -
可以编写一个脚本来批量修改文件的权限,然后观察不同权限下文件的可访问性,这有助于理解文件权限管理在文件系统中的重要性以及操作系统如何保障文件的安全性。
-
四、设备管理实践
1.设备驱动程序的简单操作
-
在Linux系统中,对于一些简单的设备,如USB设备,可以查看设备在
/dev
目录下的表示,并尝试进行简单的读写操作(如果设备支持)。 -
例如,对于一个USB闪存盘,可以查看它在
/dev
目录下对应的设备文件(如sdb
等),然后可以使用dd
命令来对其进行简单的读写测试(需要谨慎操作,以免损坏数据)。这有助于理解操作系统如何通过设备驱动程序与硬件设备进行交互,以及设备文件在设备管理中的作用。
示例代码:(代码四)
ls -l /dev
以第一个为例
一:文件类型标识
以c
开头的文件(如crw------- 1 root root 10, 175 Oct 15 02:08 agpgart
)是字符设备文件。字符设备通常以字节流的方式进行数据传输,像终端设备(console
)就是字符设备,数据是一个字符一个字符地进行读写操作。
二:权限部分
第一个字符c
或b
表示文件类型,后面的rw - - - - - -
表示所有者(root
)对agpgart
设备有读写权限,而组和其他用户没有任何权限。
三:硬链接、所有者和所属组
数字1
(如1 root root
中的1
)表示硬链接数。硬链接是指向同一个文件的不同文件名,在设备文件中,硬链接数通常为1。root root
分别表示文件的所有者和所属组。在/dev
目录下,大部分设备文件的所有者和所属组都是root
,这是因为设备的管理通常需要管理员权限。
四:设备号
后面的两个数字(如10, 175
)是设备的主设备号和次设备号。
五:日期和时间(Oct 15 02:08)
六:设备文件名(agpgart)
2.设备的挂载与卸载
-
在Linux系统中,使用
mount
和umount
命令来实现设备的挂载和卸载。例如,要挂载一个U盘,可以先确定U盘的设备文件(如/dev/sdb1
),然后使用mount /dev/sdb1 /mnt
命令将其挂载到/mnt
目录下,这样就可以访问U盘中的文件了。使用完后,使用umount /mnt
命令卸载设备。通过这样的操作,可以理解操作系统对外部设备的管理方式,包括设备与文件系统的连接和断开等操作。