我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系 。

组成部分
为什么要有内存?
1.从技术的角度:
2.从成本角度
操作系统包括:
操作系统可以看成是管理软件的软件。

为什么管理描述进程要有-PCB(process ctrl block)?
进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
课本上称之为PCB(process control block),Linux操作系统下的PCB是task_struct 。(因为Linux内核是用C语言写的,所以Linux管理内核就使用一个结构体存储一个进程的信息,以便管理)
查看进程
ls /proc
也可以使用psc
ps ajx

task_ struct内容分类
为什么父进程的父进程不变?
所有进程的最终父进程是bash进程,几乎我们在命令行上所执行的所有的指令(cmd),都是bash进程的子进程。
为什么fork(),子进程会返回0,而父进程会返回子进程的pid?
子进程的task_struct对象,内部的数据基本上都是从父进程拷贝而来的。fork()之后,父子进程的代码共享,但是一般都通过不同的返回值,让不同的进程执行不同的代码。【而数据是各自独立】
如何理解进程被运行?
理解调度器和调度队列(runqueue)
操作系统层面的进程的状态status

系统为什么要维护一个终止态?
Linux的进程状态state
task_struct中使用task_state_array[]数组存放状态变量
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 */[重点]
};

孤儿进程:父进程先死,子进程还没有被杀掉。子进程会被1号进程领养。而1号进程就是【操作系统】。
进程的大部分时间都在等待申请资源
int main()
{
printf("begin.....\n");
while(1)
{
}
return 0;
}

int main()
{
printf("begin.....\n");
while(1)
{
printf("hello world\n");
}
return 0;
}
ps ajx | head -1 && ps ajx | grep hello

表明进程大部分的时间都在申请资源。
优先级是进程获取资源的先后顺序。
资源不够。系统里面永远都是,进程占大多数,而资源是少数。
ps -la

相关概念
PRI和NI
**用top命令更改已存在进程的nice **
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发 。
单道程序设计:一次只有一个处于程序在运行中;其他程序处于等待状态。(DOS)
多道程序设计:设计出时间片,在一个时间片中执行一个程序,在下一个时间片立马然后切换为下一个程序,从而让出cpu的资源给其他程序。
由于一个时间片的时间很短,属于是毫秒级别的。所以在人的感知上,几个程序是并发进行的;但是在微观上,在一个时间片上,只有一个程序在运行。微观上串行,宏观上并行

进程的切换是依靠调度器。
操作系统,就是简单的根据队列进行先后调度吗?有没有可能突然来了一个优先级更高的进程?
依靠hash表和位图
task_struct* queue[140];
根据不同的优先级,将特点的进程放入相应的队列中。
比如优先级为2的进程,放入第二个task_struct queue中。

维护方式:
当新增一个进程时(进程的优先级为n),将改进程放入queue[n]队列的末尾。这种方式也维护了进程的优先级,即高优先级的先运行。
进程切换
CPU内的寄存器,可以临时的存储数据
int hello()
{
int a=10+20;
return a;
}
int b=hello();
/*
当返回值较小时
a是临时变量,在返回时被销毁,系统会把a对应的值放入寄存器eax中;
然后寄存器eax再将值mov给b对应的内存
*/
进程进行切换的时候,进程需要被CPU剥离。


PATH : 指定命令的搜索路径 [重点]
HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)[重点]
SHELL : 当前Shell,它的值通常是/bin/bash
查看环境变量
env $NAME //NAME:你的环境变量名称
1. echo: 显示某个环境变量值[重点]
2. export: 设置一个新的环境变量[重点]
用法:export 本地变量名
3. env: 显示所有环境变量[重点]
4. unset: 清除环境变量
5. set: 显示本地定义的shell变量和环境变量
可执行程序的搜索路径是保存在一个全局变量中,PATH,给系统提高命令的搜索路径。是环境变量中的一个
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
#每个目录中间使用冒号隔开。
#PATH的搜索顺序:从左向右依次进行程序的搜索,找到就执行程序,找不到就找下一个目录。如果全部都没有,就报错:command not found!
#也就是执行路径下的可执行文件可以直接使用(可以不指出相应的路径)
向PATH中添加路径
#方法一:
$ PATH="${PATH}:/root"
#在该用户下的环境变量中添加路径/root
#方法二:
$ export PATH=$PATH:/root
#在该用户下的PATH环境变量中添加路径/root
命令行变量
main函数的参数
int main(int argc,char* argv[])
{
/*
argc:参数个数
argv:指针数组,并且第一个字符串指向的是这个可执行程序的名字,最后一个字符串指向的是NULL
*/
}
**实现命令行计算器 **
#include
#include
#include
int main(int agrc,char* argv[])
{
if(argc!=4)
{
printf("Useage:[-a][-s][-m][-d]\n");
}
int x=atoi(argv[1]);
int y=atoi(argv[2]);
if(strcmp("-a",argv[3])==0)
{
printf("%d-%d=%d\n",x,y,x-y);
}
else if(strcmp("-s",argv[3])==0)
{
printf("%d+%d=%d\n",x,y,x+y);
}
else if(strcmp("-m",argv[3])==0)
{
printf("%d*%d=%d\n",x,y,x*y);
}
else if(strcmp("-d",argv[3])==0)
{
printf("%d/%d=%d\n",x,y,x/y);
}
return 0;
}

遍历环境变量
#include
int main(int argc, char *argv[], char *env[])
{
int i = 0;
for(; env[i]; i++){
printf("%s\n", env[i]);
}
return 0;
}

#include
int main(int argc, char *argv[])
{
extern char **environ;
int i = 0;
for(; environ[i]; i++){
printf("%s\n", environ[i]);
}
return 0;
}
#include
int main()
{
printf("%s\n", getenv("PATH"));
return 0;
}
实现一个只能被本用户调用的程序
int main()
{
char * var=getenv("USER");
if(strcasecmp(var,"west")!=0)
{
printf("没有权限...........\n");
}
else
{
printf("执行成功.........\n");
}
return 0;
}
一个函数声明的时候没有带参数,可以传参吗?
int fun()
{
printf("hello world\n");
}
int main()
{
fun(10,20,30,40);
return 0;
}
答案是可以传递参数。但是不能写成 fun(void),这就表明函数一定不能传递参数。

每一个进程启动,操作系统都会给进程创建一个地址空间,每一个进程都有自己的进程地址空间。
操作系统为了管理这些进程地址空间,有数据结构mm_struct进行管理【管理该进程的进程地址空间】。

进程的独立性:体现在进程相关的数据结构是独立的,代码和数据是独立的。
**在32位机器下,一个进程可以管理的虚拟内存空间大小为4G。**而实际上的物理空间大小不是4G。而物理内存和虚拟内存自己的对应和管理就是通过MMU和地址转换记录表进行。

比如在.data中存放了a=10,那么就可以通过MMU(记录了虚拟内存和物理内存的映射关系),将他的虚拟地址转换为物理地址,从而访问内容10。
内存访问级别:
0是最高的级别,内核访问的权限
3是允许用户访问权限级别的权限
映射问题:
用户空间映射到物理空间内存是独立的(不同的进程,即使虚拟地址相同,对应的也是不同的物理空间)
内核空间映射到物理空间内存是相同的。(上图中,两个进程的kernal指向了同一块权限为0的MMU)
页表------将进程地址空间和物理地址映射【左边是虚拟地址,右边是物理地址】
页表项组成。
相对地址/逻辑地址/进程地址空间 --------> 映射物理地址
int val=100;
int main()
{
pid_t pid=fork();
if(pid==0)
{
int i=5;
while(i)
{
printf("I am father pro,pid=%d,val=%d",getpid(),val);
sleep(1);
i--;
}
val=200;
printf("begin chage................\n");
while(1)
{
printf("I am child pro=%d,val=%d\n",getpid(),val);
sleep(1);
}
}
else
{
while(1)
{
printf("I am father pro,pid=%d,val=%d\n",getpid(),val);
sleep(1);
}
}
return 0;
}

为什么进程地址一样,但是内容确不一样。
写时拷贝原则:

实现了进程的独立性
为什么fork()有两个返回值?
pid_t pid是属于父进程空间定义的变量,return返回的时候,pid发生了写时拷贝,所以有两个返回值。
