【Linux复习】:进程概念

进程概念

冯诺依曼

核心
所有硬件都是围绕内存工作的,所有数据必须先加载到内存,CPU 才能访问。
外设:

输入设备:键盘、鼠标、硬盘(负责把数据传给内存)

输出设备:显示器、硬盘(负责从内存拿数据)

内存:临时数据中转站(速度快、断电丢失)

CPU:运算核心(只和内存交互)
IO :输入 / 输出,外设 ↔ 内存 的数据交互
数据流 :外设 → 内存 → CPU → 内存 → 外设
存储分级与IO效率:寄存器 > 缓存 > 内存(瓶颈) > 硬盘(速度递减,容量递增)

操作系统

管理软硬件

提供稳定安全的运行环境

管理

先描述

用结构体(如 task_struct)把资源的属性写清楚
再组织

用链表 / 队列把所有结构体管理起来
例子:管理进程 → 先定义 PCB 结构体 → 用链表串起所有 PCB

库函数与系统调用接口

库函数内部是封装了系统调用接口的

进程概念

原因

当程序运行时,要从硬盘加载到内存当中,操作系统为了更方便的管理每个程序的运行,就要首先描述出每一个进程的相关信息,所以有了进程控制块,而Linux中的PCB就是task_struct

描述信息

内存 mm_struct

** 文件** files_struct

(打开的文件存储在指针数组)struct file * file_arrays[]
上下文数据 CPU 寄存器数据(进程切换时保存 / 恢复)

信号

信号的阻塞位图

sigset_t blocked

信号的处理方法

struct sigaction

进程控制

进程创建

fork

复制

以父进程为模板,为子进程创建PCB,子进程和父进程共享代码 和数据写时拷贝
返回值

父进程返回子进程的pid

子进程返回0

失败返回-1

vfork

共享同一个虚拟地址空间

创建一个进程的流程

  1. 找到父进程的PCB对象,malloc一个PCB来存放子进程,用父进程的PCB初始化子进程,子进程的PCB指向父进程的代码和数据,子进程和父进程都加入调度队列开始排队等待调度
  2. 当有一方要改变数据时,就会触发写时拷贝

进程终止

终止的几种情况

正常终止,结果正确 :代码正常执行完毕,正确退出码是0
正常终止,结果不正确 :代码也正常执行结束了,但是不在合适的地点退出,退出码不为0
异常 :代码没有正常执行结束,可以使用**echo $?**来查看
exit code:当进程收到信号而退出后,可以父进程可以通过回收资源收到退出码,以查看子进程的退出情况

终止的操作对比

exit和_exit

_exit是系统调用,

exit:_exit+刷新缓冲区+关闭文件

main 的 return

return n 等价于 exit (n)

其他return :进程还在跑

进程终止系统的行为

  1. 根据被终止进程的标识,操作系统会从它的PCB集合中找到该进程的PCB ,读取该进程的状态
    2. 如果该进程处于执行状态,会立即停止执行 ,并修改状态标识,表示这个进程被终止
    3. 回收进程的资源
    4. 保留pcb 等待父进程回收
    5. 从运行队列中移除

进程等待

原因

防止内存泄漏,子进程退出,父进程不回收(waitpid),就会导致僵尸状态的出现

僵尸进程:

子进程异常终止,父进程并没有调用wait/waitpid,子进程的进程描述符等资源仍在系统中占用资源,那么此时子进程就变成了僵尸进程Z

进程等待的方式

wait :pid_t wait(int *status);
waitpid : pid_t waitpid(pid_t pid, int *status, int options);

option为0,表示阻塞等待,WNOHANG表示非阻塞等待

status是一个输出型参数,0-7是退出信号,8-15是位图

signal(退出信号)和exit code(位图)

阻塞等待和非阻塞等待

阻塞等待是停在函数调用位置等待,直到回收资源成功

非阻塞等待是,每到接口处就检测,搭配循环使用,可以做其他事

进程状态

创建,就绪,运行,阻塞,终止状态

每一种状态就是宏定义

状态变换本质就是修改宏的值,放入不同的队列

就绪状态

当进程的时间片用完之后,就会从CPU上剥离下来,此时会保存寄存器的代码和数据,存储到PCB当中,紧接着把进程的PCB加入到就绪队列当中,等待下一次调度运行

挂起状态

当进程在阻塞状态时,如果内存资源不足 ,会暂时把阻塞进程的代码和数据置换到磁盘的swap分区中 ,当进程被调度的时候再把数据加载进来
转换流程

僵尸进程

产生原因

子进程终止,父进程不回收子进程资源
危害

子进程的数据没有被回收,就会内存泄漏
避免

  1. 进程等待
    主动调用接口,waitpid进行阻塞或非阻塞等待

  2. SIGCHLD
    子进程结束后,会向父进程发送一个信号,父进程可以对于该信号进行自定义设置方案,即可处理

    signal(SIGCHLD, SIG_IGN);

父进程忽略 SIGCHLD

内核就会自动回收子进程,不再留僵尸

父进程不用写任何 wait /waitpid

孤儿进程

产生原因

子进程未终止,父进程先终止了
处理

这是无害的,bash(init 进程(pid=1))会领养孤儿进程,由bash进程进行回收资源

守护进程

  1. 后台运行
    不接受任何终端的输入,关掉终端也不退出

  2. 脱离控制
    自己创建一个会话,成为会话组长进行管理自己

  3. 独立生命周期
    用孤儿进程的方式由bash领养,避免父进程影响

  4. 总结
    连续2次fork进行变成孤儿进程,再自己创建一个会话管理自己

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <fcntl.h>

    void daemonize() {
    // 1. 第一次 fork,父进程退出
    if (fork() != 0)
    exit(0);

    复制代码
     // 2. 子进程创建新会话,脱离终端
     setsid();
    
     // 3. 第二次 fork,防止重新打开终端
     if (fork() != 0)
         exit(0);
    
     // 4. 忽略子进程退出信号,避免僵尸
     signal(SIGCHLD, SIG_IGN);
    
     // 5. 重定向标准输入输出到空设备
     int fd = open("/dev/null", O_RDWR);
     dup2(fd, 0);
     dup2(fd, 1);
     dup2(fd, 2);
     close(fd);

    }

    int main() {
    // 变成守护进程
    daemonize();

    复制代码
     // 后台死循环运行
     while (1) {
         sleep(1);
     }
    
     return 0;

    }

环境变量

概念

用来指定操作系统运行环境的一些参数

指明动静态库的位置
PATH

指定命令的搜索途径
HOME

指定用户的主工作目录
SHELL

当前shell,一般是bash

相关命令

echo $PATH -- 查看环境变量

export 变量名=val --新增一个环境变量

env 显示所有环境变量

unset 变量名;清除环境变量

set 显示所有变量(包括本地变量 + 环境变量)
特性

  1. main函数参数的第三个参数

    int main(int argc, char* argv[], char* envp[])

  2. 环境变量信息是以脚本配置文件形式存在的
    每次登陆从 ./bash_profile 当中读取内容创建表

  3. 本地变量不能被子进程继承,环境变量可以
    4.环境表本质上是一个字符指针数组,以NULL结尾

命令

常规命令

fork子进程让子进程执行该命令
内建命令

shell命令行的函数,直接读取环境变量
代码中获取设置环境变量

*getenv(const char name)

获取环境变量

**setenv(const char name, const char value, int overwrite)

设置 / 修改环境变量

*putenv(char string)

传入 name=value 格式字符串设置环境变量

虚拟地址空间

是什么

就是操作系统内核里的一个结构体:mm_struct

为什么

  1. 提高内存利用率
    不用一次性加载全部代码 / 数据
    按需分配、swap 换入换出
    解决内存碎片问题
  2. 增加内存访问控制与安全
    不能直接访问物理内存
    通过页表权限控制(只读、可读写、可执行)
    越界访问会触发段错误,保护内核和其他进程
  3. 保证进程独立性
    每个进程都以为自己独占内存
    进程之间地址空间完全隔离
    一个进程崩溃不会影响其他进程

怎么做

  1. 分段式内存管理
    程序由若干个逻辑分段组成,代码分段,数据分段,栈段,堆段等,不同的段有不同的属性,用分段的形式可以把段分离开
  2. 分页式内存管理
    产生连续的内存空间,把整个虚拟地址和物理内存空间划分为固定尺寸的大小,每一页是4KB
    通过页表映射:虚拟页号 → 物理页号
  3. 段页式内存管理
    先划分成逻辑意义的段,也就是分段机制
    再把每一个段划分成多个固定大小的页
    段号,段内页号,页内偏移定位信息

进程替换

  1. 是什么?
    进程替换 = 把当前进程的代码和数据全部换掉,换成另一个程序
    不创建新进程
    PID 不变
    只是换程序内容
  2. 执行成功会发生什么?
    用新程序的代码段、数据段覆盖当前进程
    原来的代码直接作废
    从新程序的 main 函数开始执行
    不会回到原来的程序!
    成功没有返回值
  3. 执行失败
    返回 -1
    继续执行原来的代码

系统调用接口

l → list:参数以列表形式给出(逐个写)

v → vector:参数以数组形式给出

p → path:自动去 PATH 里找命令

e → environment:自己传环境变量

逐个说明

  1. execl
    按列表传参
    需要写全路径

    execl("/usr/bin/ls", "ls", "-l", NULL);

  2. execlp
    p = 自动找 PATH
    不用写全路径

    execlp("ls", "ls", "-l", NULL);

  3. execle
    e = 自己传环境变量

    execle("ls", "ls", "-l", NULL, envp);

  4. execv
    v = 用数组传参

    char* argv[] = {"ls", "-l", NULL};
    execv("/usr/bin/ls", argv);

  5. execvp
    v + p

    char* argv[] = {"ls", "-l", NULL};
    execvp("ls", argv);

  6. execvpe
    v + p + e

    execvpe("ls", argv, envp);

MySQL shell

连接数据库

执行 SQL

管理数据库

复制代码
mysql -uroot -p
相关推荐
Par@ish2 小时前
【局域网协议】通过ISE实现radius认证,AD 的TCP 3268端口在其中发挥什么作用
服务器·网络·tcp/ip
RisunJan2 小时前
Linux命令-named-checkzone
linux·前端
REDcker2 小时前
Linux ss 命令详解与 Netlink 原理
linux·运维·服务器
爱吃菠萝121382 小时前
实验室服务器远程访问终极方案:SSH 反向隧道 + systemd 自动重连
运维·服务器·ssh
小杰3122 小时前
ZLMediakit源码梳理
服务器·音视频·流媒体·zlmediakit
知秋贺2 小时前
如何在ubuntu24中,使用docker 运行ros2 humble版本
运维·docker·容器
Jocelyn_书3 小时前
Jenkins使用手册
运维·jenkins
吹晚风吧3 小时前
解决vite打包,base配置前缀,nginx的dist包找不到资源
服务器·前端·nginx
pingao1413783 小时前
智慧井盖传感器震动倾斜溢水监测:传统市政设施的智慧化升级典范
运维·网络·安全