后台开发核心技术(12):进程

有关线程与进程的知识与操作系统是密不可分的,特别是其实现,类似线程的调度策略,其实是操作系统去实现这部分内容,为了模块化的复习,就单独抛开(有些部分还是需要涉及)操作系统去介绍关于线程、进程的内容,后面再专门去总结一下操作系统的知识。

程序与进程

linux的进程结构一般由三部分构成:代码段数据段堆栈段。堆栈段还包括了进程控制块(PCB),PCB位于进程核心堆栈的底部,不需要额外分配空间。

程序转化为进程的步骤:
1、内核将程序读入内存,为程序分配内存空间;
2、内核为该进程分配PID和其他所需资源;
3、内核为进程保存PID及相应的状态信息,把进程放到运行队列中等待执行,程序转化为进程后就可以被操作系统的调度程序执行了;

进程的创建与结束

进程的创建方式有两种:由操作系统创建、由父进程创建;
系统进程:操作系统会创建一些进程,它们承担着管理和分配系统资源的任务,这些进程通常被称为系统进程。
系统允许一个进程创建新的进程(子进程),子进程也可以创建新的子进程,形成一个树状结构。

Linux的进程构建:

1、树根进程是系统自动构造的,即在内核态下执行0号进程,它是所有进程的祖先。
2、由0号进程创建1号进程(内核态),1号负责执行内核的部分初始化工作及进行系统配置,并创建若干个用于高速缓存和虚拟存储管理的内核线程。
3、1号进程随后去调用execve()去运行可执行程序init(这是个函数),并由此演化成用户态的1号进程,即所谓的init进程(这是个进程)。
4、init进程按照配置文件/etc/initab的要求,完成系统的启动工作,创建编号为1号、2号…的若干终端注册进程getty
5、getty进程设置其进程组标识号,检测配置到系统终端的接口线路。检测到来自终端的连接信号时,getty进程通过函数执行execve()执行注册程序login
6、登录注册成功后,由login程序通过执行execv()执行shell,该shell进程接收getty进程的pid,取代原来的getty,然后shell去接管计算机(bash)。
整个过程描述总结一下如图:
在这里插入图片描述

进程创建fork()、父子进程

linux允许任何一个用户进程创建一个子进程,创建成功后,子进程将存在于系统之中,并且独立于父进程,该子进程可以接受系统调度,可以得到分配的系统资源,系统也可以检测到子进程的存在,并且赋予它与父进程同样的权力。
fork()函数不需要参数,返回值是一个进程标识符(PID)。对于返回值有以下情况:
1、对于父进程,fork()函数返回的是创建的子进程的ID。
2、对于子进程,fork()函数返回0。
3、如果创建出错,则fork()函数返回-1.

// 创建子进程
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
   pid_t pid;
   pid=fork();
   if(pid<0)
   {
     perror("fail to fork");
     exit(-1);
   }
   else if(pid==0){
    // 子进程
    printf("Sub-process,PID:%u,PPID:%u\n",getpid(),getppid());
   }
   else {
   // 父进程
   printf("Parent,PID:%u,Sub-process:%u\n",getpid(),pid);
   sleep(2);
   }
   return 0;
}      

在这里插入图片描述
这里为了方便看到,让父进程sleep2秒,两个进程的地位是平等的,先运行哪一个取决于系统的调度算法。

父子进程的继承资源
子进程完全复制了父进程地址空间中的内容,包括堆栈段数据段的内容。但是子进程并没有复制代码段,和父进程公用代码段。因为代码段是只读的,不影响。
现在Linux内核在实现fork时,并不立即复制父进程的数据段和堆栈段,而是当子进程修改这些数据内容时复制操作才会发生,内核才会给子进程分配进程空间,将父进程的内容复制过来,然后继续后面的操作。

进程的结束——exit()函数

当一个进程需要退出的时候,需要调用退出函数。linux环境下,使用exit()函数进行退出进程:

void exit(int status);

退出有两种方式:正常退出、异常退出。
一、正常退出:
1、在main()函数中执行return
2、调用exit()函数。
3、调用_eixt()函数。
二、异常退出:
1、调用abort函数。
2、进程收到某个信号,使得程序结束。
不管是哪种退出方式,系统最终都会执行内核中的同一段代码。这段代码用来关闭进程所用已打开的文件描述符,释放它所占用的内存和其他资源。
三、退出方式的不同点:
1、exit 和 return 的区别:exit是一个函数,带有参数,exit执行结束将控制权返回给系统;return是函数执行完后的返回,return执行完后把控制权交给调用函数。
2、exit和abort的区别:exit是正常终止程序,abort是异常终止。
3、exit() 和 exit_():exit是在exit_上的一个封装,其会自动调用_exit,并在调用之前刷新数据流。exit函数和_exit函数最大的区别就是在于exit函数在调用exit系统之前要检查文件的打开情况,把文件缓冲区的内容写回文件。

孤儿、僵尸、守护进程

在创建子进程后,父进程和子进程运行是一个异步过程,相互独立平等的两个进程,父进程永远无法预知子进程到底什么时候结束,于是就产生了孤儿进程僵尸进程

孤儿进程: 指一个父进程退出后,它的一个或者多个子进程还在运行,那么那些子进程将称为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对他们完成状态收集操作。
僵尸进程: 指一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait和waitpid获取子进程的状态信息,那么子进程的进程描述符仍然在系统中保存着,这种进程称为僵尸进程。

守护进程:
linux操作系统在系统引导时会开启很多服务,这些服务就叫做守护进程。
守护进程是脱离于终端并且在后台运行的进程。守护进程脱离于终端是为了避免进程在执行过程中的信息在任何终端上显示并且进程也不会被任何终端所产生的终端信息所打断。
长期存在:
守护进程是一个长期存在的进程,通常独立于控制终端并且周期性执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。linux系统有很多守护进程,大多数的服务都是通过守护进程实现的,同时守护进程也能完成很多任务,比如作业规划进程crond、打印进程lqd等。
脱离终端:
由于在linux中,每一个系统与用户进行交流的界面称为终端,每一个终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会被关闭。但是守护进程突破了这种限制,它从被执行时开始到整个系统关闭时才退出。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值