《信息安全系统设计与实现》学习笔记6

一、学习笔记

1.进程

进程是对映像的执行。在操作系统内核中,每个进程用一个独特的数据结构表示,叫作进程控制块(PCB)或任务控制块(TCB)等。定义一个PROC结构体来表示进程。

typedef struct proc{
  struct proc *next;
  int *ksp;
  int pid;
  int ppid;
  int status;
  int priority;
  int kstack[1024];
}PROC;

其中,next是指向下一个PROC结构体的指针,用于在各种动态数据结构(如链表和队列)中维护PROC结构体。ksp字段是保存的堆栈指针。当某进程放弃使用CPU时,它会将执行上下文保存在堆栈中,并将堆栈指针保存在PROC.ksp中,以便以后恢复。pid是标识一个进程的进程ID编号,ppid是父进程ID编号,status是进程的当前状态,priority是进程调度优先级,kstack是进程执行时的堆栈。

2.多任务处理系统MT的组成

(1)type.h文件

type.h文件定义了系统常数和表示进程的简单PROC结构体。

(2)ts.s文件

ts.s在32位GCC汇编代码中可实现进程上下文切换。

(3)queue.c 文件

queue.c文件可实现队列和链表操作函数。enqueue()函数按优先级将PROC输入队列中。 在优先级队列中,具有相同优先级的进程按先进先出(FIFO)的顺序排序。dequeue()函数 可返回从队列或链表中删除的第一个元素。printList()函数可打印链表元素。

(4)t.c 文件

t.c文件定义MT系统数据结构、系统初始化代码和进程管理函数。

3.进程同步

进程同步是指控制和协调进程交互以确保其正确执行所需的各项规则和机制。最简单的进程同步工具是休眠和唤醒操作。

(1)睡眠

当某进程需要某些当前没有的东西时,它就会在某个事件值上进入休眠状态,该事件值表示休眠的原因。为实现休眠操作,需要在上面PROC结构体中添加一个event字段,并实现ksleep(int event)函数,使进程进入休眠状态。PROC结构体要添加的如下:

  int event;
  int exitcode;
  struct proc *child;
  struct proc *sibling;
  struct proc *parent;

ksleep()的算法为:
1.record event value in PROC.event:running->event = event;
2.change status to SLEEP: running->status = SLEEP;
3.for ease of maintenance, enter caller into a PROC *sleepList enqueue(&sleepList, running);
4.give up CPU:
由于休眠进程不在readyQueue中,所以它在被另一个进程唤醒之前不可运行。因此, 在让自己进入休眠状态之后,进程调用tswitch()来放弃使用CPU。

(2)唤醒

多个进程可能会进入休眠状态等待同一个事件,因为这些进程可能都需要同一个资源。当某个等待时间发生时,另一个执行实体(可能是某个进程或中断处理程序)将会调用kwakeup(event),唤醒正处于休眠状态等待该事件值的所有程序。如果没有任何程序休眠等待该程序,kwakeup就不工作,即不执行任何操作。

4.进程终止

在操作系统中,进程可能终止或死亡,进程能以两种方式终止:
正常终止:进程调用exit(value),发出_exit(value)系统调用来执行在操作系统内核中的kexit(value)。
异常终止:进程因某个信号而异常终止。
在这两种情况下,当进程终止时,最终都会在操作系统内核中调用kexit()。

(1)kexit()算法

(2)进程家族树

使用进程树,更容易找到进程的子进程。

(3)等待子进程终止

在任何时候,进程都可以调用内核函数pid = kwait(int *status)等待僵尸子进程。如果成功,则返回的pid是僵尸子进程的pid,而status包含僵尸子进程的退出代码。此外,kwait()还会将僵尸子进程释放回freeList以便重用。

5.Unix/Linux 中的进程

(1)进程来源

当操作系统启动时,操作系统内核的启动代码会强行创建一个PID=0的初始进程,即通过分配PROC结构体(通常是proc[0])进行创建,初始化PROC内容,并让运行指向 proc[0]。然后,系统执行初始进程P0。P0继续初始化系统,包括系统硬件和内核数据结构。然后,它挂载一个根文件系统,使系统可以使用文件。在初始化系统之后,P0复刻出一个子进程P1,并把进程切换为以用户模式运行P1。

(2)登陆进程

P1复刻了许多LOGIN进程,每个终端上一个,用于用户登录。每个LOGIN进程向stdout显示一个login:以等待用户登录。用户账户保存在/etc/passwd和/etc/shadow文件中。每个用户账户在表单的/etc/passwd文件中都有一行对应的记录:name:x:gid:uid:description:home:program

(3)sh 进程

当用户成功登录时,LOGIN进程会获取用户的gid和uid,从而成为用户的进程、它将 目录更改为用户的主目录并执行列出的程序,通常是命令解释程序sh:现在,用户进程执 行sh,因此用户进程通常称为sh进程。

(4)进程的执行模式

在Unix/Linux中,进程以两种不同的模式执行,即内核模式和用户模式,简称Kmode和Umode。在每种执行模式下,一个进程有一个执行映像。在进程的生命周期中,会在Kmode和Umode之间发生多次迁移。每个进程都在Kmode下产生并开始执行。在Kmode模式下,通过将CPU的状态寄存器从K模式更改为U模式,可轻松切换到Umode。但是,一旦进入Umode,就不能随意更改CPU的状态了。Umode进程只能通过以下三种方式进入Kmode:

①中断

中断是外部设备发送给CPU的信号,请求CPU服务。当在Umode下执行 时,CPU中断是启用的,因此它将响应任何中断,在中断发生时,CPU将进人Kmode来处 理中断,这将导致进程进入Kmode。

②陷阱

陷阱是错误条件,这些错误条件被CPU识别为异常,使得CPU进入Kmode来处理错误。在Unix/Linux中,内核陷阱处理程序将陷阱原因转换为信号编号,并将信号传递给进程。对于大多数信号,进程的默认操作是终止。

③系统调用

系统调用(简称syscall)是一种允许Umode进程进入Kmode以执行 内核函数的机制,当某进程执行完内核函数后,它将期望结果和一个返回值返回到Umode, 该值通常为0(表示成功)或-1(表示错误)如果发生错误,外部全局变量errno会包含一个ERROR代码,用于标识错误。

6.I/O重定向

文件流和文件描述符

sh进程有三个用于终端I/O的文件流:stdin(标准输入),stdout(标准输出)和stderr(标准错误)。每个流都是指向执行映像堆区中FILE结构体的一个指针

每个文件流对应Linux内核中的一个打开文件。每个打开文件都用一个文件描述符(数字)表示。stdin、stdout、stderr的文件描述符分别为0、1、2。当某个进程复刻出一个子进程时,该子进程会继承父进程的所有打开文件。因此,子进程也具有与父进程相同的文件流和文件描述符。

7.管道

管道是用于进程交换数据的单向进程间通信通道。管道有一个读取端和一个写入端。可从管道的读取端读取写入管道写入端的数据。

二、问题与解决办法

这周上课老师讲到pwd命令,pwd命令的作用是当你不确定当前位置时,可以用它查看当前目录的绝对路径,最后老师让我们思考pwd命令的实现。

1.相关知识

因为系统的文件组织是树形的,所以可以从当前目录逐层向根目录查找。系统通过inode节点来管理文件,所以每个文件会有一个inode号。每个目录下有两个特殊的文件名".", "..",分别表示当前目录和父目录。

2.我的思考

既然是要找文件的绝对路径,所以可能会用到opendir(),readdir(),closedir()这几个函数,大概思路是查看当前目录(即.)和父目录(即..)的inode,如果两个inode不相等,说明还没有到根目录,进入父目录里继续重复这一步骤,如果两个inode相等,说明就到了根目录,然后依次输出路径名即可。
我先把上面的函数全部man了一下

虽然思路看起来比较明确,但是从思路到实现感觉还是要干好多好多,我先上网找了一个能实现的代码,看看它都实现了什么函数

这个程序里有三个函数,一个是获取inode的函数,一个是用来一层层深入目录的函数,一个是将inode转化为文件名的函数。从头文件和具体函数来看,实现pwd确实要用到这些opendir(),readdir(),closedir()其中的函数,说明我的思路大概是没错的。此外我还发现了一个chdir()函数,这是干什么的?让我继续man一下

发现它的作用是改变当前的工作目录。了解的差不多了,让我编译一下

运行得出的结果和直接pwd的结果一样,让我再换个目录试试

结果也是正确的,说明这个程序也是正确的。

三、学习感悟

上网查完pwd的指令实现,我发现我需要学习的东西还有很多,虽然说出来它如何实现很简单,但是到具体上手写的时候还是有相当大的难度的。另外就是我发现我C语言的基础还是差了一点。对于结构体什么的也不是太熟悉。但是我觉得它和数据结构写的代码很像,就是换了一个平台而已。所以,还是要继续加油。争取有一天能自己实现一条指令。

posted @   20191316王秋雨  阅读(57)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示