如何建设一个文件分享网站,轮胎 东莞网站建设,首都之窗门户网站首页,企业官网邮箱怎样申请目录
冯诺依曼体系结构
操作系统
概念
设计os的目的
定位
如何理解管理
总结
系统调用和库函数概念
进程
描述进程-pcb
组织进程
查看进程
通过系统调用获取进程标示符
通过系统调用创建进程-fork初识
进程状态
阻塞和挂起
Z(zombie)-僵尸进程 冯诺依曼体系结…目录
冯诺依曼体系结构
操作系统
概念
设计os的目的
定位
如何理解管理
总结
系统调用和库函数概念
进程
描述进程-pcb
组织进程
查看进程
通过系统调用获取进程标示符
通过系统调用创建进程-fork初识
进程状态
阻塞和挂起
Z(zombie)-僵尸进程 冯诺依曼体系结构
我们常见的计算机如笔记本。我们不常见的计算机如服务器大部分都遵守冯诺依曼体系。 截至目前我们所认识的计算机都是有一个个的硬件组件组成 输入单元包括键盘, 鼠标扫描仪, 写板等 中央处理器(CPU)含有运算器和控制器等 输出单元显示器打印机等 关于冯诺依曼必须强调几点 这里的存储器指的是内存 不考虑缓存情况这里的CPU能且只能对内存进行读写不能访问外设(输入或输出设备) 外设(输入或输出设备)要输入或者输出数据也只能写入内存或者从内存中读取。 一句话所有设备都只能直接和内存打交道。 对冯诺依曼的理解不能停留在概念上要深入到对软件数据流理解上假如从你登录上qq开始和某位朋友聊 天开始数据的流动过程。从你打开窗口开始给他发消息到他收到消息之后的数据流动过程。如果是在qq上发 送文件呢
我们通过键盘输入一条信息然后会传给存储器存储器再传给运算器和控制器运算器和控制器统称为cpucpu处理完我们的信息就会发送到qq通过网络上传给我们的朋友。
操作系统
概念
任何计算机系统都包含一个基本的程序集合称为操作系统(OS)。笼统的理解操作系统包括 内核进程管理内存管理文件管理驱动管理 其他程序例如函数库shell程序等等 这就是进程管理系统会给每个进程合理安排资源。
设计os的目的 与硬件交互管理所有的软硬件资源 为用户程序应用程序提供一个良好的执行环境 定位
在整个计算机软硬件架构中操作系统的定位是一款纯正的“搞管理”的软件
也就是说在软件方面分配好内存给它们运行在硬件上合理分配软件去使用内存还有用户的读写操作。
如何理解管理
这个时候就可以 以我们自己为例子了大家学生时代基本都是在学校的吧那么学校就是一个计算机校长就是操作系统我们就是一堆堆的“软件.毕竟铁打的学校流水的学生嘛。
那么学校是每个学期都要安排考试如果有人考试没过下学期就要安排补考大家都逢考必过校长怎么知道有什么人需要补考呢这个时候就需要通知我们的辅导员调取我们的成绩信息让没过的同学在安排好的教室和时间去补考。这个时候辅导员就是一个小的操作系统它管理着我们校长管理着辅导员。我们每个学期都要通过考试证明我们是合格的当我们各项指标都符合要求时就可以毕业而校长和辅导员就是管理我们的os他们是管理方审核我们的毕业要求而我们就是被管理方去申请这个系统的资源。 总结
计算机管理硬件 1. 描述起来用struct结构体 2. 组织起来用链表或其他高效的数据结构
也就是先描述再组织
系统调用和库函数概念
在开发角度操作系统对外会表现为一个整体但是会暴露自己的部分接口供上层开发使用这部分 由操作系统提供的接口叫做系统调用。
系统调用在使用上功能比较基础对用户的要求相对也比较高所以有心的开发者可以对部分系统 调用进行适度封装从而形成库有了库就很有利于更上层用户或者开发者进行二次开发。
进程
基本概念
课本概念程序的一个执行实例正在执行的程序等
内核观点担当分配系统资源CPU时间内存的实体。 进程不只是包含我们的软件还有一些后台进程也就是只要我们电脑开机了就有进程运行
描述进程-pcb
进程信息被放在一个叫做进程控制块的数据结构中可以理解为进程属性的集合。
课本上称之为PCBprocess control blockLinux操作系统下的PCB是task struct
task_struct-PCB的一种
task_struct 在Linux中描述进程的结构体叫做task_struct。
task_struct是Linux内核的一种数据结构它会被装载到RAM(内存)里并且包含着进程的信息。 task_ struct内容分类 标示符: 描述本进程的唯一标示符用来区别其他进程。 状态: 任务状态退出代码退出信号等。 优先级: 相对于其他进程的优先级。 程序计数器: 程序中即将被执行的下一条指令的地址。 内存指针: 包括程序代码和进程相关数据的指针还有和其他进程共享的内存块的指针 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子要加图CPU寄存器]。 IO状态信息: 包括显示的I/O请求,分配给进程的IO设备和被进程使用的文件列表。 记账信息: 可能包括处理器时间总和使用的时钟数总和时间限制记账号等。 其他信息 组织进程
可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。
linux的内核源码可以在网上下载学习
查看进程
进程的信息可以通过 /proc 系统文件夹查看
如要获取PID为1的进程信息你需要查看 /proc/1 这个文件夹。 大多数进程信息同样可以使用top和ps这些用户级工具来获取
比如我们现在写一个循环运行的文件 #include stdio.h #include sys/types.h #include unistd.h int main() { while(1){ sleep(1); } return 0; } 通过系统调用获取进程标示符
进程idPID 父进程idPPID #include stdio.h #include sys/types.h #include unistd.h int main() { printf(pid: %d\n, getpid()); printf(ppid: %d\n, getppid()); return 0; } 通过系统调用创建进程-fork初识 运行man fork认识fork fork有两个返回值 父子进程代码共享数据各自开辟空间私有一份采用写时拷贝 比如说我们现在写一个这样的程序
fork 之后通常要用if进行分流 #include stdio.h #include sys/types.h #include unistd.h int main() { int ret fork(); if(ret 0){ perror(fork); return 1; } else if(ret 0){ //child printf(I am child : %d!, ret: %d\n, getpid(), ret); }else{ //father printf(I am father : %d!, ret: %d\n, getpid(), ret); } sleep(1); return 0; } 也就是我们说的fork有两个返回值当ret为0时进入分支语句打印一次当ret不为0也就是父进程打印一次。
进程状态
在学习进程状态前我们先说一下两个关于进程运行的情况
阻塞和挂起 阻塞 进程因为等待某种条件资源就绪而导致的一种不推进的状态如我们常说的卡住了一般页面无法响应、因网络中断下载任务无法继续执行等。或者说阻塞就是当前进程不被CPU调度。事实上进程要通过等待的方式等某个具体的资源被别人用完或者有了某个资源之后再使用该资源。 我们知道操作系统对软硬件做管理其方式可以被总结为先描述再组织 。其中进程被描述为结构体 task_struct 硬件被管理时同样也是被描述为一个结构体如 struct dev 每个软硬件对应的结构体中都包括了关于自身的信息。值得注意的是在每个硬件对应的结构体中还包含了指向进程控制块 PCBtask_struct 的指针可以认为该指针指向了一个进程队列的队头通过该指针可以对某个进程队列进行管理。事实上一个进程处在运行状态时可以表示该进程处在CPU进程调度的运行队列中而当某个进程因等待某种资源而无法继续推进时通常是等待某种硬件资源如磁盘、网卡、键盘等CPU就会将该进程调出当前的运行队列并调入其所等待资源对应的等待队列中此时该进程就处在一种 阻塞 状态。换句话说当某个进程处于阻塞状态时就表示该进程对应的结构体 task_struct 正在某种被操作系统管理的资源下排队当该资源准备就绪后再将该进程调回CPU的运行队列中继续排队运行。 挂起 当因为等待某种资源就绪进程对应PCB由运行队列转至资源下的等待队列时考虑到内存空间紧张CPU会将因为等待而暂时无法运行的进程对应的代码和数据先由内存转移到磁盘中此时进程即为挂起状态等到该进程可以被运行时再将对应的代码和数据由磁盘转移回内存中。 看看Linux内核源代码怎么说
为了弄明白正在运行的进程是什么意思我们需要知道进程的不同状态。一个进程可以有几个状态在 Linux内核里进程有时候也叫做任务。 下面的状态在kernel源代码里定义可以在网上找到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这个状态只是一个返回状态你不会在任务列表里看到这个状态。
进程状态查看命令 ps aux / ps axj 二选一 ps -aux 是以BSD方式显示
a 显示所有用户的进程(show processes for all users)
u 显示用户(display the processs user/owner)
x 显示无控制终端的进程(also show processes not attached to a terminal)
ps-axj
a: 显示所有用户进程
x显示没有控制终端的进程
j显示与作业有关的信息显示的列会话期IDSID进程组IDPGID控制终端TT终端进程组IDTRGID 我们可以写一段代码来观察进程的状态 #include stdio.h #include unistd.h int main() { while(1) { printf(我是一个进程\n); sleep(1); } return 0; } 我们可以看到程序一直在打印这段文字那么这个时候程序是不是一直在运行呢
通过命令 ps axj | head -1 ps axj | grep test | grep -v grep
查看对应的进程状态 可以发现虽然该进程看上去似乎一直在运行但所显示的进程状态却表示其处于 S睡眠状态 。这是由于在这段死循环中代码中我们需要显示器资源来显示输出内容这是一个进程显然显示器资源不会一直只供该进程使用即该进程的运行需要等待显示器资源的就绪也就是上述所说的睡眠状态。
下面我们又将代码中的输出语句注释使该死循环中为空重新编译运行再次查看相应进程状态此时可以看到该进程处于 R运行状态 这是因为此时该进程不需要等待某个资源就绪因此其一直处于运行状态。 我们可以看到我们的状态都有一个号
这是因为我们的程序都在前台运行也就是我们的窗口一直在被占用要解除这种状态我们就ctrlC就可以退出程序了。
这个时候就没有这个进程了。
也可以通过kill -9指令去终止杀死这个进程
kill- l 查看kill指令 我们可以把程序再跑起来再用指令去终止 T就是停止状态 这个时候没有号就是在后台运行ctrlC就终止不了了
我们就通过kill -9杀死进程。
还有一个小t状态当进程正在被跟踪时就处于 t 这个特殊状态其本质上也是一种停止状态。例如调试程序时触发断点而停止运行此时对应进程就处在 t 状态。
这里补充一下如果我们在gdb调试可执行文件时报这样的错误
No symbol table is loaded. Use the file command. 问题应该是我们编译时没有加上ggdb3我们再加上编译一遍基本就可以了
gcc -ggdb3 -o test test.c 这就是我们运行程序调试程序还有打了断点之后程序所处的不同状态
X(dead)死亡状态 该状态只是一个返回状态瞬时状态我们不会在任务列表里看到这个状态。事实上我们创建进程无非是想通过进程完成一些任务而对于任务完成结果我们可能关心也可能不关心这就涉及到一个概念 – 退出码 。所谓 退出码 其实就是我们编写的代码中最常见的
main()主函数中的{return 0}也就是return的那个数字
我们可以通过 echo $ 命令来查看进程退出码。 #include stdio.h #include unistd.h int main() { int number 3; if(number 3) return 1; sleep(1); return 0; } Z(zombie)-僵尸进程
僵死状态Zombies是一个比较特殊的状态。
当进程退出并且父进程使用wait()系统调用,后面讲 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程 僵死进程会以终止状态保持在进程表中并且会一直在等待父进程读取退出状态代码。 所以只要子进程退出父进程还在运行但父进程没有读取子进程状态子进程进入Z状态
我们直接写一个程序看看原理 #include stdio.h #include stdlib.h int main() { pid_t id fork(); if(id 0){ perror(fork); return 1; } else if(id 0){ //parent printf(parent[%d] is sleeping...\n, getpid()); sleep(30); }else{ printf(child[%d] is begin Z...\n, getpid()); sleep(5); exit(EXIT_SUCCESS); } return 0; } 也就是说如果我们的父进程进入休眠没有读取子进程的返回码子进程一直无法把返回码给到父进程那么子进程就会进到僵尸状态
僵尸状态有什么危害呢 进程的退出状态必须被维持下去因为他要告诉关心它的进程父进程你交给我的任务我办的怎 么样了。可父进程如果一直不读取那子进程就一直处于Z状态是的 维护退出状态本身就是要用数据维护也属于进程基本信息 所以保存在task_struct(PCB)中换句话说 Z状态一直不退出PCB一直都要维护是的 那一个父进程创建了很多子进程就是不回收 是不是就会造成内存资源的浪费是的因为数据结构 对象本身就要占用内存 想想C中定义一个结构体变量对象是要在内存的某个位置进行开辟空 间 内存泄漏?是的 有一些知识点我放在下一篇再写啦一时间写不过来啦哈哈哈哈