怎么做足球直播网站,软文300字介绍商品,北京电脑软件培训学校,泰国做网站网站要判几年进程 1. 进程的概念1.1 进程的理解1.2 Linux下的进程1.3 查看进程属性1.4 getpid和getppid 2. 创建进程3. 进程状态4. 进程优先级5. 进程切换6. 环境变量7. 本地变量与内建命令 1. 进程的概念
一个已经加载到内存中的程序#xff0c;叫做进程#xff08;也叫任务#xff09… 进程 1. 进程的概念1.1 进程的理解1.2 Linux下的进程1.3 查看进程属性1.4 getpid和getppid 2. 创建进程3. 进程状态4. 进程优先级5. 进程切换6. 环境变量7. 本地变量与内建命令 1. 进程的概念
一个已经加载到内存中的程序叫做进程也叫任务。有些教材也说正在运行的程序叫做进程。
1.1 进程的理解 一个操作系统不仅仅运行一个进程可以同时运行多个进程。例如操作系统本身也是个程序它要运行起来必须先加载到内存然后再运行某个文件这样就同时运行两个进程。 进程这么多有的进程刚加载到内存有的进程已经结束有的进程还在运行。操作系统必须将进程管理起来如何管理先描述再组织。 任何一个进程在加载到内存形成一个真正的进程时OS要先创建进程的结构体对象这个结构体对象叫做PCBprocess control block/进程控制块。PCB本质就是进程属性的集合。所以一个进程要运行时首先会根据进程的PCB创建对应的结构体对象初始化再把进程的代码和数据加载到进程。所以进程 内核数据结构对象描述这个进程的所有属性值由OS创建 自己的代码和数据加载到内存的可执行程序由程序员自己维护。 前面我们提到OS对底层设备做管理其实是对底层设备的结构体对象做管理所以以后操作系统对进程做管理只需对PCB结构体对象进行管理因为PCB是进程属性的集合不需要对代码和数据进行管理。操作系统对进程做管理本质是对进程的内核数据结构PCB对象做管理。 PCB有指向代码和数据的指针操作系统要调度进程时会找到PCB根据PCB指针信息找到代码和数据再交给CPU去执行。 当内存中有多个进程怎么把所有进程管理刚刚完成先描述现在将PCB关联起来完成再组织。所以在操作系统中对多个进程进行管理变成对PCB单链表进行增删查改。 1.2 Linux下的进程
task_struct Linux操作系统下的PCB是: task_struct。Linux先将进程描述成tack_struct对象再组织。Linux是怎么组织进程的在Linux内核中最基本的组织进程task_struct方式是采用双向链表组织的。但是在操作系统内部一个task_struct还有其他的链接指针可以还会二叉树的某个节点或者队列的某个节点并不只有一个链接方式的指针。task_struct 1标示符PID: 描述本进程的唯一标示符用来区别其他进程。 2状态: 任务状态退出代码退出信号等。 3优先级: 相对于其他进程的优先级。CPU数目要少于进程这意味着进程要竞争CPU竞争就意味有优先级。 4程序计数器: 程序中即将被执行的下一条指令的地址。 5内存指针: 包括程序代码和进程相关数据的指针还有和其他进程共享的内存块的指针。 6上下文数据: 进程执行时处理器的寄存器中的数据。
1.3 查看进程属性 用ps或者top这两个指令来查看进程。以ps为例ps一般带选项ajx。 通常配合管道和grep使用比如要查找myprocess的进程 进程的信息可以通过 /proc 系统文件夹查看。
操作系统将进程信息可视化将进程放在/proc这个文件夹中。这些蓝色数字就是目录数字表示进程的PID目录里面存放进程的大量属性。
ls /proc/PID 查看进程的属性 讲讲两个属性cwd当前工作目录exe可执行程序
每个进程终止后再启动其PID大概率是变化的。
1.4 getpid和getppid
如何获得进程的PID
操作系统提供了一个系统调用接口getpid谁调用getpid就返回谁的PID。进程调用getpid就相当于在这个进程的task_struct中找到它的PID然后返回给用户。getppid则返回当前进程的父进程的PID。 证明
test.c1 #includestdio.h2 #includeunistd.h3 4 int main()5 {6 while(1)7 {8 printf(这个进程的PID是%d其父进程的PID是%d\n,getpid(),getppid());9 sleep(1); 10 }11 return 0;12 }
运行这个代码后对比打印出来的PID和PPID与用指令查看进程属性中的PID和PPID。
PID只保证每次运行期间有效下次启动进程PID可能发生变化。 在命令行创建的进程是由bash创建的并且变成bash的子进程每条指令都是bash的子进程。这些子进程的创建和销毁不影响父进程。 2. 创建进程
创建进程需要用到fork函数。 fork()创建子进程如果创建成功给父进程返回子进程的PID给子进程返回0。如果创建失败给父进程返回-1。
例子 12 pid_t id fork(); 13 if(id 0) 14 { 15 printf(我是子进程我的PID是%d\n,getpid()); 16 } 17 else18 {19 printf(我是父进程我的PID是%d\n,getpid());20 } 疑问
1为什么fork要给子进程返回0给父进程返回子进程的PID 一般而言fork之后的代码共享返回不同的返回值是为了区分让不同的执行流执行不同的代码块让父子进程执行不同的事情。给父进程返回子进程的PID用来标识子进程的唯一性一个父进程可以有多个子进程返回子进程的PID可以告诉父进程我是哪个子进程。 2fork函数究竟在做什么 创建子进程因为进程 内核数据结构 代码和数据所以实际上是在内存中创建对应的task_struct对象。子进程拷贝了父进程的内核数据结构修改了部分属性比如PIDPPID但不会拷贝父进程的代码和数据而是共享父进程的代码和数据。
3一个函数是如何做到返回两次的 从fork函数的实现就可以看出来
pid_t fork()
{//1.创建子进程的PCB//2.填充PCB对应的内容//3.让父子进程指向同样的代码//4.父子进程都是具有独立的task_struct可以被CPU调度执行...return ret;
}return这句代码是在子进程被创建后与父进程共享的代码之一被执行了两次所以fork()会有两次返回值。
4一个变量怎么会有不同的内容
独立性
任何平台进程在运行的时候具有独立性。因为数据可能被修改不能让父进程和子进程共享一份数据。
所以让子进程拷贝父进程的数据吗如果子进程对拷贝的数据不修改会导致操作系统中可用资源的减少存在重复的数据。所以操作系统识别到如果进程要修改数据中的某部分再将父进程数据中的这部分数据拷贝过来再进行修改。
父进程创建子进程后谁先运行
谁先运行由调度器决定是不确定的。调度器挑选一个进程到CPU工作CPU只负责运行进程。
所以现在我们创建进程有两种方法一是./运行程序指令级别二是fork()代码层面创建。bash创建进程其实就是调用fork函数。 3. 进程状态
Linux操作系统中进程有运行状态(R、睡眠状态S、磁盘休眠状态D、停止状态T、死亡状态X等。
运行状态R
1运行状态表明进程要么是在运行中要么在运行队列里。
每个CPU都有一个运行队列运行队列中排列着进程的task_struct。凡是处于运行队列的进程它们所处的状态叫做运行态R态。
2一个进程只要把自己放到CPU开始运行是不是就一直执行到完毕再将退出运行 肯定不是每个进程都有一个叫时间片的时间限制一旦进程在CPU中运行时间超过时间片就会从CPU中拉下来如果这个进程还要运行需要继续在运行队列排队。所以在一段时间内所有的进程代码和数据都会被执行这就是并发执行。如果不是并发执行我们就不能边听音乐边写文档不能同时运行多个进程。
把进程从CPU中拿下来放上去的动作叫做进程切换。
3问题为什么运行时是S状态为什么将打印去掉后运行时是R状态
//测试代码1 #includestdio.h2 #includeunistd.h3 int main()4 {5 6 while(1)7 {8 printf(I am a process,my pid:%d,getpid());9 sleep(1);10 }11 return 0;12 }代码里面有printfprintf要访问显示器设备CPU处理速度过快导致进程在显示器等待的时间远远大于进程在CPU上运行时间。所以导致肉眼看到的进程状态是S状态。 当将代码中的打印部分去掉时
睡眠状态S
顾名思义阻塞状态是指进程在等待时间完成处于等待队列的状态。 理解 操作系统是如何管理底层设备的先描述再组织。先将底层设备描述成一个个PCB结构体对象对象里面包括这个对象的类型这个对象的状态和进程等待队列。 当我们的进程在运行时读取到输入scanf、cin等代码需要从键盘中读取数据但是我们不输入此时进程就没办法在运行队列上运行因为当前键盘没有就绪所以我们需要让该进程链入进程的等待队列等待硬件资源就绪。每个设备都有一个等待队列当设备没有就绪进程就会链入进程的等待队列中。
拓展 当处于睡眠状态的进程过多时它们的代码和数据在内存中处于空闲的状态在等待过程中操作系统内部的资源严重不足。所以为保证其他进程正常运行需要省出内存资源。操作会把处于等待状态的进程的代码和数据交换到外设磁盘只留下进程的PCB在等待队列节省内存资源。 问题 平时我们所说的阻塞状态与睡眠状态有什么区别吗 1睡眠状态是进程本身拥有的状态而阻塞状态则是用来描述进程的状态 2睡眠状态是自动的进程主动进入睡眠状态主动进入等待队列等待不获取CPU资源不参与进程的调度让其他进程获取CPU资源而阻塞状态是被动的必须等待某件事件的完成才能被CPU调度。 3一般可以认为睡眠状态是阻塞状态。
磁盘休眠状态D
磁盘休眠状态一般也叫不可中断休眠状态、深度睡眠。当进程向磁盘写入时由于磁盘写入速度较慢进程只能等待磁盘写入完毕才能向上层反馈。在此期间如果计算机内存严重不足操作系统已经用尽浑身解数去优化内存但还是内存不足此时它就将目标转向等待的进程直接kill进程这也是为什么有时内存不足时有些程序会闪退。杀掉进程后因为磁盘写入数据有可能失败如果失败磁盘要向进程报告但此时进程已被杀掉磁盘找不到进程就可能把数据丢掉如果是这份数据异常重要比如银行的转账记录那岂不是造成很大的损失。 所以就有了进程的磁盘休眠状态。当进程在等待磁盘写入完毕之前这个进程不能被任何人杀掉这就是进程的D状态。当磁盘写入完毕后进程就从D状态改为R状态。 D状态的进程不响应操作系统的任何请求只能自己醒来。
停止状态T
1可以通过发送 SIGSTOP 信号给进程来停止T进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。利用kill -l将信号名称显示其中就有我们需要的信号。 2例子 3T状态与S状态有什么区别 处于T状态的进程可能在等待资源也可能没在等待这个进程被控制单纯不能运行处于S状态的进程一定在等待资源。
终止状态X
进程结束释放进程所有资源这就是进程的终止状态。这个状态只是一个返回状态你不会在任务列表里看到这个状态。
僵尸状态Z
1当一个子进程退出父进程没有获取子进程的退出信息或者没有调用wait/waitpid回收子进程的资源这个子进程变成僵尸进程跳出三界之外不在五行之中此时的僵尸进程不能用kill杀死。 例子
//模拟场景子进程先退出但父进程没有回收子进程的资源
int main()
{//创建子进程pid_t id fork();if(id0){perror(fork);}else if(id 0)//子进程{ int cnt 20;while(cnt--){couti am child process,my pid is getpid() endl;sleep(1);}}else//父进程{//父进程一直循环等待子进程退出while(1);}
}结果 kill -9 命令也无法杀掉僵尸进程 子进程一般退出的时候如果父进程没有主动回收子进程资源子进程就会一直处于Z状态
2那么僵尸进程的危害是什么很明显只要父进程不回收子进程的资源子进程就一直处于僵尸状态一直占用内存资源这就会造成内存泄漏。
3怎么避免僵尸进程父进程得等待子进程先退出且调用wait/waitpid获取子进程的退出信息。
4问题为什么当杀掉父进程后父进程不会变成僵尸进程子进程会被释放
孤儿进程
1讨论僵尸进程时我们注意到其前提子进程先退出父进程后退出。那如果是父进程先退出子进程后退出呢也就算上面遇到的问题。父进程先退出子进程后退出时我们把子进程叫做孤儿进程。 2例子
int main()
{//模拟场景父进程先退出子进程一直循环//创建子进程pid_t id fork();if(id0){perror(fork);}else if(id 0)//子进程{ //让父进程比子进程先退出int cnt 10;while(cnt--){couti am child process,my pid is getpid() endl;sleep(1);}}else//父进程直接退出不回收子进程的资源{int cnt 5;while(cnt--){sleep(1);}}return 0;
}结果 3子进程为什么要被领养 因为孤儿进程未来也要退出也要被释放。所以只能由OS来承担这个重任。 4. 进程优先级
概念
CPU资源分配的先后顺序就是指进程的优先权。优先级和权限没有任何关系优先级表示对于资源的访问的谁先谁后权限表示能否访问资源。
为什么要有进程的优先级
因为资源是有限的进程是大量的这注定了大量的进程要争夺有限的资源。而OS为了保证进程之间公平竞争确立优先级。如果进程长时间得不到CPU资源该进程的代码长时间得不到推进就会导致进程的饥饿问题。
如何查看进程的优先级 a. PRI表示进程优先级它的取值范围是[0,139]。其中[0,99]之间的值是给实时进程用的[100,139]是给普通进程用的我们的进程一般都是普通进程所以只需关注[100,139]之间的取值。值越小进程优先级越高被CPU执行的顺序越靠前。 b. NI表示进程的NICE值它可以修正进程的优先级。公式为PRI(new) PRI(old) nice。当nice为负值时PRI越小优先级越高。所以在Linux中调整优先级就是调整NI值。 c. 这是否意味着可以任意改变NI值提供进程的优先级从而使我的进程一直被调度并不是。Linux不想让用户过多地参数优先级的调整只允许让我们在一定的范围内进行优先级调整。即nice的取值范围为[-20,19]。
如何修改进程的优先级
需要用到top指令输入top后可以看到进程的调度情况。然后输入r 进程PID NICE值。注意设置新的PRI(new)看的是PRI(old)这个PRI(old)是历史上的第一次优先级。
OS是如何根据优先级开展进程的调度的即OS是怎么把优先级高的进程先执行优先级低的后执行
综上 我们可以得出进程的两个性质竞争性和独立性。
竞争性: 系统进程数目众多而CPU资源只有少量甚至1个所以进程之间是具有竞争属性的。为了高效完成任务更合理竞争相关资源便具有了优先级。独立性: 多进程运行需要独享各种资源多进程运行期间互不干扰。 5. 进程切换
并行: 多个进程在多个CPU下分别同时进行运行这称之为并行。并发: 多个进程在一个CPU下采用进程切换的方式在一段时间之内让多个进程都得以推进称之为并发。一般我们的CPU都是单核采用的是并发的方式。我们可以同时听歌、写文章、与他人聊天都得益于进程的并发。但是为什么我们没有感受到并发带来的影响比如卡顿因为CPU切换进程速度快我们感受不到。一个进程放在CPU上是不是得让这个进程一直跑完才可以让它从CPU下来并不是。如果是这样那其他进程怎么办OS规定每个进程都有一个时间片如果进程在这个时间段内跑完就销毁如果跑不完就从CPU上剥离下来放到等待队列继续排队这个过程就叫做进程切换。这种基于进程切换和时间片轮转的调度算法就是CPU的调度算法。为什么从CPU上剥离下的进程会被放到等待队列这个进程是不能放到运行队列因为它本是运行队列中优先级最高的放到运行队列CPU不就又调度它了吗进程怎么切换通过CPU中的寄存器。CPU中有很多寄存器包括通用寄存器eax,ebx,ecx,edx、维护栈帧的寄存器ebp,esp,eip、状态寄存器status等。它们主要是为了提高CPU运行效率将高频使用的数据放入寄存器中。例如系统如何得知进程当前执行到哪行代码通过程序计数器PC指针/eip记录当前进程正在执行指令的下一行指令的地址。 CPU寄存器保存的是进程的临时数据叫做进程的上下文。进程从CPU离开时要将自己的上下文数据保存甚至带走主要目的是为了未来恢复。 所以进程在被切换时做了两个工作保存上下文离开恢复上下文运行。CPU的临时数据保存在哪里放到进程task_struct的结构体里面。 6. 环境变量
概念
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。有点抽象先讲几个环境变量。
例子
1PATH a. 问题 当我们运行自己的程序和运行系统的指令时我们发现为什么运行自己的程序时要带 ./而运行系统的指令时却不用带 ./ b. 什么是PATH PATH是系统自带的指令搜索路径。当运行指令时shell会在PATH中找路径。 c. 问题答案 指令的路径放在PATH中而我们自己的命令放在当前目录下没有放在PATH中。shell会在PATH中查找运行程序的路径找不到我们程序的路径所以只能我们自己带 ./。
d. 如果我偏不想带 ./想让我们的程序像指令一样该怎么做 法一可以将我们的程序拷贝到/usr/bin目录下法二直接将我们的程序的路径拷贝到PATH中。 怎么在PATH中新添路径
2HOME用户登录到Linux系统的默认目录。 3SHELL当前Shell,它的值通常是/bin/bash。 4HISTSIZE保存历史命令的条数。 5SSH_TTY当前终端设备文件。 6PWD记录当前所处的工作目录OLDPWD记录上一次所处的工作目录。 还想获取更多环境变量可以用指令env查看。
用getenv获取环境变量 例子
int main()
{//getenvcoutPATH:getenv(PATH)endl;coutWHO:getenv(USER)endl;return 0;
}为什么我们能在我们的程序中获得环境变量 我们运行的进程都是子进程bash本身在启动的时候会从操作系统的配置文件中读取环境变量信息子进程会继承父进程交给我的所有环境变量。所以环境变量具有全局属性。 证明环境变量被子进程继承 如何证明在bash新增一个环境变量然后子进程继承并打印看看有没有bash中新增的环境变量。
//子进程代码
int main(int argc,char*argv[],char*env[])
{int i 0;for(; env[i]; i){coutenv[i]endl;}
}那如何销毁环境变量
那如何在代码中获得环境变量 1法一通过命令行的第三个参数
int main(int argc, char *argv[], char *env[])//这三个参数等下讲只需知道env是字符指针数组指向环境变量
{int i 0;for(; env[i]; i){coutenv[i]endl;}return 0;
}2法二通过第三方变量environ获取 environ是C库定义的一个全局变量指向环境变量表。
int main(int argc, char *argv[])
{extern char **environ;//environ没有包含在任何头文件中,所以在使用时 要用extern声明int i 0;for(; environ[i]; i){coutenviron[i]endl;}return 0;
}3法三通过系统调用getenv()相较于前两种方法这种方法更适合获取单个环境变量。
命令行参数
1main函数是可以接收参数的这些参数就是命令行参数。
int main(int argc,char*argv[])其中argv是指针数组指向命令行的字符串argc是命令行字符串的个数。argv有argc个元素。命令行参数以空格为分隔符将输入的字符串打散成几个字符串记下有多少个字符串放到argc将每个字符串的地址放到argv中再通过传参给main函数。这表明实际上main函数并不是程序真正的入口
2例子打印命令行参数
int main(int argc,char*argv[])
{int i 0;for(;iargc;i){printf(argv[%d]:%s\n,i,argv[i]);}return 0 ;
}3获取命令行参数的意义是什么 为指令、工具、软件等提供命令行选项的支持。 例子
int main(int argc,char*argv[])
{//如果命令行参数不足两个或者超过两个给出提醒if(argc ! 2) {printf(Usage: %s -[a|b]\n, argv[0]);return 0;}if(strcmp(argv[1], --help)0){printf(Usage: %s -[a|b]\n, argv[0]);}else if(strcmp(argv[1], -a) 0){printf(功能1\n);}else if(strcmp(argv[1], -b) 0){printf(功能2\n);}
}4第三个命令行参数
int main(int argc,char*argv[],char*env[])env是指针数组指针指向环境变量。 7. 本地变量与内建命令 本地变量是在命令行直接定义只在bash内部有效的变量。本地变量不能被子进程继承。 set可以查看系统中所有的本地变量和环境变量。 问题 bash定义一个本地变量MY_ENVecho是一个指令也是bash的子进程它为什么能打印父进程的本地变量不是说本地变量不能被子进程继承吗 命令行上的命令并不是全部要创建子进程。
命令可以分为两类一类是常规命令通过创建子进程替父进程完成一类是内建命令bash不创建子进程而是由自己亲自执行类似于bash调用了自己写的或者系统提供的函数。