拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Linux-进程概念

一、冯诺依曼体系结构我们常见的计算机如笔记本。我们不常见的计算机如服务器大部分都遵循冯诺依曼体系结构。存储器指的就是内存。输入设备有键盘、摄像头、话筒、磁盘、网卡、鼠标等。输出设备有显示器、播放器、磁盘、网卡等。运算器主要是对输入的数据进行计算任务计算任务分为算数运算和逻辑运算两类。控制器是对计算硬件流程进行一定的控制。注有些设备是纯输入设备有些设备是纯输出设备也有既是输入也是输出的设备例如磁盘和网卡。这5个设备都是独立的个体各个硬件单元必须用线链接起来线有两类1.系统总线2.IO总线。冯诺依曼体系中中央处理器(CPU)只能对存储器(内存)进行读写不能访问外设(输入设备和输出设备)。原因因为CPU运行时间是纳秒级别的内存运行时间是微秒级别的硬盘运行时间是毫秒级别的(1s1000毫秒1000000微秒1000000000微秒)根据木桶原理CPU和硬盘的运行效率差距太大如果CPU直接访问外设会导致CPU等待时间过长总体时间效率低下。而将硬盘中的数据提前存储到内存中因为内存还会细分为一级二级三级缓存因此CPU和内存之间时间级别差距不是特别大CPU从内存中读取和处理数据的时间效率会显著提升。因此CPU不会直接从外设读取数据而是从内存中读取数据。冯诺依曼体系结构中数据处理的流程一个程序要运行必须要先加载到内存中运行。代码也是数据CPU要进行处理时代码也必须先加载到内存里。总结所有设备都只能直接和内存打交道。二、操作系统(Operator System)1.操作系统的结构操作系统为什么要做管理部分有对系统调用接口和用户操作接口的说明。系统怎么进行管理部分有对操作系统、驱动程序、底层硬件的说明。2.操作系统是什么操作系统本质上是一款对软硬件进行管理的软件。3.操作系统为什么要做管理操作系统帮助用户管理好下面的软硬件资源。操作系统为了给用户提供一个良好(稳定、高效、安全)的运行环境。系统调用接口和用户操作接口的说明操作系统里面会有各种重要的数据由于用户之中可能坏人可能会随意修改操作系统中重要的数据因此操作系统并不信任任何用户。操作系统为了保证自身的安全也为了保证能够给用户提供服务操作系统以接口的方式给用户提供调用的入口用于获取操作系统内部的数据操作系统的接口是操作系统自身提供的。对操作系统自身内部函数的调用称为系统调用。所有访问操作系统的行为都只能通过系统调用完成就如同银行并不相信客户但为了让客户依就可以在银行进行存取钱借贷等操作就在银行柜台前只开了一个小小的窗口而玻璃窗都是防弹玻璃防止不法行为的发生。在系统调用的使用上功能比较基础对用户的要求也相对较高。所以一些技术高超的开发者对操作系统接口进行了适度封装从而形成库、指令、shell外壳等更有利于上层用户的使用或开发者进行二次开发。4.操作系统怎么进行管理操作系统是通过分析软硬件的数据信息对软硬件进行管理的。对操作系统、驱动程序、底层硬件的说明操作系统具体的管理方式先描述再组织1.用类或结构体描述每个软硬件数据信息(共同属性)。2.操作系统从驱动程序获取到类描述生成的所有对象的数据。3.操作系统用链表结构或其他高效的数据结构将这些数据对象组织起来每个数据对象会有特定的编号。4.最后操作系统对组织后的结构进行管理(增删查改等操作)。三、进程1.概念一个已经加载到内存的程序叫做进程(任务)。一个操作系统不仅仅能只运行一个进程可以同时运行多个进程。2.对进程的描述操作系统管理进程同样也是用的先描述再组织的方式。任何一个进程在加载到内存的时候在形成真正的进程时操作系统要先创建一个描述进程属性的结构体对象(PCB)。PCBprocess ctrl block - - - 进程控制块是进程属性的集合。进程内核PCB数据结构对象代码和数据操作系统对进程的管理本质上就是对PCB组织后形成的链表结构进行管理Linux操作系统中的PCB是task struct。task struct内容分类标识符进程PID描述本进程的唯一标识符用来区别其他进程。状态任务状态退出代码退出信号等。优先级相较于其他进程的优先级。程序计数器程序中即将被执行的下一条指令的地址。内存指针包括程序代码和进程相关数据的指针还有其他进程共享的内存块的指针。上下文数据进程执行时处理器中寄存器的数据。I/O状态信息包括显示的I/O请求分配给进程的I/O设备和被进程使用的文件列表。记账信息可能包括处理器时间总和使用的时钟数总和、时间限制记账号等。其他3.查看进程进程的信息可以通过/proc系统文件夹来查看ls /proc第二种查看进程的方式ps指令ps ajx查看所有进程。ps ajx | head -1查看进程的第一行。[xxlVM-0-2-centos ~]$ ps ajx | head -1 PPID PID PGID SID TTY TPGID STAT UID TIME COMMANDps ajx | head -1 ps ajx | grep test同时执行两条指令第一条是查看进程的第一行另一条是在显示的进程中过滤出含有test关键字的进程。注grep --colorauto test这条进程的出现是因为grep过滤进程中也有test关键字使用grep -v grep可以屏蔽这条进程。ps ajx | head -1 ps ajx | grep test | grep -v grep过滤出含有test的进程并且过滤掉含有grep的进程。杀死进程(强制干掉进程)kill -9 进程PID(进程编号)4.通过系统调用接口查看进程的PID和PPIDPID为进程idPPID为父进程idgetpid()函数能查看进程idgetppid()能查看父进程id这两个函数所需要的头文件都是#includesys/types.h。[xxlVM-0-2-centos test]$ cat text.c #includestdio.h #includesys/types.h int main() { printf(当前进程的id:%d,父进程的id:%d\n,getpid(),getppid()); return 0; } [xxlVM-0-2-centos test]$ ./text 当前进程的id:13885,父进程的id:81855.通过系统调用创建进程--fork创建进程的方法有两种1.[./]运行可执行程序--属于指令级别的创建进程。2.fork指令--属于代码层面创建子进程不需要特殊的头文件。fork创建子进程运行[man fork]认识fork。fork有两个返回值。父子进程代码共享但数据各自私有一份并且是以写时拷贝的方式私有的。fork之后通常用if进行分流。执行text.c[xxlVM-0-2-centos test]$ cat text.c #includestdio.h #includesys/types.h int main() { printf(hello world\n); fork(); printf(hello Linux\n); return 0; } [xxlVM-0-2-centos test]$ ./text hello world hello Linux hello Linux执行test.c[xxlVM-0-2-centos test]$ cat test.c #includestdio.h #includeunistd.h #includesys/types.h int main() { printf(当前进程pid:%d ppid:%d\n,getpid(),getppid()); pid_t idfork(); //使用fork创建子进程 if(id0) { while(1) { printf(我是子进程,pid:%d ppid:%d\n,getpid(),getppid()); sleep(1); } } else if(id0) { while(1) { printf(我是父进程,pid:%d ppid:%d\n,getpid(),getppid()); sleep(1); } } else { } return 0; } [xxlVM-0-2-centos test]$ ./test //执行test 当前进程pid:16053 ppid:8185 我是父进程,pid:16053 ppid:8185 我是子进程,pid:16054 ppid:16053 我是父进程,pid:16053 ppid:8185 我是子进程,pid:16054 ppid:16053 我是父进程,pid:16053 ppid:8185 我是子进程,pid:16054 ppid:16053原本test只是一条进程在调用fork后产生了一条子进程。对fork的介绍fork通过复制调用的进程创建一条新进程。新进程称为子进程子进程就是父进程的一份副本。fork会返回两次会给子进程返回0给父进程返回子进程的PID。fork的4个问题1.为什么fork要给子进程返回0给父进程返回子进程的PID返回不同的返回值是为了区分让不同的执行流执行不同的代码块一般而言fork之后的代码父子进程共享因为一个父进程可能会有多个子进程但通常一个子进程只会有一个父进程给父进程返回子进程的PID是为了让父进程精确的找到并控制对应的子进程。2.fork函数里面到底在干什么fork函数里面在创建子进程。3.fork函数是怎么做到返回两次的在fork函数中创建完子进程后父子进程就都可以被CPU调用了而且return返回函数也属于父子共享的代码因此父子进程都会返回一个pid_t类型的值因此fork函数会返回两次。4.一个变量里面为什么会有不同的内容例如执行test时id既能等于0进入打印我是子进程的代码块也能大于0进入打印我是父进程的代码块。fork在返回时发生了写时拷贝子进程识别id时读取到的是新开辟的内存数据父进程识别id时读取的是旧的内存空间里面的数据。所有进程都是bash进程的子进程。四、进程状态运行态(runing)又称为R状态凡是在运行队列里的进程都处于运行态。一个CPU里面会有一个运行队列(runqueue)所有在运行队列里排队或执行的进程都处于运行态。一个进程只要是把自己放到CPU上开始运行了并不是一直到执行完毕才把自己放下来。例如有一个进程里有一个死循环while(1)但其他进程依旧可以正常运行。因为每一个进程都会有一个叫做时间片的概念。例如时间片是10ms如果这个进程运行满10ms后就会被放到进程尾部进行排队。在一个时间段内CPU需要保证所有的进程代码都会被执行。--并发执行大量把进程从CPU上放上去拿下来的动作叫做进程切换。阻塞状态等待队列中的进程都处于阻塞状态。一个CPU只有一个运行队列但每个设备或进程都会有自己的等待队列例如有一些进程需要从键盘中读取数据但并没有人为在键盘中输入数据那么这个进程就会进入等待队列等待数据的输入。挂起状态将阻塞队列中进程的代码和数据放入磁盘中只保留进程PCB对象(task_struct)进行排队。当操作系统中内存资源严重不足时为了给内存空间节省出一大片空间会将阻塞状态下的进程的代码和数据放入磁盘中只保留进程PCB对象(task_struct)进行排队当进程准备好后再将代码和数据换入进入运行状态这种状态就是挂起状态全称阻塞挂起状态。Linux进程状态linux内核源代码/* * The task state array is a strange bitmap of * reasons to sleep. Thus running is zero, and * you can test for combinations of others with * simple bit tests. */ static const char * const task_state_array[] { R (running), /* 0 */ S (sleeping), /* 1 */ D (disk sleep), /* 2 */ T (stopped), /* 4 */ t (tracing stop), /* 8 */ X (dead), /* 16 */ Z (zombie), /* 32 */ };R状态(runing)运行态并不意味着进程一定在运行中进程也可能是在运行队列中等待。查看运行态#includestdio.h int main() { while(1); return 0; }执行while(1);死循环并用指令[ps ajx |head -1 ps ajx |grep text]查看进程状态。S状态(sleeping)浅度睡眠状态对应着阻塞状态表示该进程正在等待某种资源的状态。查看浅度睡眠状态#includestdio.h #includeunistd.h int main() { while(1) { printf(hello world\n); sleep(2); } return 0; }执行打印hello world死循环并用指令[ps ajx |head -1 ps ajx |grep text]查看进程状态。注R和S都表示进程在后台运行。D状态(disk sleep)深度睡眠状态也称磁盘状态有时候也叫不可中断睡眠状态(uninterruptible sleep)表示该进程正在等待磁盘写入完毕期间且该进程不能被任何人杀掉操作系统也杀不掉该进程。如果系统中显示出现了一个D状态的进程就说明这个系统已经快崩溃了。D状态不会响应任何请求除非D状态自己醒来或者拔掉电源线。查看D状态[dd]命令但可能会有风险。T状态(stopped)停止状态也类似于阻塞状态与S状态不同的是T状态是被主动控制的可能是在等待资源也可能并没有等待资源。查看T状态#includestdio.h #includeunistd.h #includesys/types.h int main() { printf(当前进程pid:%d ppid:%d\n,getpid(),getppid()); pid_t idfork(); //使用fork创建子进程 if(id0) { while(1) { printf(我是子进程,pid:%d ppid:%d\n,getpid(),getppid()); sleep(1); } } else if(id0) { while(1) { printf(我是父进程,pid:%d ppid:%d\n,getpid(),getppid()); sleep(1); } } else { } return 0; }使用gdb调试打断点时此时进程就处于T状态。或者是使用SIGSTOP[kill -19 进程编号]指令停止进程SIGCONT[kill -18 进程编号]指令启动进程。X状态(dead)终止状态这个状态只是一个返回状态你不会再进程列表中看到这个状态。Z状态(zombie)僵尸进程状态进程一般退出的时候如果父进程没有主动回收子进程信息子进程会一直让自己处于Z状态进程相关资源尤其是PCB(task_struct)结构体不能被释放。僵尸进程在父进程退出的瞬间会被操作系统进程回收。查看Z状态#includestdio.h #includeunistd.h #includesys/types.h int main() { printf(当前进程pid: %d,ppid: %d\n,getpid(),getppid()); pid_t idfork(); if(id0) { int cnt2; while(cnt) { printf(我是子进程,pid: %d,ppid: %d,cnt: %d\n,getpid(),getppid(),cnt--); sleep(3); } } else { while(1) { printf(我是父进程,pid: %d,ppid: %d\n,getpid(),getppid()); sleep(3); } } return 0; }执行text后使用指令[while :; do ps ajx | head -1 ps ajx | grep text; sleep 3; done]循环显示过滤后的text进程就可以看到Z状态的进程了。僵尸进程的危害内存泄漏孤儿进程父子进程当父进程先退出子进程的父进程会被改成1号进程(操作系统进程)表示该进程被系统进程领养因为这个孤儿进程未来也会退出也需要被释放。查看孤儿进程#includestdio.h #includeunistd.h #includesys/types.h int main() { pid_t idfork(); if(id0) { while(1) { printf(我是子进程,pid: %d,ppid: %d\n,getpid(),getppid()); sleep(3); } } else { int cnt1; while(cnt) { printf(我是父进程,pid: %d,ppid: %d,cnt: %d\n,getpid(),getppid(),cnt--); sleep(3); } printf(父进程已经退出\n); } return 0; }执行orphan可执行文件然后使用指令[while :; do ps ajx | head -1 ps ajx | grep orphan|grep -v grep; sleep 3; done]查看orphan的PPID变化就可以发现orphan的PPID在父进程断开后就变成了1。注孤儿进程无法用CtrlC退出不过可以使用kill指令退出。五、进程优先级1.基本概念优先级是什么CPU分配资源的先后顺序就是指进程的优先权(priority)。为什么会产生优先级因为资源是有限的进程是多个的注定了进程之间是竞争关系!--竞争性操作系统必须保证各个进程良性竞争所以设定了优先级。如果有些进程长时间得不到CPU资源该进程的代码就长时间无法得到推进--进程的饥饿问题。优先级怎么实现的PRI表示优先级NI表示优先级的修正数据。优先级是由PRI和NI组合实现的。2.查看系统进程在Linux操作系统中使用[ps -l]指令可以输出以下内容UID用户id指令[ls -aln]可以查看用户id。PID当前进程id。PPID父进程id。PRI进程优先级。NI进程优先级的修正数据。3.PRI和NI1.PRI表示的是进程的优先级或者通俗来说就是程序被CPU执行的先后顺序此值越小进程的优先级越高。2.NI就是所谓的nice值可表示为进程可被执行的优先级的修正数值。3.PRI的值越小越快被执行加入nice值之后将会使得PRI(新)PRI(旧)NI。注PRI(旧)的值一直等于80。4.当NI的值为负值时PRI会变小程序的优先级会变高当NI的值为正值时PRI会变大程序的优先级会变低。5.因此调整优先级在Linux中就是调整NI值。6.NI值的取值范围是[-20,19]一共是40个等级。查看和修改进程NI值的方法输入[top]指令可以查看所有进程的优先级信息。修改进程优先级的步骤1.切换成root用户非root用户无法修改优先级。2.输入[top]指令后按r键--输入需要修改的进程的PID--输入nice值[-20,19]如果超出范围则按照边界进行计算。4.进程切换操作系统中进程切换的方式CPU的运行队列结构中会有runing队列和一个waiting队列runing队列里的进程在根据优先级顺序依次运行过完一个时间片后会将进程继续按照优先级排到waiting队列中新增的进程也会在waiting队列中进行排队当runing里面所有的进程都运行完后运行队列会切换runing和waiting再次按照优先级顺序运行进程。并发多个进程在一个CPU下采用进程切换的方式在一段时间内让多个进程都得以推进称之为并发。CPU中的寄存器对于进程切换的作用CPU中有很多寄存器通用寄存器eax、ebx、ecx、edx。栈帧ebp、esp、eip。状态寄存器status。为了提高效率会将进程的高频数据放入到寄存器中。CPU寄存器中保存的是进程运行时进程的临时数据(进程的上下文)。进程从CPU上离开时要将自己的上下文数据保存好甚至带走保存的目的是为了当进程切换后再进入CPU时能够恢复进程自身的上下文数据。因此进程在被切换时1.保存上下文数据。2.恢复上下文数据。3.进程的临时数据在保存时会被保存在进程的PCB中额外知识1.查看kill功能列表指令kill -l[xxlVM-0-2-centos 913]$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN1 36) SIGRTMIN2 37) SIGRTMIN3 38) SIGRTMIN4 39) SIGRTMIN5 40) SIGRTMIN6 41) SIGRTMIN7 42) SIGRTMIN8 43) SIGRTMIN9 44) SIGRTMIN10 45) SIGRTMIN11 46) SIGRTMIN12 47) SIGRTMIN13 48) SIGRTMIN14 49) SIGRTMIN15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX2.间隔1s无限显示进程状态指令while :; do ps ajx | head -1 ps ajx | grep test; sleep 1; done3.进程之间的竞争性进程数目众多而CPU数目甚少甚至只有1个所以进程之间是有竞争性的。为了高效的完成任务更合理的竞争相关资源便有了优先级。4.进程的独立性多进程运行需要独享各种资源多进程运行期间互相各不干扰。5.并行多个进程在多个CPU下分别同时进行运行称之为并行。6.并发多个进程在同一个CPU下采用进程切换的方式在一段时间内让多个进程都得以推进称之为并发。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门