冯诺依曼体系结构

答:冯·诺依曼结构是一种计算机体系结构,它将程序指令和数据存储在同一存储器中,以及通过共享通用总线实现指令和数据之间的传输。这种结构为我们理解计算机如何进行数据流提供了基础。
假设你登录QQ并开始与朋友聊天,我们来看一下数据的流动过程:
-
发送文字消息:
- 当你打开QQ窗口并输入要发送的消息时,文本消息会被编码为数据包。
- 这些数据包首先会通过你的计算机上的网络适配器发送到本地网络路由器。
- 路由器将数据包发送到互联网,并通过一系列路由器和服务器最终到达QQ服务器。
- QQ服务器接收到消息后,将其存储在数据库中并标记为未读。
- 你的朋友登录QQ时,客户端会从服务器请求未读消息,服务器将消息发送给他的客户端。
- 朋友的客户端接收到消息后,将其解码并显示在聊天窗口中。
-
发送文件:
- 当你想发送文件时,文件数据会被分割成数据包,并按照类似文本消息的方式发送到服务器。
- QQ服务器接收到文件数据包后,会将其存储起来,并生成一个下载链接。
- 朋友在接收到文件后,可以通过下载链接从服务器上下载文件数据。
- 文件数据通过类似的网络传输方式从服务器传输到朋友的计算机上。
- 朋友的QQ客户端接收到文件数据后,将其组装还原成完整的文件,并保存在本地设备中。
总的来说,在与朋友聊天过程中,数据的流动涉及到数据的编码、传输、存储、解码等多个环节,其中涉及到网络传输、服务器存储以及客户端的数据处理等步骤。这些步骤都是建立在冯·诺依曼计算机体系结构基础上的。
操作系统(Operator System)

进程
基本概念
描述进程-PCB
进程=PCB+自己的代码和数据
PCB指的是进程控制块(Process Control Block),也称为进程描述符(Process Descriptor),是操作系统中用于管理和维护进程信息的数据结构。每个进程在操作系统中都会有一个对应的 PCB,操作系统利用 PCB 来跟踪和控制进程的状态、位置以及所需的资源。
PCB 包含了描述进程当前状态和执行情况的各种信息,通常包括以下内容:
- 进程状态:表示进程当前的状态,如运行、就绪、阻塞等。
- 程序计数器(Program Counter):指向下一条将要执行的指令地址。
- 寄存器:保存进程的寄存器状态,包括通用寄存器、程序状态字等。
- 进程标识符:唯一标识进程的ID。
- 进程优先级:用于确定进程的调度顺序。
- 进程所拥有的资源:如打开的文件、分配的内存等。
- 进程的父子关系:指向父进程和子进程的指针等。
为什么要有PCB? 通过 PCB,操作系统可以实现进程的创建、调度、切换、挂起、终止等操作,并确保进程之间的安全隔离和资源分配。PCB 是操作系统中非常重要的数据结构,对于操作系统的正常运行和进程管理至关重要。
task_struct-PCB的一种
task_ struct内容分类

组织进程
查看进程

ps ajxx
ps ajx | head -1
ps ajx | grep myprocess
ps ajx | head -1 && ps ajx | grep myprocess
拿到第一行和与 myprocess有关的行
while :; do ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep;sleep 1;done
通过系统调用获取进程标示符


小知识:ctrl+c是在用户层面终止进程,kill -9 进程id 可以用来直接杀掉进程
通过系统调用创建进程-fork初识
在Unix/Linux系统中,fork()是一个创建新进程的系统调用。调用fork()函数后,操作系统会复制当前进程,创建一个新的子进程。原始进程被称为父进程,新创建的进程被称为子进程。
在调用fork()后,父子进程将拥有相同的代码段、数据段和堆栈,但是它们拥有各自独立的地址空间。子进程是父进程的副本,它从父进程那里继承了大部分状态,包括打开的文件描述符、信号处理器等。
fork()函数返回两次,成功时,在父进程中返回子进程的进程ID(PID),在子进程中返回0。失败时,在父进程中返回-1,不创建子进程,并且适当设置errno。
通常情况下,fork() 被用于创建一个新的进程来执行不同的任务,或者实现并发处理。父子进程之间可以通过进程间通信(如管道、消息队列、共享内存等)来进行数据交换和协作。

进程状态
看看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 */
};
小知识:信号列表
进程状态查看
ps aux / ps axj 命令
Z(zombie)-僵尸进程
#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;
}
僵尸进程危害
进程状态总结
我们以前启动的所有进程,怎么从来没有关心过僵尸呢?内存泄漏?
因为直接在命令行启动的进程,他的父进程是bash,bash会自动回收新进程的Z
(shell:媒婆;bash:王婆;sh:李婆。它们是介于用户和操作系统之间的软件:命令行解释器)
孤儿进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 1;
}
else if(id == 0){//child
printf("I am child, pid : %d\n", getpid());
sleep(10);
}else{//parent
printf("I am parent, pid: %d\n", getpid());
sleep(3);
exit(0);
}
return 0;
}
进程优先级
基本概念
查看系统进程

PRI and NI
PRI vs NI
查看进程优先级的命令
环境变量
基本概念
环境变量是操作系统中用来指定操作系统运行环境的一些参数,如临时文件夹位置和系统文件夹位置等。环境变量由变量名和文件路径组成,可以设置很多个环境变量。环境变量的作用是让系统和应用程序能够在不知道文件完整路径的情况下,通过变量名查找文件或程序。例如,path环境变量指定命令的搜索路径,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找此程序外,还应到path中指定的路径去找。
常见环境变量
查看环境变量方法
echo $NAME //NAME:你的环境变量名称
环境变量默认是在系统对应的配置文件中,我们登陆Linux系统时,环境变量才被加载到bash进程中(内存)
一些配置文件:.bash_profile .bashrc /etc/bashrc
测试PATH
#include <stdio.h>
int main()
{
printf("hello world!\n");
return 0;
}
PATH=$PATH:hello 程序所在路径
测试HOME


和环境变量相关的命令

环境变量的组织方式
通过代码如何获取环境变量
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
int i = 0;
for(; env[i]; i++){
printf("%s\n", env[i]);
}
return 0;
}
方法二:通过第三方变量environ获取
#include <stdio.h>
int main(int argc, char *argv[])
{
extern char **environ;
int i = 0;
for(; environ[i]; i++){
printf("env[%d]->%s\n",i, environ[i]);
}
return 0;
}

通过系统调用获取或设置环境变量

#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("%s\n", getenv("PATH"));
return 0;
}
环境变量通常是具有全局属性的
#include <stdio.h>
#include <stdlib.h>
int main()
{
char * env = getenv("MYENV");
if(env){
printf("%s\n", env);
}
return 0;
}
export MYENV="hello world"
环境变量可以被子进程继承下去的原因在于操作系统的进程创建机制。当一个新的进程被创建时,通常会继承父进程的环境,包括环境变量。这种设计有以下几个重要原因:
-
方便性和效率:继承环境变量可以节省时间和资源,因为子进程无需重新获取和设置环境变量,而是直接继承了父进程的环境。
-
一致性:子进程继承父进程的环境变量可以确保它们在同一环境中执行,从而避免了配置不一致带来的问题。
-
灵活性:父进程可以通过设置环境变量来影响子进程的行为,实现父子进程之间的数据传递和通信。
总的来说,环境变量的继承使得父子进程之间能够共享配置信息,提高了系统的灵活性和效率。这种机制在操作系统中被广泛应用,为进程间通信和协作提供了便利。
程序地址空间
研究背景
程序地址空间回顾

来段代码感受一下
#include <stdio.h>#include <unistd.h>#include <stdlib.h>int g_val = 0;int main(){pid_t id = fork();if(id < 0){perror("fork");return 0;}else if(id == 0){ //childprintf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);}else{ //parentprintf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);}sleep(1);return 0;}
// 与环境相关,观察现象即可parent[2995]: 0 : 0x80497d8child[2996]: 0 : 0x80497d8
#include <stdio.h>#include <unistd.h>#include <stdlib.h>int g_val = 0;int main(){pid_t id = fork();if(id < 0){perror("fork");return 0;}else if(id == 0){ //child, 子进程肯定先跑完,也就是子进程先修改,完成之后,父进程再读取g_val=100;printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);}else{ //parentsleep(3);printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);}sleep(1);return 0;}
输出结果:
// 与环境相关,观察现象即可child[3046]: 100 : 0x80497e8parent[3045]: 0 : 0x80497e8
进程地址空间

Linux2.6内核进程调度队列-选学
Linux系统中每一个CPU都有一个运行队列