linux中的进程控制


一、创建

创建:
进程就是pcb,创建进程就是创建pcb

1.pid_t fork(void);

功能:通过复制父进程创建一个子进程
参数:无
返回值:
对于父进程返回子进程的pid,
对于子进程返回0,失败返回-1

2.pid_t vfork(void);

功能:通过复制父进程创建一个子进程(共用同一个虚拟地址空间)
特性:vfork创建子进程后,父进程阻塞,直到子进程exit退出或者程序替换之后才会开始运行。

因为父子进程共用同一个虚拟地址空间,因此父子进程是数据共享的,
任意一个进程对原有数据的修改都会影响到另一方;
并且共用同一个栈,因为函数调用压的都是同一个栈,
因此同时运行会造成栈混乱,以及数据影响,
所以让子进程先运行,子进程退出或者程序替换后,父进程才能运行。

父子进程数据独有,各有各的数据,否则运行会相互影响。
为了独立,又为了提高效率,避免资源浪费,因此采用了写时拷贝技术:

3.写时拷贝的思想:

子进程刚创建时,让子进程与父进程一样,映射到同一块物理内存,
但是如果某块内存中的数据即将发生改变(任意一方要修改),
则给子进程针对这一块重新开辟空间,拷贝数据过去。

二、终止

终止:如何退出一个进程

man手册 main 1-命令 2-系统调用接口 3-库函数

1. 在main函数中return;

(return只有在mian函数中才是退出程序的运行)

2. 库函数:void exit(int retval);

可以在任意位置调用退出程序的运行 头文件:stdlib.h

3. 系统调用接口:void_exit(int status);

可以在任意位置调用,退出程序的运行 头文件:unistd.h

库函数与系统调用接口的关系:库函数封装了系统调用

库函数exit与系统调用_exit的区别:退出程序前是否会刷新缓冲区

(1)exit与return在退出程序前都会刷新缓冲区,将数据写入文件;
(2)_exit调用直接退出,不会刷新缓冲区,而是直接释放资源
    (有可能存在缓冲区中的数据丢失)

return后面的数字和exit的参数status的作用:

设置进程的退出码
退出码只保留低8位(设置进程码尽量保持在0-255之间)

获取上一次系统调用错误的原因:
void perror(const char *str)
const char *strerror(int errno);


三、进程等待

进程等待:等待子进程退出,获取子进程的退出码,释放子进程资源,避免子进程成为僵尸进程。
避免这个子进程成为僵尸进程。

操作:

1. int wait(int *status);

功能:阻塞等待任意一个子进程的退出。意味着如果当前有 子进程且都没有退出就会阻塞进程一直等待
参数:int *status -一个int整形空间的地址,存放退出码
返回值:成功则返回处理的退出子进程的pid,失败则返回-1
(比如没有子进程偏要等待)

2.阻塞等待

为了完成一个功能,我们发起了一个调用,如果功能不能立即完成则一直等待。

int waitpid(pid_t pid,int *status, int option);

功能:也是等待子进程退出,但是可以等待指定的子进程,以及可以进行非阻塞等待
参数:

pid_t pid   -用于指定等待的子进程pid,如果为-1则表示等待任意一个子进程
int *status -整形空间地址用于获取退出码
option      -设置阻塞标志0 -表示阻塞等待;
              WNOHANG -表示非阻塞等待

返回值:
大于0表示处理的子进程pid;
等于0表示当前没有子进程退出(非阻塞);
小于0表示出错了;

3.非阻塞等待

为了完成一个功能,我们发起了一个调用,如果功能不能立即完成则函数立即报错返回。

在进行进程等待之前,已经退出的子进程怎么办?

wait/waitpid并不是只处理刚好退出的子进程,
而是只要有子进程退出,有已经成为僵尸进程的就会处理,
意思就是如果有已经退出的,就直接处理后返回了。

退出码的获取:
在这里插入图片描述

core dump:核心转储,
          表示程序异常退出前将自己的运行信息保存起来,以便事后调试。

进程的退出场景:

(1)正常退出(return/exit\_exit退出)
(2)异常退出-没有运行到正常退出位置中途崩溃了

进程的退出码只有在程序正常退出的时候才有意义,异常则无意义。
因此在获取退出码之前先判断,程序是否是正常退出的!!!

程序崩溃的本质:

程序的运行过程中发生异常,都是内核检测到的,
当一个程序运行异常的时候,则系统检测到之后会给进程发送一个异常信号
(表示进程发生了某个异常事件,不能在运行了)

因此进程的崩溃异常退出,
实际上是收到了一个异常信号知道自己出错了而退出了。

只有进程的异常信号值为0的时候,表示程序正常退出;否则为异常退出。
status &0x7f 获取异常退出信号值
(status >> 8)&0xff 获取退出码

四、程序替换

替换一个进程正在调度管理的程序

通常很少替换当前进程调度的程序,而是创建子进程之后,替换子进程所运行的程序

之前fork之后子进程与父进程干的事相同,可以分摊压力(需求不多),另一种是让子进程干其他的活,而干活逻辑都是在if(fork() == 0)这个判断中完成,但是会导致代码庞大,不灵活。

因此,若创建子进程之后,根据不同的工作,将子进程替换成为不同的程序,则代码模块可以灵活很多。

例如网络服务器:主程序逻辑一致-有请求来了,创建子进程,根据请求替换不同的处理程序即可

1.替换操作接口:

extern char **environ;
这是一个全局变量的声明,
这个全局变量保存了所有的环境变量

int execve(char *pathname, char *argv[], char *env[])
   pathname:要替换的新的程序的路径名
   argv:传递给新的程序的运行参数
   env:传递给新的程序的环境变量,设置为environ则表示传递 
       默认已有环境变量,如果自定义就是给什么就只有什么
返回值:失败返回-1;成功返回0;

注意:
程序替换函数如果替换成功,则这个函数调用之后的代码都不会被运行,因为程序已经被替换为新的程序,而新的程序运行完毕之后,进程就退出了(不会返回到原先的程序)。

这个系统调用接口在某些特定场景不太好用:

  1. 如果替换的都是指令程序,还得找到这个指令程序的路径才可以,
    这些默认都在/bin /sbin下,如果只给程序名,不用给路径则简单。
  2. 环境变量每次都要传递,不传就没有,如果不需要设置环境变量,默认使用已有的。

2.int execl(char *path,char *argv,…);

path:程序路径名;
argv,...不定参:参数单个赋予,不需要组织成为指针数组,
              但是以NULL结尾;环境变量使用默认已有的。

3.int execlp(char *filename,char *argv,…)

filename:程序不用给路径,只给名字就行,
         会自动默认到PATH环境变量指定的路径下去找。

4.int execle(char *path,char *argv,…, char *env[]);

相较于上面,由自己设定环境变量,不适用默认。

有没有p:
没有p则需要给程序的路径名,
有p则只需要给名称不需要路径,
但是程序必须在环境变量PATH指定的路径下,常用于命令。

有没有e:
没有e则使用默认的环境变量,
有e则自己设定环境变量。

5.int execv(char *pathname, char *argv[]);

程序给与路径名,不需要设定环境变量,使用默认的

int execvp(char *filename, char *argv[]);
int execvpe(char *filename,char *argv[],char *env[]);
接口很多,在合适的场景使用合适的接口就行。

(1)如果替换的都是指令程序,则带p,不用给路径。
(2)如果不需要设定环境变量,就不需要带e,使用默认的。
(3)参数根据你的数据组织方式决定使用l还是v,
l参数是单个赋予,
v是参数组织成为数组赋予。

五、编写一个minishell程序,就是一个简单的shell

shell:命令行解释器,
知道用户要干什么,然后执行对应的功能程序。完成用户的要求

1.捕捉键盘输入[ ls -l -a ]

gets(char *buf) 获取一行数据

2.字符串解析得到指令名称和运行参数[ls] [-a] [-l]

执行指令程序(程序替换),但是如果替换了当前shell为新的指令程序,则命令运行完毕,shell就没了。

3.创建子进程 fork()

在子进程中进行程序替换,让子进程运行命令程序 execvp()。

4.等待子进程退出,避免子进程成为僵尸进程 wait()

5.循环上去,重新进行捕捉输入

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值