认识操作系统
任何计算机都包含一个基本的程序集合,称为操作系统。
操作系统包括
- 内核(进程管理,内存管理,文件管理,驱动管理)
- 其他程序
操作系统是一个单纯负责管理的软件
一般来说,管理者与被管理对象不直接沟通,管理者通过信息管理被管理对象。
管理方法:先将管理对象描述起来,然后在将管理对象组织起来
进程
进程概念:程序的一个执行实例。
从内核角度看: 进程是担当分配系统资源(CPU时间,内存)的实体。
进程与程序的区别:
程序:硬件上的一个文件
程序:将程序运行加载到内存上,且操作系统分配了PCB
PCB
进程控制块(process control block),放置进程信息的数据结构,可以理解为进程属性的集合。Linux下的PCB是task_struct。
操作系统 通过管理PCB来管理进程
task_struct
Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体被装载到RAM(内存)里并且包含了一个进程所需要的所有信息。它定义在include/linux/sched.h
文件中,task_struct结构体,可以说是Linux内核源码中最复杂的一个结构体,成员非常多,占用内存也很大。
task_struct内容分类
- 标识符:描述进程的唯一标识符,用来区别其他进程
- 状态:任务状态,退出代码,退出信号等
- 优先级:相对于其他进程的优先级
- 程序计数器:程序中即将被执行的下一条指令的地址
[程序计数器也叫作 PC指针,是CPU内部的一个寄存器,它内部存储的是当前正在执行指令的下一条指令的地址,引导CPU执行下一条指令(CPU在周而复始的读取指令,分析指令,执行指令)] - 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
- 上下文数据:进程执行时处理器的寄存器中的数据(体现进程调度)
- I/O状态信息:包括显示的I/O请求,分配给进程的I/O 设备和被进程使用的文件列表
- 记账信息:包括处理器时间总和,使用时间总和,时间限制,记账号等。
- 其他信息
并发与并行
- 在一个CPU下,同一时间段内,多个进程通过切换让多个进程得以推进,叫做并发
- 多CPU下,多个进程任何时刻同时推进,叫做并行
所有运行在系统中的进程都以task_struct链表的形式存在内核里
查看进程:
1.进程可以通过/proc
系统文件夹查看
2.大多数进程信息也可以通过top和ps这些用户级工具来获取。
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1){
printf("I am process!\n")
sleep(1);
}
return 0;
}
(进程在分配PID的时候,理论上在短期内是不会变化的,但是不代表永远不重复)
通过系统调用获取进程标识符
- 进程id(PID)
- 父进程id(PPID)
#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有两个返回值
执行以下代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
fork();
printf("i am process pid: %d\n",getpid());
return 0;
}
fork()之后,执行流由一个变成了两个
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int ret=fork();
printf("i am process pid: %d ppid:%d ret: %d\n",getpid(), getppid(), ret);
return 0;
}
由运行结果可知:fork的两个返回值,给父进程返回子进程的pid,给子进程返回0,因为父进程对子进程是一对多的
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)
{
printf("i am child pid: %d ret: %d\n",getpid(),ret);
}
else{
printf("i am father pid: %d ret:%d \n",getpid(),ret);
}
sleep(1);
return 0;
}
父子进程的运行先后是不确定的,这由操作系统决定