Linux 进程概念

一.进程的基本概念

  • 课本概念: 进程是程序的一个执行实例, 指的是正在执行的程序。
  • 内核视角: 进程是担当分配系统资源的实体。

1.1 什么是进程PCB?

进程PCB(Process control block)是一个存储进程属性的集合。

1.1.1 Linux系统中的进程PCB

进程PCB在Linux操作系统中为task_struct, 它是Linux内核中的一种数据结构, 在程序启动时, 内核会在内存中开辟一块空间, 用于存储该进程的信息。

1.1.2 Linux系统中进程PCB的内容分类
  • 标识符: 描述进程的唯一标识符, 用于区分不同的进程。
  • 状态: 任务状态, 退出码, 退出信号等。
  • 进程优先级: 相对其他进程的优先级, 决定了执行的顺序。
  • 程序计数器: 程序中即将被执行的下一条指令的地址。
  • 内存指针: 存放了程序代码和进程相关数据的指针, 还有其他进程共享的内存块的指针。
  • 上下文数据: 进程执行时处理器的寄存器中的数据。(以便下一次继续运行)
  • I/O状态信息: 包括了I/O请求, 以及分配给I/O设备和进程使用的文件列表。
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和, 时间限制, 记账号等。
  • 以及其他信息

二.查看进程

2.1 进程的信息

进程的信息可以通过 /proc 系统文件夹查看:

2.2 进程的查看指令

bash 复制代码
ps [选项]

-e 或 --select [exp]: 显示所有进程。

-f 或 --forest: 使用树状格式显示进程及其子进程。

-l 或 --long: 显示长格式输出。

-a 或 --all: 显示所有终端上的进程,不包括会话组领导进程。

-x 或 --extended: 显示没有控制终端的进程。

-u 或 --user [user]: 显示指定用户的进程。

-p 或 --pid [pid]: 显示指定PID的进程。

-C 或 --name [command]: 显示指定名称的进程。

-o 或 --format [format]: 自定义输出格式。

2.3 系统调用获取进程标识符

getpid(): 获取当前进程ID
getppid(): 获取父进程ID

cpp 复制代码
#include <iostream>
#include <sys/types.h>
#include <unistd.h>

int main(){
    std::cout << "pid: " << getpid() << std::endl;
    std::cout << "ppid: " << getppid() << std::endl;
    return 0;
}

三.创建的进程

fork(): 创建一个子进程

cpp 复制代码
#include <iostream>
#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 : %d!, ret: %d\n", getpid(), ret);
    }
    else{
        printf("I am father : %d!, ret: %d\n", getpid(), ret);
    }
    return 0;
}

fork有两个返回值, 父进程返回子进程的ID, 子进程返回0, 父子进程代码共享, 数据各自开辟空间, 私有一份。(采用写时拷贝)

四.进程的状态

进程在运行时有许多种不同的状态, 有的进程可能正在运行, 有的进程可能被阻塞, 有的进程可能已经结束, 所以进程会有许多种不同的状态。

c 复制代码
/*
* 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 */
};
  • R 运行状态(running): 并不意味着进程一定在运行中, 它表明进程要么是在运行中要么在运行队列里。
  • S 睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。
  • D 磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep), 在这个状态的进程通常会等待IO的结束。
  • T 停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
  • X 死亡状态(dead): 这个状态只是一个返回状态, 你不会在任务列表里看到这个状态。
  • Z 将死状态(zombie): 这个状态是一个比较特殊的状态, 当进程退出并且父进程没有读取到子进程退出的返回代码时就会进入此状态。僵尸进程会以终止状态保持在进程表中, 并且会一直在等待父进程读取退出状态代码。所以, 只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。

4.1 僵尸进程的危害

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.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;
}

父进程创建了很多子进程,一直不回收,就会造成内存资源的浪费。因为数据结构对象本身就要占用内存!

4.2 孤儿进程

父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?

  • 父进程先退出,子进程就称之为"孤儿进程"。
  • 孤儿进程被1号init进程领养,由init进程回收。
cpp 复制代码
#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;
}

4.3 进程优先级

查看系统进程: ps -l

  • UID : 代表执行者的身份
  • PID : 代表这个进程的代号
  • PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
  • PRI :代表这个进程可被执行的优先级,其值越小越早被执行
  • NI :代表这个进程的nice值

既然有了PRI, 那么NI值又是什么呢?

其表示进程可被执行的优先级的修正数值: PRI(new)=PRI(old)+nice

五.环境变量

5.1 环境变量的基本概念

  • 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。

如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。

常见的环境变量:

PATH : 指定命令的搜索路径

HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)

SHELL : 当前Shell,它的值通常是/bin/bash。

5.2 查看环境变量的方法

bash 复制代码
echo $NAME //NAME:你的环境变量名称
  • 每个程序都会收到一张环境变量表,环境变量表是一个字符指针数组,每个指针指向一个以'\0'结尾的环境字符串

    通过代码如何获取环境变量:
  1. 命令行第三个参数
cpp 复制代码
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
    for(int i = 0; env[i]; i++){
        printf("%s\n", env[i]);
    }
    return 0;
}
  1. 通过第三方变量environ获取
cpp 复制代码
#include <stdio.h>
int main(int argc, char *argv[])
{
    extern char **environ;
    for(int i = 0; environ[i]; i++){
        printf("%s\n", environ[i]);
    }
    return 0;
}
  1. 通过系统调用获取或设置环境变量
cpp 复制代码
#include <cstdio>
#include <cstdlib>
int main()
{
    printf("%s\n", getenv("PATH"));
    return 0;
}

感谢大家观看,不妨点赞支持一下吧喵~

如有错误,随时纠正喵~

相关推荐
嵌入式小能手几秒前
Linux C接口编程入门之文件I/O
linux·c语言
程思扬4 分钟前
用ChatGPT做数据分析与挖掘,爽!
运维·经验分享·docker·chatgpt·数据挖掘·数据分析·前端框架
我的运维人生14 分钟前
Apache服务器深度解析与实践应用:构建高效Web服务的基石
服务器·前端·apache·运维开发·技术共享
别NULL25 分钟前
毕业设计项目——基于RISC-V的标签化跨层调度应用任务管理(论文/代码)
linux·课程设计·risc-v
4647的码农历程27 分钟前
Linux网络编程 -- 网络套接字预备与udp
linux·服务器·网络
Jet-W1 小时前
Docker基础命令
运维·docker·容器
职场人参1 小时前
视频声音怎么去除?高效的视频声音去除方法
java·服务器·数据库
旦沐已成舟1 小时前
网站集群批量管理-Ansible(ad-hoc)
运维·centos·ansible
pyliumy1 小时前
ansible 剧本模式
linux·运维·ansible
2401_857026231 小时前
医疗革新:Spring Boot医院管理系统
服务器·数据库·spring boot