目录
一、操作系统
概念
设计操作系统的目的
定位
如何理解“管理”
系统调用和库函数概念
二、进程
概念
描述进程—PCB(process control block)
查看进程
进程状态
进程优先级
三、其它的进程概念
一、操作系统
概念
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。简单的理解,操作系统包括:
- 内核(进程管理、内存管理、文件管理、驱动管理)
- 其他程序(例如函数库、shell程序等等)
设计操作系统的目的
- 与硬件交互,管理所有的软硬件资源
- 为用户程序(应用程序)提供一个良好的执行环境
定位
在整个计算机软硬件架构中,操作系统的定位是:一款“搞管理”的软件。
如何理解“管理”
答:先描述,再组织
先描述:先用结构体确定其属性
再组织:再用链表或者其它高效的数据结构将其关联起来
层状结构图
系统调用和库函数概念
- 在开发角度,操作系统对外会表现一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
- 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度,从而形成库(c库、c++库等),有了库,就方便上层用户或者开发者进行二次开发。
二、进程
在Windows中,我们启动一个软件,本质上是启动了一个进程
而在Linux中,我们运行一条命令或者一个程序,其实就是在系统层面上创建了一个进程
概念
狭义的理解:进程是正在运行的程序的示例
广义的理解:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动
内核的观点:进程是担当分配系统资源的实体
进程 = 对应的代码和数据 + 进程对应的PCB结构体
描述进程—PCB(process control block)
PCB中存放着进程的信息,可以把PCB理解为是进程属性的集合
不同的操作系统对PCB的命名可能不同,在Linux下,PCB是被命名为task_struct的结构体
task_struct是Linux中描述进程的结构体
task_struct是Linux内核的一种数据结构,它会被装载到内存里并且包含着进程的信息
task_struct的内容划分
- 标示符:描述本进程的唯一标示符,用来区分其它进程
- 状态:任务状态,退出码,退出信号等
- 优先级:相对于其它进程的优先级
- 程序计数器:程序中即将被执行的下一条指令的地址
- 内存指针:包括程序代码和进程相关数据的指针,还有和其它进程共享的内存块的指针
- 上下文数据:进程执行时CPU寄存器中的数据
- I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表
- 记账信息:可能包括处理器时间的总和,使用的时钟数总和,时间限制,记账号等
- 其它信息
查看进程
进程的信息可以通过/proc系统文件夹查看
myproc.c
#include <stdio.h> #include <unistd.h> int main() { while(1) { printf("hello world\n"); sleep(1); } return 0; }
通过系统调用来获取进程标示符
- 进程ID(PID)
- 父进程ID(PPID)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
while(1)
{
pid_t pid = getpid();//自己进程的pid
pid_t ppid = getppid();//父进程
printf("hello world,pid: %d, ppid: %d\n", pid, ppid);
sleep(1);
}
return 0;
}
其中的这个父进程就是bash
进程状态
先来看看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运行状态:并不意味这进程一定在运行中,它表明进程要么在运行中要么在运行队列里
- S睡眠状态:意味着进程在等待事件完成(这里的睡眠有时候也叫可中断睡眠)
- D磁盘休眠状态:有时候也叫不可中断睡眠状态,在这个状态的进程通常会等待I/O结束
- T(t)停止状态:可以通过发送SIGSTOP信号给进程来停止进程。这个被暂停的进程可以通过发送SIGCONT信号让进程继续运行。
- X死亡状态(终止状态):这个状态只是一个返回状态,是一瞬间的,你不会在任务列表里看到这个状态
注:当你在查看你的进程状态时后面可能会带一个+号,表明这是一个前台进程
如果要程序在后台运行,可以在运行时加上&,如下
- Z僵尸状态:僵尸状态是一个比较特殊的状态。一个子进程已经退出,但是父进程没有读取到子进程退出的返回代码,此时子进程就会变成僵尸状态,这个进程也称为僵尸进程。僵尸进程会以终止状态保持在进程表中,并且会一直等待父进程读取退出状态码。
代码演示僵尸状态
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> int main() { pid_t id = fork(); if(id < 0) { perror("fork"); return 1; } else if(id == 0) { while(1) { printf("I am child, pid:%d, ppid:%d\n", getpid(), getppid()); sleep(3); break; } exit(0); } else { while(1) { printf("I am father, pid:%d, ppid:%d\n", getpid(), getppid()); sleep(1); } } return 0; }
僵尸进程的危害
- 造成内存资源的浪费。因为进程退出了,但是还没有被释放,因此这个进程就还需要被维持下去,而维持这个状态本身就是要用数据来维持,也属于进程的基本信息,所以保存在PCB中,Z状态一直不退出,PCB就要一直维护。
- 造成内存泄露。因为PCB是个结构体,而结构体是要在内存中开辟空间的。
孤儿进程
父进程先退出,子进程还没退出,这时子进程就称之为“孤儿进程”,孤儿进程会被1号init进程(系统本身)领养,当然也会被init进程回收。
代码演示:
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> int main() { pid_t id = fork(); if(id < 0) { perror("fork"); return 1; } else if(id == 0) { while(1) { printf("I am child, pid:%d, ppid:%d\n", getpid(), getppid()); sleep(1); } } else { while(1) { printf("I am father, pid:%d, ppid:%d\n", getpid(), getppid()); sleep(5); break; } exit(0); } return 0; }
进程优先级
CPU资源分配的先后顺序,就是指进程的优先级。优先级高的进程有优先执行的权利。
为什么要有优先级?
因为CPU是有限的,而进程太多,需要通过某种方式来竞争资源。
Linux中的优先级的做法是:
优先级 = 老的优先级 + nice值
ps -l:查看系统进程
PRI就是进程的优先级,其值越小进程的优先级别越高
NI就是nice值,其表示进程可被执行的优先级的修正数值
加入nice值之后,此时PRI = PRI(老)+ nice
当nice值为负值时,该程序的优先级值将会变小,即其优先级会变高。因此调整进程优先级在linux下就是调整进程的nice值。
nice的取值范围是-20至19,一共40个级别
更改进程的优先级
- 在命令行输入top
- 进入top后按“r”
- 输入进程的PID
- 输入nice值
设置有限的nice值而不直接改PRI的原因是:操作系统设置nice值的本质就是更好的平衡进程对系统资源的占用,如果直接改PRI可能产生一个进程长时间占用系统资源的现象。
三、其它的进程概念
独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰。
竞争性:系统进程数目众多,而CPU资源有限,所以进程之间是具有竞争属性的。
并行:多个进程在多个CPU下同时进行运行。
并发:多个进程在一个CPU下采用进程切换的方式,在一段时间内,让多个进程都得以推进。
上下文数据:一个进程在被运行时,CPU里的寄存器保存的一定是这个进程的临时数据,而这个临时数据就叫做上下文。上下文是绝对不可以被丢去的。在进程时间片到了要被切换时,要连同上下文一起打包带走。上下文可以有多份,分别对应不同的进程。
内容如有错误,还望批评指正。如果你觉得内容有用的话,就给博主一键三连吧,你的支持将会是我的动力。