[Linux]进程控制详解

1.创建进程

在这里插入图片描述
进程调用fork,当控制转移到内核中的fork代码后,内核做:
● 分配新的内存块和内核数据结构给子进程
● 将父进程部分数据结构内容拷贝至子进程
● 添加子进程到系统进程列表当中
● fork返回,开始调度器调度

这个前面提到过,就不多说了

2.写时拷贝

引言:当程序变成二进制后,所有的变量名都会变成地址。
fork之后代码共享,就是子进程被创建时,会以父进程为模板,子进程和父进程默认拥有一份相同的代码和数据,当然页表中存储的地址也是一样的。
在这里插入图片描述
在这里插入图片描述
两个问题:
创建子进程时,为什么不直接把父进程的数据给一份?
因为操作系统按需分配,节省空间。

为什么要拷贝?开新空间就好了啊
这里的写:包括了增删查改,不是全被都被覆盖。可能只修改一部分的数据

上图中页表部分括号里有个只读权限,接下来我们分析一下页表

(i)页表

页表不仅仅有虚拟地址和物理地址,物理地址还包括了权限
在这里插入图片描述
举例:字符常量无法被修改,就是我们在写入时,由虚拟地址到物理地址的转化中,权限中没有写入权限。
写代码时const的意义:把运行时产生的错误提前到编译时(防御性编程)

3.终止进程

进程退出情况

我们写代码时,最后都会return 0,main函数的返回值,就是进程的退出码
一般0表示成功,非0表示失败。
在进程执行结束之后,如果进程失败了,我们最想知道的是他的错误原因

echo $?
这个可以查看进程的退出码

当然错误码我们是无法理解的,所以就有错误码转化为错误描述
strerror()
在这里插入图片描述
我们可以看看linux中的错误码

int main()
{
    for(int i=0;i<200;i++)
    {
        printf("%d: %s\n",i,strerror(i));
    }
    return 0;
}

在这里插入图片描述

函数退出情况

除了进程有退出,函数也是有退出的。
函数也是有返回值的,函数的返回值就是错误码,这个和进程的一样
在这里插入图片描述

总结

总结:
任何进程的最终执行情况我们用两个数字表示==(异常)信号编号+进程退出码

如何让进程退出?
1.main函数return
2.exit(退出码)——终止进程,status(进程退出码)
这个进程退出码status会在进程等待中详细的说

4.进程等待

我们提到的父进程如果不等待子进程,就会使子进程变成僵尸进程,会引发内存泄漏
在这里插入图片描述
所以父进程通过wait的方式去回收子进程的资源,这个是必须要做的事情。
在这里插入图片描述
wait会默认进行阻塞等待,等待任意一个子进程;
返回值大于0,等待成功,等待子进程的pid;返回值小于0,等待失败

int main()
{

    pid_t id = fork();
    if (id == 0)
    {
        // child
        int cnt = 5;
        while (cnt)
        {
            printf("I am child,pid: %d,ppid: %d\n", getpid(), getppid());
            cnt--;
            sleep(1);
        }
        printf("马上变僵尸\n");
        exit(0);
    }
    sleep(10);
    // father
    pid_t rid = wait(NULL);
    if (rid > 0)
    {
        printf("wait success child_id:%d\n", rid);
    }
    return 0;
}

在这里插入图片描述
wait比较简单,我们来看看waitpid()
在这里插入图片描述
status:局部使用的位图形式在这里插入图片描述
在这里插入图片描述
status的8到15位时退出码,0到7时退出信号。

提取status的信号:status&0x7F
这样就把他的前7位的数字取出来了;
提取status的退出码:(status>>8)&0xFF
这样就把他的次低8位的数字取出来了.
当然这种方式太麻烦了,我们有专门的宏来完成这件事
在这里插入图片描述

if (WIFEXITED(status))
    {
        printf("wait success,rid: %d,status: %d,exit code: %d\n",rid,status,WEXITSTATUS(status));
    }

非阻塞等待才是重点
在这里插入图片描述
这里当options设位0,就是阻塞等待,设为WNOHANG就是非阻塞等待。
非阻塞:当在等待子进程退出的时候,父进程还可以去做别的事情

waitpid的返回值:
● 大于0:系统调用成功
● 等于0:系统调用成功,但子进程还没退出
● 小于0:调用失败

我们用代码来看看非阻塞等待的优势

const static int NUM=3;
typedef void(*func_t)();

func_t task[NUM];


void printfNAME()
{
    printf("this is print name\n");
}
void printfNPC()
{
    printf("this is print npc\n");
}
void printfAGE()
{
    printf("this is print age\n");
}

void InitLog()
{
    task[0]=printfNAME;
    task[1]=printfNPC;
    task[2]=printfAGE;
    task[3]=NULL;
}
void Exe()
{
    for(int i=0;task[i];i++)
    {
        task[i]();
    }
}
int main()
{
    InitLog();
    pid_t id = fork();
    if (id == 0)
    {
        // child
        int cnt = 5;
        while (cnt)
        {
            printf("I am child,pid: %d,ppid: %d\n", getpid(), getppid());
            cnt--;
            sleep(1);
        }
        sleep(12);
        exit(111);
    }
    // father
    int status=0;
    pid_t rid = waitpid(id,&status,WNOHANG);
    while(1)
    {
        if(rid>0)
        {
            printf("wait sucess,rid: %d,status: %d,exit code: %d\n",rid,status,WEXITSTATUS(status));
            break;
        }
        else if(rid==0)
        {
            printf("child is running,do other thing\n");
            //开始任务
            printf("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
            Exe();
        }
        else
        {
            perror("waitpid");
            break;
        }
        sleep(1);
    }

    return 0;
}

当子进程还在运行的时候,父进程不是傻傻的等,等待的时候可以去完成别的工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tpoog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值