注册了域名之后怎么做网站,机械类网站如何做网站优化,明星网页设计范例,有自己网站好处目录 一、冯诺依曼体系结构二、操作系统(OS)1. 操作系统是什么2. 操作系统如何做管理3. 系统调用和库函数概念 三、进程1. 进程是什么#xff1f;2. 描述进程-PCB3. 查看进程的方法 四、进程状态1 运行、阻塞和挂起状态2 Linux中的进程状态 五、进程优先级1. 什么是优先级2.查… 目录 一、冯诺依曼体系结构二、操作系统(OS)1. 操作系统是什么2. 操作系统如何做管理3. 系统调用和库函数概念 三、进程1. 进程是什么2. 描述进程-PCB3. 查看进程的方法 四、进程状态1 运行、阻塞和挂起状态2 Linux中的进程状态 五、进程优先级1. 什么是优先级2.查看优先级3. 修改优先级 六、其他概念七、环境变量1.引入2.环境变量和本地变量的关系3.命令行参数表和环境变量表4.子进程获取环境变量的方式 八、进程地址空间1.虚拟地址空间的引入2.虚拟地址空间布局 3.为什么要存在虚拟地址空间 一、冯诺依曼体系结构
冯诺依曼思想包括存储程序、程序控制和计算机的五大功能部件。 存储程序将程序存放在计算机的存储器中。 程序控制 按指令地址访问存储器并取出指令经译码依次产生指令执行所需的控制信号实现对计算的控制完成指令的功能。 五大功能部件控制器、运算器、存储器、输入设备、输出设备。 其中运算器完成算术运算、逻辑运算 控制器控制指令的执行 根据指令功能给出实现指令功能所需的控制信号 控制器和运算器构成了中央处理器CPU。 存储器就是内存存放程序和数据带电存储具有掉电易失的特性。 输入设备能够输入操作者提供的原始信息并将其转化为机器能识别的如键盘、鼠标等。 输出设备将计算机处理的结果用人们或其他机器能够接受的方式输出如显示屏。
在不考虑缓存的情况下CPU只能对内存进行读写不能直接访问其他设备。外设输入设备或输出设备想要输入或输出数据也只能写入内存或从内存中读取。
二、操作系统(OS)
1. 操作系统是什么
操作系统是一个进行软硬件资源管理的软件。 操作系统包括内核进程管理、内存管理、文件管理、驱动管理、其他程序如函数库shell程序等等 操作系统为什么对软硬件资源进行管理呢 操作系统通过管理好软硬件资源(手段)给用户提供良好(安全、稳定、高效、功能丰富)的执行环境(目的)。
2. 操作系统如何做管理
先描述在组织。将需要管理的对象用结构体描述再将每个结构体进行连接形成链表一样的数据结构。 当操作系统下达命令后驱动程序就会对这些结构进行增删查改等操作。 计算机的层状结构
3. 系统调用和库函数概念
系统调用在开发角度操作系统对外会表现为一个整体但是会暴露自己的部分接口供上层开发使用这部分由操作系统提供的接口叫做系统调用。在执行一段程序时比如printf(“hello world”)时实际上进行了系统调用但我们并不知道因为编译器帮你做了。库函数系统调用在使用上功能比较基础对用户的要求相对也比较高所以有心的开发者可以对部分系统调用进行适度封装从而形成库有了库就很有利于更上层用户或者开发者进行二次开发。
三、进程
1. 进程是什么
进程是程序的一个执行实例是正在执行的程序。 以上是书本中的概念一句话来概括进程内核描述进程的数据结构当前进程的代码和数据。 当我们写好一段代码经过编译、链接等过程后生成了可执行程序此时的可执行程序是一个文件存储在磁盘中。当我们运行该程序时该程序的代码和数据就会被加载到内存中。此时操作系统会将进程的各种属性放在一个叫做PCB的结构体中并将PCB用链表等数据结构管理起来方便进行增删查改。
2. 描述进程-PCB
进程信息被放在一个叫做进程控制块的数据结构中可以理解为进程属性的集合。课本上称之为PCBprocess control blockLinux操作系统下的PCB是task_struct。
Linux中描述进程的结构体叫做task_struct它会被装载到RAM(内存)里并且包含着进程的信息。 task_struct内容分类
标示符描述本进程的唯一标示符用来区别其他进程。状态任务状态退出代码退出信号等。优先级相对于其他进程的优先级。程序计数器程序中即将被执行的下一条指令的地址。内存指针包括程序代码和进程相关数据的指针还有和其他进程共享的内存块的指针。上下文数据进程执行时处理器的寄存器中的数据。I/O状态信息包括显示的I/O请求分配给进程的I/O设备和被进程使用的文件列表。记账信息可能包括处理器时间总和使用的时钟数总和时间限制记账号等。其他信息。
3. 查看进程的方法
进程的信息可以通过/proc系统文件夹查看
要获取PID为1的进程信息需要查看/proc/1这个文件夹。 大多数进程信息同样可以使用top和ps这些用户级工具来获取。 当我们执行上述程序便可以通过以下指令查询到该进程的信息。 通过系统调用查看进程PID进程idPID通过getpid()获取父进程idPPID通过getppid()获取 当我们多次执行程序能够发现每次启动时进程id都不同但父进程id都相同 这是因为这些进程有共同的父进程bash——命令行解释器。
通过系统调用创建进程 创建进程需要用到fork先通过man手册来了解一下fork。 fork能够创建子进程在父进程中返回值为子进程id在子进程中返回值为0。 显然fork调用后的内容打印了两边原因是fork()创建了子进程父子进程都打印了fork后。
我们还可以通过搭配if-else来进行分流利用fork的返回值进行区分 fork之后执行流会变成两个执行流当有一个执行流尝试修改数据时操作系统会为该进程将代码和数据拷贝一份再进行修改我们称之为写时拷贝。
四、进程状态
1 运行、阻塞和挂起状态
程序在运行时需要CPU读取程序的数据并进行计算但进程的数量一般会多于CPU所以操作系统采用了运行队列来对进程进行管理。进程入队列等待CPU资源。CPU调度进程就是从运行队列中找到进程PCB并执行进程对应的代码和数据。 进程的运行状态并不是指进程正在运行而是指这个进程的PCB在CPU的运行队列中。 进程还会占用外设资源但外设的运行速度很慢也会出现多个进程访问一个硬件的情况所以这里进程也需要排队。当CPU调度的某个进程需要访问外设时操作系统就会把这个进程放到硬件的等待队列中直到硬件准备就绪此时这个进程的状态就是阻塞状态。 当多个进程状态都是阻塞时这些进程无法被CPU立即在执行需要排队很长时间进程的代码和数据始终占用着内存操作系统为了避免这样的浪费会将这些进程的代码和数据暂时保存在磁盘上但保留进程的PCB在内存里这样的进程称之为挂起进程。等到进程需要的硬件资源准备就绪后操作系统会将进程的代码和数据唤回内存。
2 Linux中的进程状态
运行状态并不意味着进程一定在运行中它表明进程要么是在运行中要么在运行队列 里。 只要执行一个死循环程序即可观察到运行状态。
休眠状态当我们像上述代码中添加一个printf语句观察现象。 观察到此时状态变成了S这就是休眠状态。 因为我们的代码访问了显示器显示器是外设速度很慢99%的时间都是进程等待显示器就绪1%的时间是CPU在运行所以查看到进程是休眠状态是阻塞状态的一种。
停止状态当进程被停止时也是阻塞状态的一种。
kill -19 进程id --- 停止运行进程
kill -18 进程id --- 继续运行进程可以看见当我们输入kill -19后进程的状态从R变成了T也就是停止了。 这里的是用来标志这个进程是前台还是后台进程状态后面带表示该进程在前台不带则是后台进程。 后台进程在运行时shell命令行可以使用但无法使用ctrlC将后台进程终止需要使用kill -9来杀掉进程。 磁盘休眠状态有时候也叫不可中断睡眠状态在这个状态的进程通常会等待IO的结束。 当阻塞进程太多内存空间不足操作系统会将一些进程挂起但如果依旧无法解决问题OS就会将进程杀死但杀死进程可能会导致数据的丢失造成巨大损失所以就提供了磁盘休眠状态用D来表示这样的进程无法被OS杀掉只能等IO结束进程自己醒来。但这种状态一般情况下不会出现只有在高IO的情况下才可能会出现。 跟踪状态当我们调试某个可执行程序时如果打了断点当进程运行到断点就会停下来等待我们的下一步操作此时查看进程的状态就是跟踪状态用小写字母 t 表示。 僵死状态 一个进程在完成任务之后其父进程或操作系统需要知道该任务完成的结果所以当进程终止时OS的机制是不立即释放该进程的内存资源的要保存到其父进程来读取进程的结果。 但如果某个进程退出后没有被父进程或OS回收这样的进程就是僵尸进程。 下面用一段代码演示僵尸进程的产生首先创建一个子进程让子进程在父进程之前退出此时子进程就会处于僵死状态。
#include stdio.h
#include unistd.h
#include stdlib.h
int main()
{pid_t id fork();if (id 0){printf(我是子进程,pid:%d, ppid:%d\n, getpid(), getppid());sleep(5);exit(0);}else if (id 0){int count 10;while (count--){printf(我是父进程,pid:%d, ppid:%d\n, getpid(), getppid());sleep(1);}}return 0;
} 可见当程序执行五秒之后子进程退出但父进程不回收子进程的状态变为Z僵死状态。 当父进程运行结束操作系统会将父进程和子进程一起回收避免内存泄漏。 进程的退出状态也属于进程的基本信息会被保存在进程PCB中也就是说如果父进程一直不读取子进程的退出状态那么PCB就要一直维护这种状态这是就会产生内存泄露的问题。 僵尸进程无法被杀死因为它已经死了。 死亡状态当进程死亡后操作系统会很快的回收进程的所有的资源和数据以至于我们无法观察到X状态。它的PCB和对应的代码和数据都被释放不再占用内存资源。 孤儿进程如果一个程序运行时父进程先退出那么子进程就变成了孤儿进程此时1号init进程就会回收它。 当我们用刚才的程序提前杀死父进程可以看到子进程的PPID变成了1并且该进程变成了后台进程。 作为操作系统是必须要领养这个孤儿进程的为了防止内存泄漏
五、进程优先级
1. 什么是优先级
cpu资源分配的先后顺序就是指进程的优先权priority。 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用可以改善系统性能。 还可以把进程运行到指定的CPU上这样一来把不重要的进程安排到某个CPU可以大大改善系统整体性能。
2.查看优先级 通过ps -l指令可以查看进程的信息其中PRI和NI组合起来用来表示进程的优先级。 PRI也还是比较好理解的即进程的优先级或者通俗点说就是程序被CPU执行的先后顺序此值越小进程的优先级别越高。 那NI呢?就是我们所要说的nice值了其表示进程可被执行的优先级的修正数值。 PRI值越小越快被执行那么加入nice值后将会使得PRI变为 PRI(new)PRI(old)nice。 这样当nice值为负值的时候那么该程序将会优先级值将变小即其优先级会变高则其越快被执行。 所以调整进程优先级在Linux下就是调整进程nice值。 nice其取值范围是-20至19一共40个级别。
3. 修改优先级
想要修改优先级需要使用top指令并且需要管理员身份这个指令用来修改nice值范围是-20到19如果输入范围以外的数也不会获得更大或者更小的数值因为OS不会让我们过度修改nice值。 方法进入top后按“r”–输入进程PID–输入nice值
六、其他概念
竞争性: 系统进程数目众多而CPU资源只有少量甚至1个所以进程之间是具有竞争属性的。为了高效完成任务更合理竞争相关资源便具有了优先级。 独立性: 多进程运行需要独享各种资源多进程运行期间互不干扰。 并行: 多个进程在多个CPU下分别同时进行运行这称之为并行。 并发: 多个进程在一个CPU下采用进程切换的方式在一段时间之内让多个进程都得以推进称之为并发。
七、环境变量
1.引入
我们平时使用的Linux指令其实是可执行程序但我们自己创建的可执行程序在运行时需要加上./而系统提供的指令不需要这是为什么呢 要执行一个程序需要先找到它这也是为什么我们要在前面加上./指的是当前目录而系统指令的位置在/usr/bin目录下想要让我们创建的可执行程序不用加上./可以将其拷贝到/usr/bin目录下但不建议这样做因为自己创建的程序未经过严格的测试会污染系统的指令池。 那为什么在/usr/bin目录下系统就能够找到呢 因为系统中存在环境变量PATH操作系统在启动时会定义一个PATH变量可以利用echo $PATH指令进行查看。 在执行系统指令时操作系统会默认在PATH环境变量里面的路径查找我们输入的指令所以想要不带./运行自己的程序可以将程序所在路径添加进环境变量PATH中。 我们可以随意的修改PATH因为只要重新登陆PATH就会恢复。 使用export指令来修改环境变量。 export PATH路径 这种操作会覆盖原有的内容不建议使用 export PATH$PATH:路径 建议使用 当修改成功之后就可以不加./执行自己的程序了。 不过重新登陆后环境变量就会恢复到默认所以不加./执行自己的程序也只限定在本次登录。
2.环境变量和本地变量的关系
父进程的本地变量不会被子进程继承但环境变量会被继承下去。
#include stdio.h
#include stdlib.h#define MY_ENV myvalint main()
{char* myenv getenv(MY_ENV);if (myenv NULL){printf(%s:not found\n, MY_ENV);return 1;}printf(%s%s\n, MY_ENV, myenv);return 0;
}使用set指令可以显示所有本地变量使用env指令可以显示所有环境变量。 取消本地变量unset变量名
在定义环境变量时可以带双引号也可以不带但如果环境变量带有空格就要加上双引号了。
3.命令行参数表和环境变量表
在main函数中有几个隐藏的参数平时在做OJ题的时候有可能会注意到。
int main(int argc, char* argv[])不过我们平时并不使用这些参数但在系统编程中这些参数比较常用。
int main(int argc, char* argv[])
{int i 0;for (; i argc; i){printf(argv[%d]-%s\n, i, argv[i]);}return 0;
}通过上面的例子可以看出main函数中的第一个参数argc是命令行中运行程序的时候以空格为分隔符字符串的个数而argv指针数组中的指针指向的就是这些字符串。 系统提供的各种指令能够通过带-a、-l等实现不同的功能就是这样实现的
#include stdio.h
#include unistd.h
#include stdlib.h
#include string.h
void Usage(const char* name)
{ printf(\nUsage: %s -[a|b|c]\n\n, name); exit(0);
} int main(int argc, char* argv[])
{ if (argc ! 2) Usage(argv[0]); if (strcmp(argv[1], -a)) printf(打印当前目录下的文件名\n); else if (strcmp(argv[1], -b)) printf(打印当前目录下文件的详细信息\n); else if (strcmp(argv[1], -c)) printf(打印当前目录下的文件名(包含隐藏文件)\n); else printf(更多功能待开发\n); return 0;
}利用这两个参数可以实现一个如上所示的进程。
4.子进程获取环境变量的方式
在前面我们知道了可以通过getenv()系统调用可获得环境变量的值。 下面介绍的一种方式也是通过main函数的参数char* env[]
int main(int argc, char* argv[], char* env[])
{int i 0;for (; env[i]; i){printf(env[%d]-%s\n, i, env[i]);}return 0;
}通过上面这段代码我们打印出了所有的环境变量。
C语言提供了一个第三方的指针变量environ在调用main函数时系统就把这个变量传给了mainenviron就相当于指针数组env[]的另一个数组名environ指向的就是env的第一个元素。environ是一个二级指针下面用environ打印环境变量
int main()
{extern char** environ;int i 0;for (; environ[i]; i){printf(environ[%d]-%s\n, i, environ[i]);}return 0;
}八、进程地址空间
1.虚拟地址空间的引入
#include stdio.h
#include unistd.hint global_val 100;int main()
{pid_t id fork();if (id 0){int cnt 0;while (1){printf(我是子进程, pid:%d, ppid:%d, global_val:%d, global_val:%p\n, getpid(), getppid(), global_val, global_val);sleep(1);cnt;if (cnt 3){global_val 300;printf(子进程已经更改了global_val的值了!\n);}}}while (1){printf(我是父进程, pid:%d, ppid:%d, global_val:%d, global_val:%p\n, getpid(), getppid(), global_val, global_val);sleep(1);}return 0;
}从上面程序的结果来看一个全局变量在地址并未改变的情况下竟然出现了不同的值这是为什么呢首先一个变量肯定是只能有一个值的但地址只有一个这就说明现在内存中应该出现了两个变量。而一个变量在内存中只能有一个地址那么我们打印出来的一定不是真实的地址也就是说现在的内存中有两个全局变量分别属于父子进程并且他们都拥有自己的物理内存地址只是他们共用了一个虚拟地址。之前在学习C和C时所看到的其实都是虚拟地址真正的物理地址用户看不到统一由操作系统保管。
2.虚拟地址空间布局 代码段存放函数体的二进制代码代码段是只读的可以防止其他进程恶意修改正在运行的进程的二进制指令程序的执行就是从代码段中的main函数开始执行程序运行结束后由操作系统回收。 初始化数据段用于存储初始化的全局变量、static变量、对象、字符串、数组等常量。但基本类型常量不在这里它们在代码段。 未初始化数据段包含所有未初始化的全局变量和static变量此段中所有变量由0或NULL初始化。 堆区堆区时向上增长的程序运行时动态申请的内存空间就是在堆上开辟的由开发人员手动申请释放。 映射段也称共享区存储动态链接库、以及共享文件等。 栈区栈时向下增长的函数的局部变量、返回值、形参等都在栈区上函数调用时的栈帧就在栈上。
mm_struct就是操作系统描述虚拟空间所构造的结构体。
3.为什么要存在虚拟地址空间
虚拟内存是计算机系统内存管理的一种技术它让进程认为它拥有连续可用的内存实际上它通常被分割成多个物理内存碎片还有部分暂时存储在外部磁盘存储器上需要时才进行数据交换。 进程在和外设IO的过程所占内存大小为4KB即4096个连续的物理地址这部分区域被称为页。 进程无法直接接触物理内存只能通过虚拟地址依靠页表映射物理地址的方式来间接访问物理内存。因为页表的存在进程在访问不属于当前进程的地址时页表就会拦截进程非法访问地址的请求。 如上面的例子当子进程修改global_val时操作系统会先拷贝这个变量更改页表映射最后让子进程对数据进行修改这样的技术被称为写时拷贝。这样保证了进程的独立性通过虚拟地址空间和页表让不同的进程使用同一个虚拟地址能够映射到不同的物理内存。