系统编程之进程详述

系统编程之进程的概念

1. 冯诺依曼体系结构
二进制: 所有数据都是按照二进制的方式进行存储
内存存储: 数据都是交给存储器设备进行设备
中央处理器(CPU), 存储器(内存) , 输入设备(键盘, 网卡) , 输出设备(屏幕, 网卡)
2. 操作系统
操作系统是一个软件 , 与硬件交互,管理所有的软硬件资源 , 为用户程序(应用程序)提供一个良好的执行环境
操作系统 = 操作系统内核 (保存操作系统管理计算机资源的代码, 用来管理计算机软硬件资源) + 一组应用
操作系统通过描述 + 组织的方式来管理操作系统的软硬件资源
硬件设备 + 驱动程序(鼠标驱动) + 操作系统内核 + 系统调用 + 库函数 + 用户
管理 = 描述(struct) + 组织(使用某种数据结构, struct->struct->struct)

描述: 一班
组织
	一班: 张三
	一班: 李四
个人信息:
typedef struct UserInfo{
	char name[20];
	char school[20];
	char class[20];
	char address[20];
}USERINFO;  

man 1 [xxx] : 查看命令
man 2 [xxx] : 查看系统调用: 操作系统内核封装给程序员调用的接口 ( 比如 open(…), …)
man 3 [xxx] : 查看库函数: 操作系统内核提供的某些接口比较生涩难用, 所以一堆大佬子啊系统调用的基础上, 再次进行封装, 提供了一系列的接口供程序员调用 (比如 sleep(int seconds) …)

3. 进程概念

3.1 程序和进程的区别

  • 程序: 本质上就是一个普通文件, 通常认为编译完成之后产生的可执行的文件, 称之为程序, 程序是静态的;
  • 进程: 进程是程序执行起来的一种表现形式, 内核观点 : 进程是操作系统分配资源(程序的地址空间) 的最小单位;

3.2. 操作系统是如何来管理进程

描述:

linux操作系统是使用进程控制块来描述进程的:

  • 进程控制块:

本质上是一个结构体, struct task_struct{…}; 别名: PCB(process control block), 进程信息被放在进程控制块中,可以理解为进程属性的集合。

struct task_struct在/usr/src/kernels/3.10.0957.el7.x86_64/include/linux/sched.中可找到

  • task_struct:

进程标识符: 别名进程号/进程pid,用来标识一个进程; 在同一个操作系统当中pid是不能重复的; ps aux可以查看当前操作系统当中的进程信息; pa aux | grep [xxx]

 USER   PID  %CPU  %MEM  STAT  START  TIME  COMMAND
  y2   43640  0.0   0.0   S+   18:00  0:00  ./test
    USER:  该进程属于哪一个用户;
    PID: 进程号
    STAT: 当前进程的状态
    START: 进程启动时间
    TIME: 占用cpu时长
    COMMAND: 启动进程的命令

当一个程序启动时, 操作系统内核会维护一个task_struct这样的结构体, 并且在以进程pid命名的进程文件夹(在/proc/[pid]文件夹下)保存与当前进程相关的文件, /proc/[pid]/fd这个文件夹里是当前进程打开的文件信息, 称为文件句柄, 打开一个文件(fopen)如果没有关闭(fclose), 就会导致内存泄漏( 实质为文件句柄泄漏 )
与进程相关系的/proc/[pid]文件夹, 当进程运行时, 会存在; 当进程退出时, 就会被操作系统所清理

  • 前台进程&后台进程:

在命令行中启动一个程序的时候, 不能输入命令, 输入无效的情况下, 称之为前台进程,
结束方式前台进程方式: ctrl + c (ctrl + c也可以结束当前命令的输入)
后台进程:
fg 将后台进程放到前台来运行, 再执行 ctrl + c结束前台进程
kill 命令结束一个进程(前台进程 & 后台进程)
kill [pid] 可以结束前台或后台的进程 并不能杀死暂停状态下的进程 需为kill -9[pid]

  • 进程状态:

阻塞状态: 等待IO, 进行数据输入, 如果一直没有准备好数据, 当前数据一直处于阻塞状态
运行状态: 进程拥有CPU, 再进行运算,
就绪状态: 在就绪队列当中等待CPU资源的进程状态称之为就绪状态

R(Running): 运行状态, 并不意味着进程一定在运行中(1. 拿着CPU进行计算, 2, 在就绪队列当中)
S(sleeping): 可中断睡眠状态(意味着进程在等待事件完成)
D(Disk sleep): 磁盘睡眠状态 死了等复活(不可以被打断)在这个状态的进程通常会等待IO的结束。
T(stopped): 暂停状态 
	crtl+Z: 使得一个程序变成暂停状态, 
	kill [pid]: 并不能杀死在暂停状态的进程, 
	kill -9 [pid]: 杀死一个暂停状态的进程(强杀)
t(trace stop): 跟踪状态 gdb调试时会产生t状态,
X: 死亡状态 程序员看不到, 在进程退出的时候才能看到死亡状态, 也就是内核释放进程时会调这个状态, task_struct当中存在
Z(Zombie): 僵尸状态 
"+": 代表当前进程是前台进程

用于进程切换:
程序计数器: 保存进程即将要执行的下一条指令, 调试时在 disassemble 命令中可以看到
4核8C: 4个物理CPU 8个逻辑CPU
多个进程之间对于CPU而言是抢占式执行 (茅坑)
第一种: 并发执行 多个进程使用一个CPU, 每个进程使用一小会 换其他
第二种: 并行 多个进程每一个进程都占一个CPU进行运算, 并行的运行
上下文数据: 保存上次执行的时候, 寄存器当中的值(用来恢复(获得)寄存器的值)

内存指针: 指向进程地址空间,
IO信息&记账信息
当前进程启动的时间
当前进程占用CPU的时间
进程在占用CPU计算时, 时间是微秒, 纳秒计算
优先级:

  • cpu资源分配的先后顺序,就是指进程的优先权,
  • 进程的优先级(PRI)就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
  • 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
  • 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。
  • nice值表示进程可被执行的优先级的修正数值, PRI(new) = PRI(old) + NI(nice值) 影响new的PR值(更改进程的优先级–> 优先调动权), 当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行 ; 调整进程优先级,在Linux下,就是调整进程nice值, 但是会不会影响由操作系统决定, (修改优先级, 进入top后按“r”–>输入进程PID–>输入nice值)

组织: 内核使用双向链表来组织进程中的task_struct的信息, 每一个结构体task_struct都是双向链表(内核)的一个元素
4 . 如何在代码中创建一个进程?

  • 系统调用: pid_t getpid(void) 获取当前进程的pid接口 (进程标示符)
  • 系统调用: int fork(); 用来创建进程

返回值:

  • 成功返回子进程pid(大于0) --> 这个分支是给父进程准备的,大于0的数, 是子进程的PCB,(子进程的名字就相当于pid,返还给父进程)
  • 成功返回0 --> 这个分支是给子进程准备的
  • 创建失败返回小于0的数
    首先创建了父进程(有自己的PCB…) fork()接口就是以父进程为模板拷贝成为了子进程(也是需要执行代码的)
#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){ //child
		printf("I am child : %d!, ret: %d\n", getpid(), ret);
	}
	else{ //father
		printf("I am father : %d!, ret: %d\n", getpid(), ret);
	}
 	sleep(1);
	return 0;
}

如果fork创建子进程(调用)成功, 则会返回两次值,一次返回大于0(子进程PCB). 一次等于0.
代码编写的直观意义: 可以使用分支语句去区分父子进程执行不同的逻辑;
fork返回0的时候是进入到父进程的逻辑当中, 父进程并不清楚自己到底创建了多少子进程, 管理不了子进程, 将子进程的PID返回给父进程,是为了更好的管理子进程

子进程和父进程的关系

  1. 代码共享 : 由于子进程拷贝了父进程的PCB , 换句话说, 也就是将PCB当中内存指针指向的程序地址空间当中的代码段也拷贝了. 所以父子进程对于代码段是一模一样的, 我们可以通过fork的返回值,来让父子进程执行不同的逻辑
  2. 数据独有 : 由于父子进程各有自己的程序地址空间, 所以在各自的虚拟地址空间上的数据互不干扰,
这两个都是查看进程信息 只是显示内容的多少不一样
    ps aux|head -n 1
    pa -ef|head -n 1

注: 子进程的pid不一定等于父进程的pid+1

4.1 僵尸状态:
由于子进程退出, 但是父进程来不及回收子进程的退出资源, 导致子进程变成僵尸状态
究其根本: 由于子进程的PCB并没有被内核所释放, 释放这样的PCB需要父进程来进行回收, 使用ps aux | grep [xxx] + ps命令可以查看进程信息的时候, 发现子进程变成了僵尸状态(Z)

  • 僵尸进程的危害:
    该进程被强杀都不能强杀掉, 还泄漏了操作系统的资源, 由于僵尸进程的PCB并没有被释放掉 —> task_struct(结构体)

4.2 孤儿进程:
父进程先于子进程退出, 子进程被1号进程所领养, (为了当子进程退出的时候, 回收子进程的退出信息, ) 孤儿进程不是一种进程状态, 而是一种进程种类的名称
1号进程: 操作系统启动的第一个进程,被称为Init进程,1号进程会在子进程退出的时候, 回收子进程的退出信息(好多进程都是被1号进程所创建的), 防止子进程变成僵尸进程
进程是有僵尸状态的, 但是没有孤儿状态

僵尸进程如何回收:
1. 杀死僵尸进程的父进程(父进程被杀掉, 相当于僵尸进程变成了孤儿进程, 孤儿进程又会被1号进程所回收, 就没有僵尸进程了)
僵尸进程是死的 用kill命令杀不死, 但是孤儿进程是活的 ,可以用kill -9 pid杀死(孤儿杀养父, 完了后就全部死光了, )
2. 后面会有进程等待 也会解决僵尸进程的问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值