Linux-进程控制

一、进程创建

  • 进程创建:进程的创建流程以及接口
  • 创建一个进程:创建一个pcb,因为pcb在linux下是一个task struct结构体,这个结构体存放在内核中,只能通过系统调用接口实现创建

pid_t fork(void)

流程:clone()
1、创建pcb;
2、复制信息;
3、内存数据发生改变的时候为子进程重新开辟空间,拷贝数据(写时拷贝技术)
写时拷贝技术:子进程创建出来后,与父进程映射访问同一跨物理内存,当物理内存中数据即将发生改变时,重新位子进程开辟物理内存,拷贝数据过去;(为了避免直接给子进程开辟空间,拷贝数据,而子进程不使用,降低了进程创建效率,造成内存冗余数据)

pid_t vfork(void)

创建一个子进程,父子进程共用同一个虚拟地址空间共用同一块虚拟地址空间,使用同一个栈,则若父子进程同时运行就会造成调用栈混乱;因此让子进程先运行,直到子进程退出或者程序替换后有了自己的地址空间(在原有的地址空间中子讲程的调用就都出栈了)

因此父进程调用vfork创建子进程,vfork的调用,并不会立即返回,会被阻塞,直到子进程exit()退出/进行了程序替换,重新创建了自己的虚拟地址空间之后,父进程才会vfork返回进行运行

但是要注意,vfork创建的子进程,不能在main函数中使用return退出,因为子进程使用’return退出的时候释放了所有资源,父进程运行的时候资源是错误的

二、进程终止

1、进程退出的场景:

  • 正常退出,结果符合预期,正常退出:完成任务,按照逻辑运行到return或者exit地方退出
  • 正常退出,结果不符合预期
  • 异常退出,结果不能作为判断基准,异常退出:没有完成任务,程序运行到半途因为某些原因崩溃退出了

2、进程的返回值,只用了一个字节来保存
3、在linux系统中erron.h这个头文件中有两个全局变量:

  • int errno;用于保存每次系统调用的错误编号
  • char *sys_errlist[] :系统调用的错误原因描述
  • perror(char* info):打印上一次系统调用的错误原因在info之后
  • strerror(int errno):根据给定的erron来获取错误信息

4、进程退出方法:

  • main函数中的return;(普通函数中的return只能退出函数,不能退出进程)
  • void exit(int status);库函数,退出调用进程,将status作为返回值返回给父进程;可以在程序的任意位置退出一个进程;exit退出进程时会刷新缓冲区,将缓冲区中的数据写入文件;文件数据在使用库函数的IO接口进行数据写入的时候,数据不会直接被写入文件,而是先写入到了一块内存中(缓冲区),只有在刷新缓冲区的时候,数据才会被真正写入文件(目的是为了减少IO次数,提高IO效率)
  • void_exit(int status);系统调用接口,退出调用进程,将status作为返回值返回给父进程;可以在程序的任意位置退出一个进程;_exit退出进程时,直接释放资源,不会刷新缓冲区
  • exit与_exit区别: exit是库函数,_exit是系统调用接口

5、 缓冲区,\n,刷新缓冲区的作用

  • printf打印数据,实际上是把数据交给显示器,把数据写入标准输出文件,让显示器显示出来—涉及到了不同设备之间的数据交互
  • 如果有很多小的数据要频繁的交给显示器,效率是比较低的
  • 因此操作系统做了一种优化 :把很多小的数据放到内存中缓冲起来,积累成一个大的数据,然后让显示器一次性取出来–只有一次设备间的数据交互
  • 因此printf并没有立即把数据交给显示器,而是放到了内存中((缓冲区),等到程序退出的时候,或者刷新缓冲区的是时候才会把数据交给显示器
  • \n除了换行的作用之外,还有一个作用,就是刷新缓冲区的作用(特定显示器设备–通常称之为标准输出)

三、进程等待

1、 概念:

  • 父进程等待子进程退出,获取子进程返回值,允许操作系统释放子进程资源;避免产生僵尸进程
  • 僵尸进程的产生原因:因为子进程先于父进程退出,为了保存退出返回值,而无法完全释放资源产生的。
  • 不管子进程是正常退出,还是异常退出,只要是退出了,没有被父进程等待处理,就都会成为僵尸进程。

2、 如何等待:

  • pid_t wait(int* status);-----阻塞函数
    处理退出的子进程,那么如果调用这个接口的时候没有子进程已经退出,则会使父进程阻塞等待,直到有子进程退出
    status:输出型参数–用于获取退出子进程的返回值;
    返回值:成功则返回处理的退出子进程的pid;失败则返回-1(比如没有子进程)
  • pid_ t waitpid(pid_t pid, int *status, int options);
    pid>0:则等待指定的子进程;pid=-1:则等待任意一个子进程
    options=0:则默认阻塞等待子进程退出;options=WNOHANG则waitpid为 非阻塞,若没有子进程已经退出,则立即返回
    waitpid返回值为0则表示当前没有子进程退出,-1则出错
    wait/waitpid不是子进程退出的时候才回收,而是只要有已经退出的子进程,就 都可以回收
    非阻塞操作,通常需要循环处理,否则一次处理不成功,总不能不处理了,得循环判断能否进行处理,直到成功为止

3、阻塞、非阻塞

  • 阻塞:为了完成一个功能发起调用,当时当前若不具备完成功能的条件;则一直挂起等待,直到条件满足完成功能后返回
  • 非阻塞:为了完成一个功能发起调用,当时当前若不具备完成功能的条件;则立 即报错返回

6、其他

  • 在获取返回值之前,应该先通过低7位,判断进程是否是正常退出的(正常退出,则异常退出信号值为0;否则则大于0)

  • 一个进程在运行中,有可能会突然崩溃,这是由于一些异常引起的;进程自己怎么知道自己发生了异常呢? ?
    一个病人,生什么病是医生告知的;一个进程是否产生了某个异常,是操作系统检测到的,通知的进程;而信号就是进程中的异常通知的值,不同的值表示不同的异常

  • 返回值的获取:
    1、只有进程正常退出的时候,获取进程的返回值,才有意义
    2、判断进程是否正常退出:通过status数据的低7位是否为0,若为0,这进程是正常退出
    3、获取返回值:返回值存储在status的低16位中的高8位 ;因为返回值只用了一个字节保存,因此进程的返回值最好不要大于255

status&0x7f :获取程序异常退出信号值
(status >>8)& 0xff :获取程序的退出返回值
WIFEXITED(status):判断进程是否正常退出,正常则退出则返回真
WEXITSTATUS(status):获取退出子进程的返回值
core dump,核心转储其实就指的是程序异常退出时,将退出前的程序运行信息保存下来(默认是关闭的:程序中有很多敏感数据,有隐私泄露风险;保存运行数据需要占用磁盘空间)

四、程序替换

  • 概念:替换一个程序进程正在运行的程序
    在一个进程中,可以先将另一段程序加载到内存中,将自己的页表映射到新的程序位置,初始化pcd中的虚拟地址空间中的代码和数据段信息;这时候这个pcd(当前进程)则运行另一段程序
  • 程序替换:替换一个进程正在调度运行的程序;
    程序只是一堆死代码,运行时会被加载到内存中(依然还是一堆数据)﹔如何动起来,是pcb干的事情;
    程序替换说白了就是,重新加载另一个程序到内存中;然后将现有的一个pcb的内存指针所指向的内存空间指向这个新的程序〈更新页表映射信息),则这个现有的pcb就跑去调度这个新的程序了;
  • 使用场景:fork创建一个子进程,代码共享数据独有;—父子进程干的事情是一样的;但是通常情况下,我们创建一个子进程的目的并不是为了让子进程与自己一样干相同的事情,而是让子进程运行调度一个新的程序,让它去干其他的事情,完成其他的任务。这时候就用到了程序替换

exec函数族

  • int execve(char *filename,char *argv[],char *env[]);
    filename:新的程序代码文件名称(带有路径的文件名)
    argv:新程序的运行参数信息
    env:新程序的环境变量信息
    返回值:失败返回-1,成功则进程已经运行新的程序了
  • int execl(const char *path, const char *arg, …);
    path:带有路径的新程序名称;
    arg…:新程序的运行参数 ,逐个赋予,最后以NULL结尾
  • int execlp(const char *file,const char *arg, …);
    file:只需要一个文件名即可(会去PATH环境变量指定的路径下去找相应的程 序) int
  • execle(const char *path, char *arg, …,char * env[]);
    env:为新程序自定义环境变量
  • int execv(const char *path, char *const argv[]);
  • int execvp(const char *file, char *const argv[]);

有没有p的区别:程序名称是否需要带路径
有没有e的区别:是否自定义环境变量
l和v的区别:程序运行参数的赋予方式不同

在这里插入图片描述

  • 自主实现一个minishell,完成shell的基本功能

fflush(stdout); 手动刷新缓冲区
fgets(buf,1024,stdin);从标准输入读取数据
buf[strlen(buf)-1]=’\0’;将后的换行符修改为字符串结束标志
execvp(argv[0], argv);子进程进行程序替换
waitpid(pid, NULL, 0);阻塞等待子进程命令执行完毕
不能直接对shell进行程序替换,因为替换后就没有shell

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值