【Linux】进程控制(详解)

一.进程创建

1.fork函数

在linux中fork函数是⾮常重要的函数,它从已存在进程中创建⼀个新进程。新进程为⼦进程,⽽原进 程为⽗进程。

  • #include <unistd.h>
  • pid_t fork(void);
  • 返回值:⾃进程中返回0,⽗进程返回⼦进程id,出错返回-1

进程调⽤fork,当控制转移到内核中的fork代码后,内核做:

  1.  分配新的内存块和内核数据结构给⼦进程
  2.  将⽗进程部分数据结构内容拷⻉⾄⼦进程
  3.  添加⼦进程到系统进程列表当中
  4. fork返回,开始调度器调度
  5. fork之后,谁先执⾏完 全由调度器决定

2.写时拷贝

当父进程创建子进程时fork,父进程首先要做的是,直接将父进程代码和数据对应页表项的权限,全部改成只读权限,然后子进程继承下来的页表也全部是只读权限,当子进程尝试通过代码对某些数据进行修改的时候,那么页表就立马识别到当前正在对只读区域进行写入操作,就会触发系统错误,触发系统错误的时候,系统就要进行判断,是真的错了,还是要进行写时拷贝,因为毕竟也会有野指针异常访问空间,系统错误后,就会触发缺页中断(后面学习会讲解,这里只需要了解就行),让系统去做检测,如果发现写入的区域是代码区,直接进行杀进程,但一旦发现写入的区域是数据区,就判定成发生写时拷贝,然后系统向物理内存申请空间,进行拷贝,修改页表映射,恢复权限。

要是写入时,这个内存空间不在物理内存里呢?

也是触发错误,发生缺页中断,系统检测,发生页表置换,从磁盘调入物理内存 

 写时拷贝就是时间换空间的做法!!!
为什么还要做一次拷贝呢?不能直接开辟空间吗?

因为你的写入操作!=对目标区域进行覆盖,也有可能会用到原有数据,比如:count++。

二.进程终止

main函数的返回值,是返回给父进程或者是系统,最终表示程序对还是不对!!

回顾一下查看上一次进程退出码的指令:echo $?查看上次进程的退出信息,命令行中,最近一次程序的退出码。退出码表示错误原因。

一般0表示成功,非0 表示错误,用不同的数字,约定或表明出错的原因,系统提供了一批错误码,也可以自己约定错误码。

errno表示获取错误码,而strerror则是将该错误码转换成字符串

数字是给系统看的,字符串是给用户看到

看下面代码,如果不存在该文件,打开一定是失败的,则会返回错误码errno,然后通过strerror将数字转换成字符串。

#include <iostream>
#include <string>
#include <cstdio>
#include <string.h>
#include <errno.h>

int main()
{
    printf("before: errno: %d,errstring: %s\n", errno, strerror(errno));

    FILE *fp = fopen("./log.txt", "r");
    if (fp == nullptr)
    {
        printf("after: errno: %d,errstring: %s\n", errno, strerror(errno));
        return errno;
    }
    return 0;
}

所有再用echo $?查看也是与errno一样。

在Linux下系统错误码有0-133,Windows下有0-140.

在两种系统分别执行下面代码查看:

#include <iostream>
#include <string>
#include <cstdio>
#include <string.h>
#include <errno.h>

int main()
{
    for(int i = 0;i<200;i++)
    {
        std::cout<<"code: "<<i<<", errstring: "<<strerror(i)<<std::endl;
    }
    return 0;
}

1.进程退出的时候,main函数结束代表进程退出,mian函数返回值代表进程所对应的退出码。

2.进程退出码可以由系统默认的错误码来提供,也可以约定自己的退出码。

1.进程终止的方式

1.main函数return

2.进程调用exit,exit在代码任何地方,表示进程结束,非mian函数的return,只表示函数结束,exit表进程结束

3._exit,系统接口,也是终止进程

补充:系统级头文件都是.h.语言级头文件,比如<stdio.h>,在c++中可以写成<cstdio>,但系统级头文件不行。

观察下面代码结果:

int main()
{
    printf("进程运行结束!");
    sleep(2);
    exit(23);
    sleep(2);
    return 0;
}

然后对比_exit,看看区别:

int main()
{
    printf("进程运行结束!");
    sleep(2);
    _exit(23);
    sleep(2);
    return 0;
}

结论:语言级exit会把打印从缓冲区刷新出来,再结束进程,而系统级_exit则不会把打印从缓冲区刷新出来,直接结束进程。

1.刷新缓冲区的区别

2.上下层的区别

我们知道语言级函数,往往是封装系统调用接口,更接近上层,

我们试想一下我们之前认为的缓冲区在哪个位置?

这个缓冲区一定不在OS内部,因为如果在OS内部那么printf打印的信息也一定在OS中,所不管调用哪个函数,都会刷新缓冲区,所有这个缓冲区一定不在OS中。

结论:这个缓冲区是语言级缓冲区,由C/C++提供的!!!

调用exit,fflush从语言层把缓冲区内容刷新到OS中,在刷新到屏幕上。

调用_exit,则直接会杀死进程,数据还在缓冲区内,没机会刷新。

三.进程等待

对于我们创建出来的子进程,作为父进程,必须等待这个子进程,因为是父进程创建的,就必须对子进程负责,对子进程进行回收,如果不回收,根据进程状态那一节内容,子进程就是变成僵尸进程,如果忘了可以进行回顾:进程的状态

我们看下面代码,如果不进行回收,子进程就会变成僵尸:

#include <iostream>
#include <string>
#include <cstdio>
#include <string.h>
#include <errno.h>
#include <cstdio>
#include<unistd.h>

int main()
{
    pid_t id = fork();
    if(id<0)
    {
        printf("errno: %d,errstring: %s\n",errno,strerror(errno));
        return errno;
    }
    else if(id == 0)
    {
        int cnt = 10;
        while(cnt)
        {
            printf("子进程运行中,pid: %d\n",getpid());
            sleep(1);
            cnt--;
        }
        exit(0);
    }
    else
    {
        while (true)
        {
            printf("我是父进程,pid: %d\n",getpid());
            sleep(1);
        }
        
    }
    return 0;
}

1.wait 

学习两个回收子进程函数:

1.先看wait,一般而言,父进程创建子进程就要等待子进程,直到结束

wait

1.回收子进程的僵尸状态

2.等待的时候子进程,如果不退,父进程就要一直阻塞在wait内部,类似scanf。

3.返回值,大于0,表示成功回收子进程,小于0,表示回收失败

4.等待期间,父进程阻塞式等待,等待成功一般返回子进程pid

5.作用:等待任意一个子进程退出

#include <iostream>
#include <string>
#include <cstdio>
#include <string.h>
#include <errno.h>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t id = fork();
    if(id<0)
    {
        printf("errno: %d,errstring: %s\n",errno,strerror(errno));
        return errno;
    }
    else if(id == 0)
    {
        int cnt = 5;
        while(cnt)
        {
            printf("子进程运行中,pid: %d\n",getpid());
            sleep(1);
            cnt--;
        }
        exit(0);
    }
    else
    {
        sleep(10);
        pid_t rid = wait(nullptr);
        if(rid > 0)
        {
            printf("wait sub processon,rid: %d\n",rid);
        }
        while (true)
        {
            printf("我是父
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值