进程创建
使用系统掉用接口 fork()函数 复制PCB
子进程的复制:使用了写时拷贝技术(代码共享,数据独有)
返回值:父子进程的返回值各有不同,父进程返回子进程的pid,子进程返回0,以实现代码分流
父子进程返回值可以反过来吗?
不行, 不光是代码分流的功能,还有进程控制, 父进程使用返回值控制子进程
父进程要等待子进程退出(避免僵尸进程),父进程要对子进程进行很多管理,而父进程可能有很多子进程,通过子进程pid才能管理相应子进程,如果不返回子进程pid,就没办法管理相应子进程
进程终止(如何退出一个进程)
进程退出要保存退出原因-为什么?
为了让父进程能够获取到子进程的退出原因,进而了解子进程运行的任务是否正确完成
进程退出场景:正常退出,结果正确 ---结果符合预期
正常退出,结果错误 ---结果不符合预期
异常退出,(异常崩溃) ---代码没有走到return (返回值不能作为判断标准)
异常是在可控范围内,不是程序真的崩溃,是遇到异常,被操作系统检测到,操作系统要求下进程自己退出,不是期望的返回值,不能作为评判标准
进程的退出方式:
-
main函数中的return语句 效果等同于exit
-
库函数 void exit(int status); 退出一个进程,退出时刷新缓冲区 + 很多其他释放操作
-
系统调用 void _exit(int status); 粗暴退出直接释放所有资源 exit是对_exit 的一层封装
_exit 没有刷新缓冲区的这一步,直接就全部将空间回收了
缓冲区相关:进程处理的所有数据都在内存上面,exit退出时,将数据刷新到缓冲区,关闭文件。而main函数中return也具有相类似的效果。而_exit直接把这块内存释放掉,也就无法在进行刷新了
进程等待:是什么: 为什么:
本质(功能) 父进程等待子进程的退出, 子进程先于父进程退出容易成为僵尸进程, 子进程退出的通知的消息来的时间是不确定的, 为了避免僵尸进程得推出,父进程就需要关注子进程得退出状态(操作系统发出的通知),但是子进程声明时间退出/这个推出通知什么时候发给父进程,父进程不确定,因此父进程只能一直等待这个退出通知 ---可以联想到快递的短信
如何实现进程等待: 操作系统提供的接口: wait waitpid 默认都是阻塞函数
-
wait接口 wait等待任意进程,waitpid等待任意子进程退出
-
阻塞:为了完成一个功能发起请求,当发现不具备完成条件时,则挂起等待
-
非阻塞:为了完成一个功能,发起调用,如果当前不具备完成条件,立刻报错返回 (汇报返回原因)
-
阻塞与非阻塞的区别(本质):发起调用后,是否立即返回
status在低十六位的高八位上
获取返回值:子进程返回的数据时一个字节 接收用用四个字节 获取子进程的退出返回码
最常见错误:段错误
低7位存储进程退出的信号值是否为0:并且这低7位用于判断一个进程是否是正常退出的
0表示正常退出,如果是正常退出,获取返回值,如果是异常退出,获取返回值就没意义了,高八位用于打印,低7位用于打印
进程程序替换
程序替换: 是什么?为什么? 怎么实现?
一个进程运行什么程序,取决于虚拟地址空间中的代码段映射物理地址中的哪一个真是代码区域,意味着如果将虚拟地址中的代码映射到物理内存的代码位置替换成另一个程序的位置,那么进程将运行另一个程序
-
是什么:替换代码段映射的代码位置成为另一个内存区域代码的位置,并且重新初始化数据段
-
为什么:大多数情况下,我们创建一个子进程是为了让子进程运行另一个程序,做其他的任务
-
实现:使用一套接口来实现程序替换,不是单独的 统称为exec函数族 都能进行程序替换,不是组合起来才能实现,他们各有不同; (环境变量中会有定义文件路径)
自己写微型shell,shell运行原理
Shell编程就是shell脚本编程 语法也基本与其他向当,执行的多为命令行命令等 不需要编译 通过 ./ *.sh 直接运行
命令行解释器:从标准输入读取数据(scanf),对字符串进行解析。检测是否有这个命令
根据这个命令创建一个进程,让这个进程运行这个命令(程序替换)
读取输入--》命令解析--》创建子进程--》程序替换(子进程)--》进程等待(父进程)
printf 输出的分析:把数据写入到终端里
回车符\n用于自动刷新缓冲区,如果没有可以用fflush手动刷新
scanf从键盘读取数据:根据冯诺依曼,键盘拿到数据先放到输入缓冲区,scanf再从缓冲区里读取数据
特性:遇到空格就截至,下次再从空格后面继续。现在让它以换行为终止。
scanf与sscanf 的区别:
重点了解:字符串解析 linux开发:网络层面,文件IO层面 字符串层面
catch cpu中的高速缓存,缓存用于连接cpu与内存,速度比内存更高,但造价很高