进程控制部分(包括自己实现minishell)

进程控制部分:
面试题:创建一个进程的流程
进程创建:(理解)
流程: fork,vfork,clone-》复制-》修改-》创建完毕
fork(具有写时拷贝)
写时拷贝技术:初始阶段父进程和子进程指向的是同一个位置,只有当数据发生改变时,才给子进程重新开辟一块物理内存
数据独有,代码共享的理解: 各自有各自的虚拟地址空间和页表,并且当数据发生改变时,子进程重新开辟物理内存,所以数据独有,代码段具有只读特性,代码段的数据不可更改,因此父子进程几乎一直指向同一块内存。

vfork(父进程阻塞):与父进程共用同一块虚拟地址空间—子进程先运行,为了防止调用栈混乱,因此父进程调用vfork会阻塞,阻塞到子进程退出或者子进程程序替换,开辟内存创建自己的地址空间。子进程exit退出或者程序替换之后,父进程才开始运行,进程return后将资源释放,父进程则调用栈混乱,因此vfork创建在子进程不能在main中return 退出,子进程把虚拟地址资源释放了,等到父进程可以运行的时候,资源已经被释放了,父进程就崩溃了。
vfork存在的意义
是快速创建子进程,并且子进程是专门用来运行其他程序的。共用地址空间可以减少子进程数据拷贝,但是fork通过写时拷贝技术实现后,vfork就被淘汰了。

fork,vfork,clone都是调用克隆

进程终止:(就是进程退出)

终止场景:(超市买方便面的故事)
    正常退出,结果符合预期(运行到return)程序运行完毕
    正常退出,结果不符合预期,程序运行完毕
    异常退出(没有返回值 )程序没有运行完毕
终止方式:
    1)main函数中return ,退出前刷新缓冲区
	2)调用exit(int status)函数  库函数 --》退出前会刷新缓冲区,做退出的收尾工作,任意位置调用都是退出进程
	3)调用_exit(int status )函数  系统调用接口--》直接退出,释放资源,任意位置调用都是退出进程,但是退出不会刷新缓冲区,缓冲区的数据被丢弃。
 如果没有\n数据会写入缓冲区中,并不会直接写入文件中,等到刷新缓冲区的时候,数据才会写入到缓冲区中,return 0会刷新缓冲区,exit也会刷新缓冲区,_exit退出时不会刷新缓冲区。
man 1号手册:命令手册   2号:系统调用手册   3号:库函数手册
返回值:
      echo $? 查看一个进程的返回值

错误编号:每个系统调用执行完毕后都会重置进程中errno这么一个全局变量,这个全局变量中存储着当前调用的系统调用接口错误编号,当系统调用接口出错,用户就可以通过errno获取系统调用错误原因
进程等待:(问的比较多)
等待子进程退出,释放资源,获取返回值(退出原因),避免产生僵尸进程父进程不知道子进程什么时候退出,这时候如果子进程退出了,操作系统通知的时候,父进程没关注,子进程成为僵尸进程。 为了避免这种现象(不知道子进程何时退出),父进程一直等待。凡是涉及到多进程的都要考虑进程等待
如何等待?
pid_t wait(int *status)
pid_t taitpid(pid_t pid,int *status,int options)
wait():阻塞函数,一直等待任意一个子进程的退出,子进程退出后获取返回值,放入到传入的参数status中,如果一直没有进程退出,就会一直等着(阻塞),可以通过opt将操作设置成非阻塞。
阻塞:为了完成功能发起调用,如果当前不具备条件,则一直等待,直到完成后返回(面做好了才能吃)

非阻塞:为了完成功能发起调用,如果当前不具备条件,则立即报错返回 ,非阻塞轮询操作,弄一个循环,每隔一会,看看有没有子进程退出。

区别:调用功能当前不具备完成条件是否立即返回(泡面是否烫的故事)

perror():打印出错原因
errno是一个全局变量,存储每次系统调用出现的错误原因编号
waitpid(int pid,int*statu,int opt):可以等待一个任意子进程退出,或者等待指定子进程退出,可以设置为非阻塞
waitpid()的返回值:
-1表示出错
==0表示没有子进程退出

0表示退出子进程的pid
option:WNOHANG:将waipid设置为非阻塞,意味着没有子进程退出,则立即报错返回 0:默认阻塞

status(4个字节):低16位中的高8位存的是子进程退出返回值,低8位中的高1位存储core_dump标志的核心转储:status中的低7位中保存的是进程的异常退出信号值,若为0,表示进程正常退出,若非0,进程因为一个异常而退出,这时候retval就不能作为进程任务处理结果判断标准。程序异常退出时保存程序的运行信息,便于实时调试,默认关闭。若低8位中的低7位是0,表示没有异常退出信号,表示程序正常退出,,否则表示程序异常退出,返回值将没有意义。

获取低7位:status&0x7f
获取低16位中的高8位:(status>>8)&0xff

返回值:
若WAHANG被指定,没有子进程退出则立即报错返回0,错误:返回-1
程序替换:替换一个进程正在运行的程序,让子进程完成一个其他的任务,exec函数族execl/execlp/execle/execv/execvp/execve(系统调用),其他是库函数

回顾:
如何管理?
先描述再组织进行管理。

系统调用和库函数的关系?
上下级调用关系,库函数是对系统调用的一层封装

进程与线程的概念
进程:进行中的程序,进程就是pcb(task_struct)
描述信息:内存指针,程序计数器,上下文数据,进程的表标识符(pid),进程状态,优先级,IO状态,记账

查看进程
ps-ef aux /proc pid_t getpid()
进程创建:
fork;通过复制调用进程(父进程),创建一个新的进程(子进程)
复制:复制pcb -----代码共享,数据独有
创建子进程的目的:分摊压力,完成其他任务
通过返回值分辨父子进程。父进程返回子进程的pid,子进程返回0

笔试题
fork();1
if(fork()2&&fork()3||fork()4)
fork();5
16个
0
|-1
|-2
|-4
|-5
|-3
|-4
|-5
|-5
|-2
|-4
|-5
|-3
|-4
|-5
|-5
for (int i = 0; i<2 i++)
{
fork();
printf("-");
}
一共打印8个-
如果加入\n就是6个
因为子进程复制了父进程的所有数据

进程的优先级:决定cpu资源的优先分配权的一个评级
ps -l PRI NI PRI=PRI+NI
renice nice 调整优先级
nice值得范围:-20-19
竞争性:狼多肉少,因此每个狼之间都具有竞争性
独立性:进程间相互独立
并行:cpu资源足够,多个程序同时运行
并发:cpu资源不够,多个程序切换运行,同时推进

环境变量:保存系统运行环境参数的变量
命令:
env echo set export unset
常见的环境变量:
HOME SHELL PATH
代码中的操作以及特性:

程序地址空间----内存描述符 mm_struct
作用:保持进程的独立性,通过页表映射物理地址,充分利用物理内存,增加内存访问控制
进程控制:
进程创建: fork, vfork存在的意义是快速创建子进程,并且子进程是专门用来运行其他程序的。共用地址空间可以减少子进程数据拷贝
clone
进程终止:进程退出场景–退出方式 return exit(库函数) _exit(系统调用);errno perror streror;
$?获取进程的返回值
进程等待:等待子进程状态改变(子进程的终止)—避免产生僵尸进程
pid_t wait(int statu)–阻塞等待任意一个人子进程退出,没有子进程退出就一直等待,获取子进程退出返回值,返回退出子进程的pid

waitpid(int pid,int *statu,int opt) statu:获取子进程的返回值,返回值只用了两个字节

子进程返回值获取:int的4个字节中高16位没有使用,低16位中的高8位—子进程退出码,低16位中的低8位—core dump标志;低7位—异常信号退出值(statu>>8)&0xff statu&0x7f>0表示信号异常退出

默认阻塞:等待任意一个人子进程/指定子进程退出,没有子进程退出则一直等待,但是waitpid第三个参数可以将waitpid设置为非阻塞,
没有子进程退出则立即报错返回0
阻塞:为了完成功能发起调用,如果当前不具备条件,则一直等待,直到完成后返回
非阻塞:为了完成功能发起调用,如果当前不具备条件,则立即报错返回

程序替换:
替换进程所运行的程序,替换代码段,初始化数据段,重新建立进程虚拟地址空间与物理内存映射关系,重新从main程序开始运行
程序替换:替换一个进程正在运行的程序,替换一个pcb映射在内存中的代码和数据,说白了就是让这个进程运行另一个程序,更新页表信息,让子进程指向内存中的另一个程序

为什么替换:让子进程完成其他功能

如何替换:exec函数族
execl:execl(const char* path,const chanr* arg,…)
使用path这个路径的程序,替换当前要进行的程序
Arg参数一个个赋予进来

       execlp(const char*file,const char* arg):相当于给了路径,直接给出文件名,前提是程序在指定的路径下存放
       execle(const char*path,const char* arg,...const env[])

这个环境变量自己设置,上边没有的是,父进程什么环境变量,子进程就是什么环境变量。
Execv(const path,char const argv[])
execvpe
execve(系统调用)

execl和elecv的区别:参数的赋予是以指针数组赋予还是以不定参数形式赋予
有p和无p的区别:第一个参数,程序名称是否需要路径,有p的可以不用路径
有e和无e的区别:要运行的程序,环境变量是否由用户自己设置
v和l的区别:程序的运行参数是函数的参数平铺或者直接组织成为字符串指针数组给与

程序替换就是:将进程的虚拟地址空间所映射在物理内存的区域进行改变,改变成另一个程序在内存中的位置,更新页表信息,重新初始化虚拟虚拟地址空间。
Pcb没有变,但是运行的程序不一样了。

目的:为了让进程运行另一个程序,

加载程序——》创建pcb——》建立映射

自己实现minishell(简单的命令行解释器)这块不懂
Shell是一个命令行解释器

shell处理流程:
while(1)
{
1、获取标准输入(等待键盘敲的啥信息)[ls -a -l]
Scanf() fgets()->从文件流指针中读取数据
2、对输入字符串进行解析(获取程序名称+参数)
3、创建子进程
4、进程等待(父进程)
5、子进程进行程序替换
}
为什么不在当前程序去运行这个命令呢?
字符串解析:把空白部分替换成\0
isspace:检测空白字符
直接按一个回车
此时缓冲区里只有一个回车,取不出来,会导致死循环

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值