马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
目录
1 冯诺依曼体系结构
2 操作体系(Operator System)
概念
计划OS的目的
定位
总结
体系调用和库函数概念
3 历程
3.1 基本概念
3.2 描述历程-PCB
3.2 组织历程
3.3 检察历程
3.4 通过体系调用获取历程标示符
3.5 历程状态
在了解历程概念前我们还得了解下冯诺依曼体系结构和操作体系的概念与定位。
1 冯诺依曼体系结构
我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部门都遵守冯诺依曼体系:
- 输入单元:包括网卡,键盘, 鼠标,扫描仪, 写板,话筒等 ;
- 中央处置处罚器(CPU):含有运算器和控制器等 ;
- 输出单元:网卡,表现器,打印机等;
关于冯诺依曼,必须强调几点:
- 这里的存储器指的是内存 ;
- 不思量缓存情况,这里的CPU能且只能对内存举行读写,不能访问外设(输入或输出装备) ;
- 外设(输入或输出装备)要输入或者输出数据,也只能写入内存或者从内存中读取;
- 一句话,所有装备都只能直接和内存打交道;
对冯诺依曼的理解,不能只停留在概念上,要深入到对软件数据流理解上,请表明,从你登录上qq开始和某位朋友谈天开始,数据的活动过程。 在不思量网络层情况下,小明用qq向小红发送了一条消息,小明的电脑从键盘上读取信息然后加载到内存,再从内存将数据通过一系列操作发送到输出装备上(网卡),然后通过一系列的网络操作将数据发送到小红的输入装备上(网卡),小红的电脑再从输入装备中将数据读到内存,然后通过输出装备(表现器)将信息刷新到小红的电脑上,这里数据刷新是两个方面的,再成功发送后小明的电脑也会表现出已经成功发送后的信息。 2 操作体系(Operator System)
概念
任何计算机体系都包罗一个基本的程序集合,称为操作体系(OS)。笼统的理解,操作体系包括:
- 内核(历程管理,内存管理,文件管理,驱动管理)
- 其他程序(例如函数库,shell程序等等)
计划OS的目的
- 与硬件交互,管理所有的软硬件资源
- 为用户程序(应用程序)提供一个良好的实行环境
定位
在整个计算机软硬件架构中,操作体系的定位是:一款纯正的“搞管理”的软件。
总结
计算机管理硬件
- 先描述起来,用struct结构体
- 再组织起来,用链表或其他高效的数据结构
体系调用和库函数概念
- 在开辟角度,操作体系对外会表现为一个团体,但是会暴露本身的部门接口,供上层开辟使用,这部门由操作体系提供的接口,叫做体系调用。
- 体系调用在使用上,功能比较基础,对用户的要求相对也比较高,以是,故意的开辟者可以对部门体系调用举行适度封装,从而形成库,有了库,就很有利于更上层用户或者开辟者举行二次开辟。
3 历程
有了上面对冯诺依曼体系结构和操作体系的理解,我们天然可以想到历程也是先描述,再组织。
3.1 基本概念
- 课本概念:程序的一个实行实例,正在实行的程序等
- 内核观点:担当分配体系资源(CPU时间,内存)的实体。
有些教材书上甚至是给出这样的界说的:历程就是程序加载到内存中。但是我觉得这种描述是狭隘的不够详细的,详细的我们下面会给出表明.
3.2 描述历程-PCB
- 历程信息被放在一个叫做历程控制块的数据结构中,可以理解为历程属性的集合。
- 课本上称之为PCB(process control block),Linux操作体系下的PCB是: task_struct
task_struct是PCB 的一种:
- 在Linux中描述历程的结构体叫做task_struct。
- task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包罗着历程的信息
我们将所有历程的属性用一个队列来维护,当我们想要加载程序时就将它的PCB链接到该运行队列中,这样就很好的维护了历程。
那现在我们再来回复什么是历程?
历程=当出息序的代码和数据+内核关于历程的相关数据结构
task_ struct内容分类:
- 标示符: 描述本历程的唯一标示符,用来区别其他历程。
- 状态: 任务状态,退出代码,退出信号等。
- 优先级: 相对于其他历程的优先级。
- 程序计数器: 程序中即将被实行的下一条指令的地址。
- 内存指针: 包括程序代码和历程相关数据的指针,还有和其他历程共享的内存块的指针
- 上下文数据: 历程实行时处置处罚器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
- I/O状态信息: 包括表现的I/O哀求,分配给历程的I/O装备和被历程使用的文件列表。
- 记账信息: 大概包括处置处罚器时间总和,使用的时钟数总和,时间限制,记账号等。
- 其他信息
3.2 组织历程
- 可以在内核源代码里找到它。所有运行在体系里的历程都以task_struct链表的形式存在内核里。
3.3 检察历程
我们在Linux环境中创建了一个profile.cpp的Cpp文件,然后编译运行生成了一个叫做profile的可实行文件,我们可以通过一下下令来查找历程:
- ps ajx | head -1 && ps ajx | grep "查找进程的名字"
复制代码 当我们运行profile后来检察:
不难发现我们查询到了profile历程的一些基本信息,如果我们想不加上下面那一行的信息可以将下令反面多加一些内容:
- ps ajx | head -1 && ps ajx | grep "查找进程的名字" | grep -v grep
复制代码
当然,文件名可加可不接双引号。
我们还可以在./proc中查询:
3.4 通过体系调用获取历程标示符
我们向profile.cpp中写入以下代码:
- 1 #include<iostream>
- 2 #include<sys/types.h>
- 3 #include<unistd.h>
- 4 using namespace std;
- 5
- 6
- 7 int main()
- 8 {
- 9 while(1)
- 10 {
- 11 pid_t ret=getpid();
- 12 cout<<"hello"<<ret<<" "<<endl;
- 13 pid_t t=fork();
- 14 if(t==0)
- 15 {
- 16 while(1)
- 17 {
- 18 cout<<"我是一个子进程"<<" pid:"<<getpid()<<" ppid:"<<getppid()<<endl;
- 19 sleep(1);
- 20 }
- 21 }
- 22 else if(t>0)
- 23 {
- 24 while(1)
- 25 {
- 26 cout<<"我是一个父进程"<<" pid:"<<getpid()<<" ppid:"<<getppid()<<endl;
- 27 sleep(1);
- 28 }
- 29 }
- 30 }
- 31 return 0;
- 32 }
复制代码 当我们检察历程时:
fork()后实行流会变成两个,是先实行父历程还是子历程是由调度器决定的,fork()后的代码共享,我们通常是用if else 来举行分流的。
- 运行 man fork 熟悉fork
- RETURN VALUE
- On success, the PID of the child process is returned in the parent, and 0
- is returned in the child. On failure, -1 is returned in the parent, no
- child process is created, and errno is set appropriately.
复制代码- fork有两个返回值
- 父子历程代码共享,数据各自开辟空间,私有一份(接纳写时拷贝)
当我们只读数据不写数据时,父子历程是共享代码的,而当有其中一个实行流尝试修改数据时OS就会在当进步程触发写时拷贝另外生成一份。
怎样理解有两个返回值呢?
创建子历程本质上就是OS提供的一个函数,当函数内部举行return 时我们主体功能已经完成了。
3.5 历程状态
在了解历程状态前我们还得了解下什么是壅闭和挂起?
信赖大家在看一些操作体系的书的时候就见过类似于这样的图片:
壅闭状态是一种等待某种资源就绪,而导致的一种不被推进的过程。这么说有点儿抽象,我们来举一个栗子:
当有大量历程存在时我们是不是要先描述,再组织,前面我们说过组织历程靠的是内核中以某种数据结构来维护历程的PCB。假设你在应用市场要下载一个软件,但是下到一半时网络突然停止了,那么操作体系会一直等到网络资源恢复后再去运行其他历程吗?显然是不会的,假如操作体系这样计划的话那么岂非我们电脑上的其他程序就不运行了吗?就只等你一个?以是当网络资源停止时操作体系会将该历程从CPU的运行队列中拿走,放到对应硬件资源的等待队列中,等到网络资源恢复后再将该历程链接到CPU的运行队列中实行,而这种等待某种资源就绪而不被推进的一种状态就叫做壅闭状态。
而挂起又是什么意思呢?
由于机器的资源是有限的,在资源不敷的情况下操作体系可以临时将一些在内存中的历程淘汰出局,当条件允许的时候又会被操作体系给调回来,这个比较好理解就不在多做表明了。
看看Linux内核源代码怎么说
下面的状态在kernel源代码里界说:
- /*
- * 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运行状态(running): 并不意味着历程一定在运行中,它表明历程要么是在运行中要么在运行队列里。
- S就寝状态(sleeping): 意味着历程在等待事件完成(这里的就寝偶然候也叫做可停止就寝 (interruptible sleep))。
- D磁盘休眠状态(Disk sleep)偶然候也叫不可停止就寝状态(uninterruptible sleep),在这个状态的历程通常会等待IO的竣事。
- T停止状态(stopped): 可以通过发送 SIGSTOP 信号给历程来停止(T)历程。这个被停息的历程可 以通过发送 SIGCONT 信号让历程继续运行。
- X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
第一个R状态并不是表现历程在运行中,有大概是在运行队列里面。接下来给出大家一个程序大家猜猜这时一种什么状态?
- 1#include<iostream>
- 2 #include<unistd.h>
- 3 using namespace std;
- 4
- 5 int main()
- 6 {
- 7 while(1)
- 8 {
- 9 cout<<"我是一个进程 我的pid是:"<<getpid()<<endl;
- 10 }
- 11 return 0;
- 12 }
复制代码 我信赖第一次大家或许都会想:既然是死循环那么程序肯定在运行状态了,也就是R状态,事实真的是这样吗?我们一起来看看:
我们居然惊讶的发现,该历程居然处于S状态,这不合理吧?
其实大家想想,我们使用cout往表现屏中打印字符串,这里的输出装备就是我们的屏幕,但是输入输出装备是很慢的相对于CPU来说,大概当输入输出一个字符时CPU就已经跑了上百万行代码了。以是当我们往表现屏打印字符串时,操作体系并不会直接将该历程直接运行,而是等到当某种资源就绪后会将该历程链接到运行队列中,那这S状态与我们上面讲的壅闭状态有点儿类似呀,其实S状态本质就是壅闭状态。(要想获得R状态就得在资源预备就绪的一刹时来用下令检察,太难抓了,我就不演示了)
那反面的+号是什么意思呢?
这里+表现该历程是前台运行的,当我们使用ctrl+c的时候能够终止掉该历程,不写+表现的是后台运行的,这时用ctrl+c是无法终止掉该程序的,要用下令杀掉历程来终止,我们接下来会介绍的。
当我们注释掉代码里的打印字符时:
- 1 #include<iostream>
- 2 #include<unistd.h>
- 3 using namespace std;
- 4
- 5 int main()
- 6 {
- 7 while(1)
- 8 {
- 9 // cout<<"我是一个进程 我的pid是:"<<getpid()<<endl;
- 10 }
- 11 return 0;
- 12 }
复制代码 再来看看:
我们会发当代码历程的状态已经变成了R.
同理,当我们往键盘中输入数据时险些绝大多数都是S状态,只有刚输入数据那一刹时才是R状态。
D状态是一种不可停止休眠状态,这时就算是强如操作体系都不能够干掉他,但是这种场景一般很少见,除非机器快宕机了。
T状态是一种停止状态,我们可以通过一个下令来改变当前的状态位T状态,大家可以检察有哪些kill下令:
这时就会出现很多与kill相匹配的选项:
- 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) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
- 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
- 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
- 48) SIGRTMIN+14 49) SIGRTMIN+15 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) SIGRTMAX
复制代码 这里我们使用19号下令来停息历程:
那怎样恢复呢?
可以用kill下令配带的18号选项:
但是大家发现没有,这里的S状态是没有加+,也就是该历程是在后台运行的,不可以被ctrl+c终止
这时应该怎么处置处罚呢?
我们可以试试kill带的9号选项:
这时该历程已经被干掉了。
除了T状态还有一个t状态,这里的t表现的一种追踪停息,类似于我们打断点运行到断点处:
我们可以打开Makefile加入断点信息,然后调试起来
不难发现此时的状态已经变成了t.
至于反面的X和Z状态将会放在下一节课来讲授,如果该文对你有帮助的话能不能3连支持一下博主呢?
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |