操作系统
- 推荐学习视频:
- BV1YE411D7nH(b站 2019 王道考研 操作系统)
- 第一章 操作系统引论
- 计算机系统的组成: 硬件、软件(系统软件和应用软件)
- 操作系统分类: 单道批处理程序(系统对作业的处理成批进行,但内存中只运行一道作业),多道程序设计技术(允许多个程序同时进入内存并运行),分时系统(把处理器的运行时间分成很短的时间片,按时间片轮流把处理器分配给各联机作业使用),实时系统(用于专用系统,有着非常严格的固定时间要求)、多处理机系统、网路系统、分布式系统、嵌入式系统
- 常见的操作系统: MSDOS(单用户单任务)、Windows(单用户多任务)、UNIX(多用户多任务)、Linux(多用户多任务)
- 国产操作系统: 银河麒麟、华为鸿蒙
- 操作系统的定义: 操作系统是一个大型的程序系统,它负责计算机系统软、硬件资源的分配;控制和协调并发活动;提供用户接口,使用户获得良好的工作环境。
- 操作系统的功能: 资源管理(进程管理、存储管理、设备管理、文件管理)、用户接口。
- 操作系统的特征: 并发、共享、异步、虚拟。
- 处理机的特权级: 目的:保护操作系统。
- 管态(系统态、核心态): 操作系统执行时机器所处的状态,又称处理机的特权级。在此状态下处理机可使用全部指令(包括一组特权指令);使用全部系统资源(包括整个存储区域)。
- 目态(用户态): 用户程序执行时机器所处的状态。在此状态下禁止使用特权指令,不能直接取用资源与改变机器状态,并且只允许用户程序访问自己的存储区域。转换: 访管中断。
- 中断 发生CPU会立即进入核心态,针对不同的中断信号,采取不同的处理方式。中断是CPU从用户态进入核心态的唯一途径。中断分为内中断和外中断。进程中断时,操作系统会保存CPU的运行环境,当进程再次运行时可以从中断的状态处继续运行。
- 第二章 进程管理
-
进程定义,进程特征,程序和进程的区别。
-
进程的状态及变迁。
-
进程管理: 进程控制、进程同步、进程通信、进程调度
-
进程同步(会使用PV操作解决问题),经典进程同步问题
例题 在测量控制系统中,数据采集任务把所采集的数据送往一单缓冲区;计算任务从该单缓冲区中取出数据进行计算。试写出利用信号量机制实现两任务共享单缓冲区的同步算法。
Semaphore: empty=1,full=0 采集进程: While(1) P(empty) Put data V(full) 计算进程: While(1) P(full) get data V(empty) -
进程通信: 共享内存、管道、消息、套接字(网络通信)
-
进程调度: 调度与分派,调度方式(抢占和非抢占) ,周转时间/带权周转时间计算,调度算法(FCFS、短作业优先、优先级、时间片轮转)
例题 设有四个进程,它们到达就绪队列的时刻、运行时间及优先级(此处优先级1为最低优先级,优先级5为最高优先级)如下表所示。若分别采用非抢占式优先级调度算法和可抢占式优先级调度算法,试给出各进程的调度顺序以及平均周转时间。
进程 到达时刻 运行时间 优先级 P1 0 8 1 P2 2 12 4 P3 8 3 5 P4 9 7 3 解答:(1)非抢占式:P1,P3,P2,P4
进程 到达时刻 运行时间 优先级 开始运行 暂停运行 完成时间 周转时间 P1 0 8 1 0 8 8 P3 8 3 5 8 11 3 P2 2 12 4 11 23 21 P4 9 7 3 23 30 21 平均周转时间:=(8+3+21+21)/4=13.25 (2)抢占式:
进程 到达时刻 运行时间 优先级 开始/继续运行 暂停运行 剩余运行时间 完成时间 周转时间 P1 0 8 1 0 2 6 P2 2 12 4 2 8 6 P3 8 3 5 8 11 3 P2 11 17 15 P4 9 7 3 17 24 15 P1 24 30 30 平均周转时间:=(3+15+15+30)/4=15.75 -
进程和线程: 进程是资源分配的最小单位,线程是CPU调度的最小单位(进程=火车,线程=车厢)
-
磁盘存储器管理: 移臂调度算法【FCFS、最短寻道时间优先算法、扫描(SCAN、电梯、SCAN改进算法)】、旋转调度
-
死锁 定义、原因(竞争资源、进程推进顺序不当)、死锁必要条件(1.互斥条件、2.请求和保持条件、3.不剥夺条件、4.环路等待条件)
-
处理死锁: 预防死锁、避免死锁(安全状态、银行家算法)、死锁检测(资源分配图)、死锁解除(剥夺和撤销)
例题 某时刻系统的A、B、C、D四种资源状态如下表所示:Pno Allocation Max Available P0 0012 0112 1540 P1 1000 1750 P2 1354 2356 P3 0014 0656 (1)系统中四类资源各自的总数是多少?
(2)请写出Need矩阵。
(3)当前系统状态是否安全?请写出一个安全序列。
(4)如果P1发出请求(0,4,2,0),是否可以满足该请求?如果可以,请给出安全序列。
解答:(1) 系统中四类资源各自的总数是多少?
(3,8,11,10)
(2) 请写出Need矩阵。
Pno Need P0 0100 P1 0330 P2 1002 P3 0642 (3) 当前系统状态是否安全?请写出一个安全序列。
P0,P2,(P1,P3)
(4) 如果P1发出请求(0,4,2,0),是否可以满足该请求?如果可以,请给出安全序列。
P0,P2,(P1,P3)
(5) 请分析利用银行家算法解决死锁问题时的局限性。
银行家算法要求每个进程事先给出其所需要资源的最大数量,这在实际环境中不现实。
- 第三章 存储器管理
-
存储器管理方式分类: 大小不等(分区、分段)、大小相等(分页)、段页式存储、虚拟存储器
-
地址映射: 静态地址映射和动态地址映射
例题 某虚拟存储器的用户空间共有32个页面,每页1KB,主存16KB。假定某时刻系统为用户的第0,1,2,3页分配的物理块号为5、10、4、7,而该用户作业的长度为6页,试将十六进制的虚拟地址0A5C、103C、1A5C转换成物理地址。
简答:0A5CH=(1010 0101 1100)2,页号为2,块号为4,则物理地址为:(01 0010 0101 1100)2,即125CH
103CH=(0001 0000 0011 1100)2,页号为4,缺页中断
1A5CH=(0001 1010 0101 1100)2,页号为6,越界中断
-
动态分区分配: 分配过程、放置策略及特点、碎片问题及拼接技术、分区回收算法。
-
页式存储管理: 概念、地址结构、页表、地址变换机构、快表的地址映射过程。了解多级页表原理。
例题 某系统具有多级存储系统,包括Cache、RAM和disk,并且启用虚拟存储器。已知访问Cache获取一个字word的时间为2ns,访问RAM的时间为10ns,访问磁盘的时间为10ms,并且Cache的命中率为95%,RAM的命中率为99%(Cache不命中的时候),试计算在该系统中访问一个字的平均时间。
解答:95%×2 + (2+10)×5%×99% + (2+10+107)×(1-95%)×(1-99%)
or
95%×2 + 5%×( 99%×(2+10) + 1%×(2+10+107))
-
页面换出机制: FIFO、OPT、LRU、Clock
例题 设分配给一个作业的内存块数为4,它还没有任何页装入内存,那么,对于下面的引用串:1,2,3,4,5,3,4,1,6,7,8,7,8,9,7,8,9,5,4,5,4,2时,采用FIFO、LRU、CLOCK算法时,访问过程中各将产生多少次缺页中断?
采用FIFO 算法:缺页次数:13次;
1 2 3 4 5 3 4 1 6 7 8 7 8 9 7 8 9 5 4 5 4 2 缺 缺 缺 缺 缺 缺 缺 缺 缺 缺 缺 缺 缺 1 1 1 1 5 5 5 5 8 8 8 8 2 2 2 2 2 1 1 1 1 9 9 9 9 3 3 3 3 6 6 6 6 5 5 5 4 4 4 4 7 7 7 7 4 4 换出 1 2 3 4 5 1 6 7 8 采用LRU算法:缺页次数:13次
1 2 3 4 5 3 4 1 6 7 8 7 8 9 7 8 9 5 4 5 4 2 缺 缺 缺 缺 缺 缺 缺 缺 缺 缺 缺 缺 缺 1 1 1 1 5 5 6 6 6 6 8 8 2 2 2 2 2 1 1 1 1 9 9 9 9 3 3 3 3 3 7 7 7 5 5 5 4 4 4 4 4 8 8 7 4 4 换出 1 2 5 3 4 1 6 7 8 采用CLOCK算法:缺页次数:13次
1 2 3 4 5 3 4 1 6 7 8 7 8 9 7 8 9 5 4 5 4 2 缺 缺 缺 缺 缺 缺 缺 缺 缺 缺 缺 缺 缺 < 1* 1* 1* 1*< 5* 5* 5* 5* 5 5< 8* 8* 8* 8* 8* 8* 8* 8 8< 8< 8< 2* < 2* 2* 2* 2< 2< 2< 1* 1 1 1< 1< 1< 9* 9* 9* 9* 9 9 9 9 9< < 3* 3* 3 3* 3* 3*< 6* 6* 6* 6* 6* 6*< 6*< 6*< 6*< 5* 5* 5* 5* 5* < 4* 4 4 4* 4* 4< 7* 7* 7* 7* 7* 7* 7* 7* 7< 4 4 4* 4* 换出 1 2 3 4 5 1 6 7 8 -
段式存储管理: 原理、地址结构、原理、段表、变换机构;分段与分页的区别。
-
段页式存储管理方式: 思想、原理、地址结构和地址转换机构。
例题 某请求分页系统,用户空间为32KB,每个页面1KB,主存16KB。某用户程序有7页长,某时刻该用户进程的页表如下:
页号 物理块号 是否在TLB 0 8 是 1 7 是 2 4 否 3 10 否 4 5 否 5 3 是 6 2 是 (1)计算两个逻辑地址:0AC5H、1AC5H对应的物理地址。
(2)已知主存的一次存取为1.5us,对于TLB表(快表)的查询时间可以忽略,则访问上述两个逻辑地址共耗费多少时间?
解答:(1)0AC5H:转换为二进制地址:101011000101,低10位为页内地址,所以页号为2,查页表,得块号为4,则物理地址为:1001011000101,即12C5H(4805);
1AC5H:转换为二进制地址:1101011000101,低10位为页内地址,所以页号为6,查页表,得块号为2,则物理地址为:101011000101,即0AC5H(2757)
(2)访问0AC5H:不在快表,两次访问内存:3us;访问1AC5H,在快表,访问一次内存,1.5us,所以总共4.5us
- 第四章 设备管理
- I/O设备分类: 块设备、字符设备、网络设备
- I/O控制方式: 程序I/O、中断、DMA、I/O通道
- 缓冲管理: 为了缓和CPU与I/O设备速度不匹配的矛盾,提高它们之间的并行性,在现代计算机系统中,几乎所有的I/O设备在与CPU交换数据时,都用了缓冲区。缓冲管理的主要职责是组织好这些缓冲区,并向进程提供获得和释放缓冲区的手段。
- 设备分配: 独占分配、共享分配、虚拟分配(SPOOLing技术)
- 第五章 文件管理
-
文件分类: 普通文件、目录文件、设备文件
-
文件存取: 顺序存取、随机存取
-
磁盘存储器的组织和格式: 存储容量、扇区编址方式
-
文件物理结构 :连续文件、串联文件(隐式链接、显示FAT)、索引文件【单级、多级、混合(UNIX、Linux)】
例题 在UNIX文件系统中,每个磁盘块大小为4K字节,保存一个磁盘块号需要4个字节,则文件的最大长度可以为多少个字节?
简答:一个索引块能存块号数量:4K/4=1K
三级索引块数量:1
二级索引块数量:1 + 1 * 1K
一级索引块数量: 1 + ( 1 + 1 * 1K) * 1K
数据块数量:(1 + ( 1 + 1 * 1K) * 1K)*1K + 10
文件大小:((1 + ( 1 + 1 * 1K) * 1K)*1K + 10)*4K
> 1K * 1K *1K * 4K = 4T
**例题 ** 设磁盘容量为2MB,磁盘块大小为1KB,从0开始编号,某文件数据顺序存储在5个磁盘块上(每个磁道仅含一个盘块)且分别位于60,120,21,900,300磁道上,且该文件的目录项位于51号磁道的某个物理块上,若上一次磁盘访问的是9号磁道。
(1) 若采用隐式链接,按顺序写出对磁盘的操作步骤及相应磁道号。
(2) 若采用FAT分配方法,FAT表存储在磁盘开始的位置,每个FAT表项占4B。现在要在720号磁道上为该文件尾部追加数据,按顺序写出对磁盘的操作步骤及相应磁道号。
解答:
(1) 9->51->60->120->21->900->300
(2) 2MB/1KB = 2K块
1KB/4B ,每个数据块存放256个FAT表项。2K个FAT表项共占据8个磁盘块
0-255 第0块;256-511第1块;512-767第2块;768-1023第3块
60,120,21在第0个FAT块, 300在第1个FAT块,900在第3个FAT块
因此,访问顺序9->51->0(读取60,120,21,900)->3(读取300)->1(FAT第300项写入720)->2(写入休止符)->720块追加数据
-
文件存储空间管理: 空闲文件目录、空闲块链表、位视图法、成组链接法(超级块)
例题 采用位示图管理一块容量为40GB的磁盘(块大小为4KB),则位示图需要占用多少字节的存储空间?若采用FAT作为文件的物理结构,则FAT占据多少存储空间?
**简答:**位示图需要的字节数计算:INT(40GB/4KB)/ 8
223<10*220 <2^24 24位,3字节。FAT表大小 = 3 * 10*2^20 = 30MB
例题 在 UNIX 系统中有空闲盘块栈如下图所示:
(1)现有一个进程要释放 3 个物理块,其块号为 156#、160#、220#,画出空闲盘块
栈的变化。
释放 156#
释放 160#
释放 220#, 将空闲盘块栈内容存入空闲块 220 中,空闲盘块栈变为:
(2)在(1)的基础上假定一个进程要求分配 5 个空闲块,请说明进程所分配到的盘块
的盘块号,并画出分配后的空闲盘块栈。
220,160,156,201,151
-
索引节点: 指在许多类Unix文件系统中的一种数据结构。每个索引节点保存了文件系统中的一个文件系统对象的元信息数据,但不包括数据内容或者文件名。
-
目录结构: 单级、二级、树形
-
文件共享: 共享索引节点法、符号链接法。
例题 在实现文件系统时,为了加快文件目录的检索速度,可利用“FCB分解法”。假设目录文件存放在磁盘上,每个盘块512B。FCB占64B,其中文件名占8B,通常将FCB分解为符号目录项和基本目录项两部分,其中符号目录项大小为10B:
(1)基本目录项大小为多少字节?
(2)假设某一目录文件共有254个FCB,试分别给出采用分解法之前和之后,对该目录文件分别的平均访问磁盘次数(最大访问磁盘次数除以2):
解答:
(1)基本目录项大小为多少字节? 64-8=56B
(2)假设某一目录文件共有254个FCB,试分别给出采用分解法之前和之后,对该目录文件分别的平均访问磁盘次数:
答:
分解前:FCB占用块数:254*64/512=32块,平均访问磁盘次数:(1+32)/2=16.5
分解后:FCB占用块数:254*10/512=5块,平均访问磁盘次数:(1+5)/2+1=4
- 实践项目
1.安装Linux虚拟机,熟悉Linux基本命令
man命令:shenkan@shenkan-virtual-machine:~$ man ls
输出反馈:ls命令的详细内容
sudo命令:shenkan@shenkan-virtual-machine:~$ sudo -h
输出反馈:切换用户身份命令的帮助
pwd命令:shenkan@shenkan-virtual-machine:~$ pwd
输出反馈:/home/shenkan
cd命令:shenkan@shenkan-virtual-machine:~$ cd /
输出反馈:无反馈
再次执行pwd命令,当前工作目录改变为:/目录
2.实现一个模拟shell
(1)编写三个不同的程序:cmd1.c, cmd2.c, cmd3.c分别编译成可执行文件cmd1, cmd2, cmd3
(2)编写主程序,模拟shell程序的功能,能根据用户输入的字符串(表示相应的命令名),去为相应的命令创建子进程并让它去执行相应的程序,而父进程则等待子进程的结束,然后再等待接收下一条命令。如果接收到的命令为exit,则父进程结束,如果接收到无效命令,则显示”command not found”,继续等待。
(3)在主函数编写中
pid = fork()会返回多个值,只需在fork()后使用多个判断语句即可。
pid<0表示错误,打印error之后退出
pid=0表示子进程运行,使用execl替换进程,替换为我们想要的进程,如cmd1.o。
pid>0表示父进程运行,使用wait(NULL)函数等待所有子进程的退出。
//test1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
char input[20];
pid_t pid=0;
// printf(“输入要运行的程序名$\n”);
// scanf("%s", input);
while(1)
{
printf(“输入要运行的程序名$\n”);
scanf("%s", input);
if(strcmp(input,“exit”) == 0)
{
printf(“父进程退出\n”);
printf("\n进程结束,pid:%d\n", getpid());
exit(0);
}
else if(strcmp(input,“cmd3”) == 0 || strcmp(input,“cmd2”) == 0 || strcmp(input,“cmd1”) == 0)
{
//创建子进程
pid = fork();
if(pid < 0)
{
printf(“vfork() error\n”);
exit(-1);
}
else if(pid == 0)
{
printf(“This is son, his pid is: %d\n”, getpid());
char path[80] = “/home/osclass/Class/test3/1/cmd/”;
char *lastName = “.o”;
strcat(path, input);
strcat(path, lastName);
execl(path,"",NULL);
}
else
{
printf(“This is father, his pid is: %d\n”, getpid());
pid_t temp = wait(NULL);
printf("\n进程结束,pid:%d\n", temp);
}
}
else
{
printf(“Command not found\n”);
// printf(“输入要运行的程序名$\n”);
// scanf("%s", input);
continue;
}
}
return 0;
}
结果
**3.**实现一个管道通信程序
//test2
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#define READ 0 //filedes[0]用于读
#define WRITE 1 //filedes[1]用于写
int main() {
/*
函数原型:pipe(int filedes[2])
参数含义:filedes[0]对应管道读端,filedes[1]对应管道写端
功能:pipe在内存缓冲区中创建一个管道,并将读写该管道的一对文件描述符保存在filedes所指数组中
返回值:成功返回0,失败返回-1
*/
int filedes[2];
pid_t pid1,pid2,pid3;//pid_t本质就是int
char buf[256]; //用作read的缓冲区,保存读取的字符
pipe(filedes); //创建无名管道
//改进的地方
pid1=1;pid2=1;pid3=1;
pid1 = fork();
if(pid1>0) pid2=fork();
if(pid1>0&&pid2>0) pid3=fork();
if (pid1 == 0) {
sleep(1);
printf(“正在产生子进程pid1:%d\n”,getpid());
close(filedes[READ]);
write(filedes[WRITE], “pid1\n”, strlen(“pid1\n”));
exit(0);
}
if(pid2 == 0) {
sleep(1); //挂起一秒
printf(“正在产生子进程pid2:%d\n”,getpid());
//子进程向父进程写数据,关闭管道的读端
close(filedes[READ]);
write(filedes[WRITE], “pid2\n”, strlen(“pid2\n”));
exit(0);
}
if (pid3 == 0) {
sleep(1);
printf(“正在产生子进程pid3:%d\n”,getpid());
close(filedes[READ]);
write(filedes[WRITE], “pid3\n”, strlen(“pid3\n”));
exit(0);
}
else {
//waitpid()会暂时停止目前进程的执行,直到有信号来或者子进程结束
pid1 = waitpid(pid1, NULL, WUNTRACED);
pid2 = waitpid(pid2, NULL, WUNTRACED);
pid3 = waitpid(pid3, NULL, WUNTRACED);
printf(“main pid: %d\n”,getpid());
printf(“wait pid: %d %d %d 返回信息\n”,pid1,pid2,pid3);
/父进程从管道读取子进程写的数据,关闭管道的写端/
close(filedes[WRITE]);
//read():读取的数据保存在缓冲区buf
read(filedes[READ], buf, sizeof(buf));
printf(“3个子进程传输的数据为:\n%s\n”, buf);
}
return 0;
}
结果
**4.**利用Linux****消息队列通信机制实现两个线程间的通信
命令:vim a.c
//a.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct MSG{
long mtype;//消息类型
char buf[100];//消息数据
};
struct MSG_read{
long mtype_read;//消息类型
char buf_read[100];//消息数据
};
void *write1(void *arg)
{
//1.获取key
key_t key = ftok("/",‘a’);
if(key==-1)
{
perror(“ftok”);
exit(-1);
}
int msqid = msgget(key,IPC_CREAT|0666); //2.通过key创建消息队列
if(msqid==-1)
{
perror(“msgget”);
exit(-2);
}
//3.发送消息
struct MSG msg1;
while(1)
{
msg1.mtype = 1;
fgets(msg1.buf,100,stdin);
int res = msgsnd(msqid,&msg1,sizeof(msg1.buf),0);
}
}
void *read1(void *arg)
{
key_t key = ftok("/",‘b’);
if(key==-1)
{
perror(“ftok”);
exit(-1);
}
int msqid = msgget(key,IPC_CREAT|0666); //2.通过key创建消息队列
if(msqid==-1)
{
perror(“msgget”);
exit(-2);
}
struct MSG_read msg;
while(1)
{
int res = msgrcv(msqid,&msg,sizeof(msg.buf_read),2,0); //收类型为2 的信息
if(res==-1){
perror(“msgrcv”);
exit(-3);
}
printf("%s",msg.buf_read);
}
}
int main()
{
pthread_t id1;
pthread_t id2;
//创建线程
int res = pthread_create(&id1,0,write1,0);
if(res){
printf("%s\n",strerror(res));
//exit(-1);
}
int res2 = pthread_create(&id2,0,read1,0);
if(res2){
printf("%s\n",strerror(res2));
//exit(-1);
}
pthread_join(id1,0);
pthread_join(id2,0);
return 0;
}
命令:vim b.c
//b.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct MSG{
long mtype;//消息类型
char buf[100];//消息数据
};
struct MSG_read{
long mtype_read;//消息类型
char buf_read[100];//消息数据
};
void *read2(void *arg)
{
//1.获取key
key_t key = ftok("/",‘a’);
if(key==-1)
{
perror(“ftok”);
exit(-1);
}
int msqid = msgget(key,IPC_CREAT|0666); //2.通过key创建消息队列
if(msqid==-1)
{
perror(“msgget”);
exit(-2);
}
struct MSG msg1;
while(1)
{
int res = msgrcv(msqid,&msg1,sizeof(msg1.buf),1,0); //收类型为2 的信息
if(res==-1){
perror(“msgrcv”);
exit(-3);
}
printf("%s",msg1.buf);
}
}
void *write2(void *arg)
{
key_t key = ftok("/",‘b’);
if(key==-1)
{
perror(“ftok”);
exit(-1);
}
int msqid = msgget(key,IPC_CREAT|0666); //2.通过key创建消息队列
if(msqid==-1)
{
perror(“msgget”);
exit(-2);
}
struct MSG_read msg1;
msg1.mtype_read = 2;
while(1)
{
fgets(msg1.buf_read,100,stdin);
int res = msgsnd(msqid,&msg1,sizeof(msg1.buf_read),0);
}
}
int main()
{
pthread_t id1;
pthread_t id2;
//创建线程
int res = pthread_create(&id1,0,write2,0);
if(res){
printf("%s\n",strerror(res));
//exit(-1);
}
int res2 = pthread_create(&id2,0,read2,0);
if(res2){
printf("%s\n",strerror(res2));
//exit(-1);
}
pthread_join(id1,0);
pthread_join(id2,0);
return 0;
}
命令:vim Makefile
//Makefile
all:a.c b.c
gcc a.c -pthread -o b.out
gcc b.c -pthread
命令:make
打开另一个终端窗口。分别键入命令:./a.out和./b.out
5.利用Linux共享内存通信机制实现两个进程间的通信
//sender.c
#include “common.h”
#include <stdio.h>
key_t key;
int shmid;
char * shmptr;
char input[SHM_SIZE];
sem_t * full;
sem_t * mutex;
void Init()
{
key = KEY_NUM;
shmid = GetShmId(key);
shmptr = shmat(shmid,NULL,0);
//semaphore init
full = sem_open(FULL_NAME,O_CREAT);
mutex = sem_open(MUTEX_NAME,O_CREAT);
}
void SaveMessage()
{
P(mutex);
strcpy(shmptr,input);
V(mutex);
V(full);
}
int main(int argc, char const *argv[])
{
init();
fgets(input, 1024, stdin);
SaveMessage();
printf(“Sender: Process End\n”);
return 0;
}
//receiver.c
#include “common.h”
key_t key;
int shmid;
char * shmptr;
char result[SHM_SIZE];
sem_t * full;
sem_t * mutex;
void Init()
{
key = KEY_NUM;
shmid = GetShmId(key);
shmptr = shmat(shmid,NULL,0);
full = sem_open(FULL_NAME,O_CREAT);
mutex = sem_open(MUTEX_NAME,O_CREAT);
}
void ReadMessage()
{
P(full);
P(mutex);
strcpy(result,shmptr);
V(mutex);
}
int main(int argc, char const *argv[])
{
Init();
ReadMessage();
printf(“Receiver : message is %s\n”,result);
SemDestroy();
printf(“Receiver : Process End \n”);
return 0;
}
Makefile
all : init sender receiver
.PHONY : clean
init : init.o common.o
cc -pthread -o init init.o common.o
sender : sender.o common.o
cc -pthread -o sender sender.o common.o
receiver : receiver.o common.o
cc -pthread -o receiver receiver.o common.o
init.o : common.h
sender.o : common.h
receiver.o : common.h
clean :
rm init
rm receiver
rm sender
rm *.o
结果
6.简单的文件系统
(1)在内存中开辟一个虚拟磁盘空间作为文件存储分区,在其上实现一个简单的基于多级目录的单用户单任务系统中的文件系统。在退出该文件系统的使用时,应将该虚拟文件系统以一个文件的方式保存到磁盘上,以便下次可以再将它恢复到内存的虚拟磁盘空间中。
(2)文件存储空间的分配可采用显式链接分配或其他的办法。
(3)空闲磁盘空间的管理可选择位示图或其他的办法。如果采用位示图来管理文件存储空间,并采用显式链接分配方式,那么可以将位示图合并到FAT中。
(4)文件目录结构采用多级目录结构。为了简单起见,可以不使用索引结点,其中的每个目录项应包含文件名、物理地址、长度等信息,还可以通过目录项实现对文件的读和写的保护。
(5)要求提供以下操作命令:
1)my_format:对文件存储器进行格式化,即按照文件系统的结构对虚拟磁盘空间进行布局,并在其上创建根目录以及用于管理文件存储空间等的数据结构。
2)my_mkdir:用于创建子目录。
3)my_rmdir:用于删除子目录。
4)my_ls:用于显示目录中的内容。
5)my_cd:用于更改当前目录。
6)my_create:用于创建文件。
7)my_open:用于打开文件。
8)my_close:用于关闭文件。
9)my_write:用于写文件。
10)my_read:用于读文件。
11)my_rm:用于删除文件。
12)my_exitsys:用于退出文件系统。
核心代码:
在自己的文件系统里创建一个liuxinrui文件夹,并在这个文件夹里创建一个名为1.txt的txt文本,运用“write”命令写入文本后再使用“read”命令对1.txt文本的内容进行读取。
代码参考:https://blog.csdn.net/qq_36285879/article/details/88771691
https://wenku.baidu.com/view/df9f1440b4daa58da1114a2e.html