5. 进程控制

1.进程创建

1. fork函数初始

fork函数从已存在进程中创建⼀个新进程。新进程为⼦进程,⽽原进程为⽗进程。
#include <unistd.h>
pid_t fork(void);
返回值:⾃进程中返回0,⽗进程返回⼦进程id,出错返回-1
进程调⽤fork,当控制转移到内核中的fork代码后,内核做:
• 分配新的内存块和内核数据结构给⼦进程
• 将⽗进程部分数据结构内容拷⻉⾄⼦进程
• 添加⼦进程到系统进程列表当中
• fork返回,开始调度器调度

 所以,在C/C++上申请空间malloc or new 的时候,需要在物理内存里开辟空间吗??

开辟虚拟空间即可!!(惰性空间开辟)真正使用的时候,OS才会给你做内存级申请->构建完整的映射关系。(对于用户来说,完全透明,她不知道,也无需知道)

2. fork的常规用法

• ⼀个⽗进程希望复制⾃⼰,使⽗⼦进程同时执⾏不同的代码段。例如,⽗进程等待客⼾端请求,
⽣成⼦进程来处理请求。

• ⼀个进程要执⾏⼀个不同的程序。例如⼦进程从fork返回后,调⽤exec函数。

3. fork调用失败的原因

• 系统中有太多的进程
• 实际⽤⼾的进程数超过了限制

2.进程终止

进程终⽌的本质是释放系统资源,就是释放进程申请的相关内核数据结构和对应的数据和代码。

1.背景:

问题:

1.进程终止,操作系统要做什么?
2.main() return 0;return 0?是什么意思?给谁了?
答:return 的其实是进程退出时的退出码, 会被OS获得->父进程拿到退出码,用系统来辨别该进程的执行情况。0:表示程序运行成功。

 2.进程退出场景

_exit函数:

#include <unistd.h>
void _exit(int status);
参数:status 定义了进程的终⽌状态,⽗进程通过wait来获取该值

说明:虽然status是int,但是仅有低8位可以被⽗进程所⽤。所以_exit(-1)时,在终端执⾏$?发现
返回值是255。
exit函数:

#include <unistd.h>
void exit(int status);
exit最后也会调⽤_exit, 但在调⽤_exit之前,还做了其他⼯作:
1. 执⾏⽤⼾通过 atexit或on_exit定义的清理函数。
2. 关闭所有打开的流,所有的缓存数据均被写⼊
3. 调⽤_exit

缓冲区一定不在操作系统的内部缓冲区(因为_exit不能刷新缓冲区),也就是说exit的底层封装一定包含了_exit(结合上图)

3.进程等待

进程等待是什么?

让父进程通过等待的方式,回收子进程PCB,Z,如果需要,获取子进程的退出信息。

1.进程等待必要性

1. 之前讲过,⼦进程退出,⽗进程如果不管不顾,就可能造成‘僵⼫进程’的问题,进⽽造成内存
泄漏。
2. 另外,进程⼀旦变成僵⼫状态,那就⼑枪不⼊,“杀⼈不眨眼”的kill -9 也⽆能为⼒,因为谁也
没有办法杀死⼀个已经死去的进程。
3. 最后,⽗进程派给⼦进程的任务完成的如何,我们需要知道。如,⼦进程运⾏完成,结果对还是
不对,或者是否正常退出。(如何判断子进程把任务完成的怎么样?->退出码!)
4. ⽗进程通过进程等待的⽅式,@1回收⼦进程资源,@2获取⼦进程退出信息

@1必要,@2可选的

2. 进程等待的方法

2.1 wait方法

#include<sys/types.h>
#include<sys/wait.h>

pid_t wait(int* status);

返回值:
        成功返回被等待进程pid,失败返回-1。
参数:
        输出型参数,获取⼦进程退出状态,不关⼼则可以设置成为NULL

2.2 waitpid方法

pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
        当正常返回的时候waitpid返回收集到的⼦进程的进程ID;
        如果设置了选项WNOHANG,⽽调⽤中waitpid发现没有已退出的⼦进程可收集,则返回0;
        如果调⽤中出错,则返回-1,这时errno会被设置成相应的值以指⽰错误所在;
参数:
    pid:
        Pid=-1,等待任⼀个⼦进程。与wait等效。
        Pid>0.等待其进程ID与pid相等的⼦进程。

    status: 输出型参数
            WIFEXITED(status): 若为正常终⽌⼦进程返回的状态,则为真。(查看进程是
否是正常退出)
            WEXITSTATUS(status): 若WIFEXITED⾮零,提取⼦进程退出码。(查看进程的
退出码)
    options:默认为0,表⽰阻塞等待

    WNOHANG: 若pid指定的⼦进程没有结束,则waitpid()函数返回0,不予以等
待。若正常结束,则返回该⼦进程的ID。
• 如果⼦进程已经退出,调⽤wait/waitpid时,wait/waitpid会⽴即返回,并且释放资源,获得⼦
进程退出信息。
• 如果在任意时刻调⽤wait/waitpid,⼦进程存在且正常运⾏,则进程可能阻塞。
• 如果不存在该⼦进程,则⽴即出错返回。

 2.3 获取子进程status

• wait和waitpid,都有⼀个status参数,该参数是⼀个输出型参数,由操作系统填充。
• 如果传递NULL,表⽰不关⼼⼦进程的退出状态信息。
• 否则,操作系统会根据该参数,将⼦进程的退出信息反馈给⽗进程。
• status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16
⽐特位):

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main( void )
{
    pid_t pid;
    if ( (pid=fork()) == -1 )
        perror("fork"),exit(1);
    if ( pid == 0 ){
        sleep(20);
        exit(10);
    } else {
        int st;
        int ret = wait(&st);
        if ( ret > 0 && ( st & 0X7F ) == 0 ){ // 正常退出
            printf("child exit code:%d\n", (st>>8)&0XFF);
        } else if( ret > 0 ) { // 异常退出
            printf("sig code : %d\n", st&0X7F );
        }
    }
}

1. 为什么会出现异常?因为出现了问题,导致OS给你的进程发送信号了(无0号)

2. 进程如果执行完,如果结果不对,怎么知道是什么原因导致不对?exit code,有退出编号和描述

我们用两个数字,来表示子进程的执行情况:

1.进程退出信号

2.进程退出码

 为什么要有非阻塞轮询方案?->不会卡住父进程,父进程就可以在等待的事件间隙做其他事情了。所以非阻塞比阻塞式调用效率高一点点,父进程可以做更多其他的事!

while :; do ps ajx |grep myproc; sleep 1; done
: 是 shell 中的一个内建命令,它始终返回真值(退出状态码为 0)。
while : 表示循环的条件永远为真,意味着这个 while 循环会一直持续执行,直到被手动中断
do 用于标记循环体的开始。

 

4.进程程序替换

fork() 之后,父子各自执行父进程代码的⼀部分如果子进程就想执行⼀个全新的程序呢?进程的程序
替换来完成这个功能!
程序替换是通过特定的接口,加载磁盘上的⼀个全新的程序(代码和数据),加载到调用进程的地址空间中!
4.1 替换原理
⽤fork创建⼦进程后执⾏的是和⽗进程相同的程序(但有可能执⾏不同的代码分⽀),⼦进程往往要调⽤⼀种exec函数以执⾏另⼀个程序。当进程调⽤⼀种exec函数时,该进程的⽤⼾空间代码和数据完全被新程序替换,从新程序的启动例程开始执⾏。调⽤exec并不创建新进程,所以调⽤exec前后该进程的id并未改变。

 4.2 替换函数

• 这些函数如果调⽤成功则加载新的程序从启动代码开始执⾏,不再返回。
• 如果调⽤出错则返回-1
• 所以exec函数只有出错的返回值⽽没有成功的返回值。
• l(list) : 表⽰参数采⽤列表
• v(vector) : 参数⽤数组
• p(path) : 有p⾃动搜索环境变量PATH
• e(env) : 表⽰⾃⼰维护环境变量

 

 stderr:stderr 是标准错误输出的简称,它是文件描述符的一种

 

 子进程能够执行系统的命令,可以执行,我自己的命令吗??可以的,只要保证找到。

putenv是一个用于改变或增加环境变量内容的函数

*5.自主Shell命令行解释器 

命令行解释器是一个软件 --- 是一个死循环软件;用户输入的“ls -a -l -n”,就是一个长字符串

1.strtok

1.str:指向要被分割的字符串的指针。

2. delim:指向包含分隔符的字符串的指针。这些分隔符用于指定字符串中哪些字符作为分割的界限。

2. chdir更改当前的工作目录

3. getcwd用于获取当前工作目录的绝对路径

 让子进程执行cd和父进程有啥关系?所以cd ..  并不会变化

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值