Linux编程:2、进程基础知识

news2025/6/9 1:36:14

一、进程基本概念

1、进程与程序的区别
  • 程序:静态的可执行文件(如电脑中的vs2022安装程序)。
  • 进程:程序的动态执行过程(如启动后的vs2022实例),是操作系统分配资源的单位(如 CPU 时间、内存)。
  • 特点:同一程序可启动多个进程(如多个 vs2022 窗口),进程关闭后程序仍存在。
2、进程的组成
  • 进程控制块(PCB):操作系统为每个进程创建的唯一标识,包含进程状态、资源信息等。
  • 程序段(代码):进程执行的代码逻辑。
  • 数据集(数据):进程操作的数据。

二、进程控制块(PCB)

1、核心字段
  • 进程号(PID):32 位无符号整数(Linux 最大为 32767),通过getpid()获取当前进程号。
  • 进程状态
    • R:可执行状态。
    • S:可中断睡眠。
    • D:不可中断睡眠。
    • T:暂停或跟踪状态。
    • Z:僵尸进程(已退出但未释放资源)。
    • X:即将销毁的进程。
  • 查看命令ps -eo stat,pid,user,cmd(显示状态、PID、用户、命令)。
2、其他字段
  • 优先级:决定 CPU 调度顺序。
  • CPU 现场信息:保存进程暂停时的 CPU 状态,以便恢复。
  • 资源清单:内存、I/O 设备等分配情况。
  • 队列指针:链接同一状态的进程(如就绪队列、等待队列)。

三、进程 PID 文件

1、存储位置:
  • 位于/var/run目录,文件名通常为进程名.pid,内容为单行的进程号。
  • 注意:需程序自行创建,系统不会自动生成。
2、作用:
  • 防止程序重复启动(通过文件锁机制实现)。
  • 示例代码:通过fcntl加锁判断进程是否已运行。
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/file.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    // PID文件路径,用于存储当前运行实例的进程ID
    #define PID_FILE "/var/run/test.pid"
    
    /**
     * 检查程序是否已在运行,确保只有一个实例
     *
     * 返回值:
     *   0 - 程序未运行,当前是第一个实例
     *  -1 - 程序已在运行,当前实例退出
     */
    int checkAlone(void)
    {
        int  fd;       // 文件描述符
        char buf[16];  // 存储PID的缓冲区
    
        // 打开或创建PID文件,使用O_CREAT标志确保文件存在
        fd = open(PID_FILE, O_RDWR | O_CREAT, 0666);
        if (fd < 0) {
            perror("open pid failed");  // 打印错误信息
            exit(1);                    // 打开失败时终止程序
        }
    
        // 定义文件锁结构,准备加写锁
        struct flock fl;
        fl.l_type   = F_WRLCK;   // 写锁类型
        fl.l_start  = 0;         // 从文件开始位置
        fl.l_whence = SEEK_SET;  // 以文件起始为基准
        fl.l_len    = 0;         // 锁定整个文件
    
        // 尝试加非阻塞写锁
        // 返回值:0-成功,-1-失败(文件已被锁定)
        int ret = fcntl(fd, F_SETLK, &fl);
        if (ret < 0) {
            close(fd);             // 关闭文件描述符
            printf("Had run.\n");  // 提示已有实例在运行
            return -1;             // 返回错误码
        }
    
        // 将当前进程ID写入PID文件
        sprintf(buf, "%ld", (long)getpid());
        write(fd, buf, strlen(buf) + 1);
    
        printf("first running.\n");  // 提示首次运行
        return 0;                    // 返回成功
    }
    
    int main(void)
    {
        // 检查程序是否已在运行
        if (checkAlone() < 0) {
            return -1;  // 已有实例在运行,退出当前进程
        }
    
        // 主程序逻辑:循环执行任务
        while (1) {
            printf("working...\n");   // 输出工作状态
            sleep(1);                // 休眠1秒
        }
    
        return 0;
    }
 3、开启两个终端:

第一次运行程序(第一个终端):

第二次运行程序(第二个终端) :


四、进程的创建

1、fork () 函数
  • 功能:创建子进程,返回两次(父进程返回子进程 PID,子进程返回 0)。
  • 特点:子进程复制父进程的内存空间(不共享内存),继承打开的文件描述符等资源。
  • 示例1
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main()
    {
        int count = 0;  // 用于父子进程各自计数的变量
        printf("准备创建子进程...\n");
        
        // 创建子进程:fork调用会返回两次
        // 父进程返回子进程的PID(正值)
        // 子进程返回0
        // 返回负值表示创建失败
        pid_t pid = fork();
        
        // 错误处理:创建子进程失败
        if (pid < 0) {
            printf("创建子进程失败");
            exit(1);  // 终止程序并返回错误码1
        }
        
        // 子进程执行分支
        else if (pid == 0) {
            // 子进程中fork返回0
            // getpid()返回子进程自身的PID
            printf("我是子进程,pid=%d, 进程号=%d\n", pid, getpid());
            count++;  // 子进程的count加1
        }
        
        // 父进程执行分支
        else {
            // 父进程中fork返回子进程的PID
            // getpid()返回父进程自身的PID
            printf("我是父进程, pid=%d, 进程号=%d\n", pid, getpid());
            count++;  // 父进程的count加1
        }
        
        // 父子进程都会执行此语句
        // 通过判断pid值区分当前是哪个进程
        printf("我是%s, count=%d\n", pid == 0 ? "子进程" : "父进程", count);
        
        return 0;
    }

    说明1:
    由 fork 创建的新进程被称为子进程,原来的进程,称为“父进程”

    该函数被调用一次,但返回两次(在父进程中返回 1 次,子进程中返回 1 次)
    1)子进程的返回值是 0
    2)而父进程的返回值是子进程的 PID


    fork 执行完之后,子进程和父进程继续执行 fork 之后的指令。
    父进程和子进程几乎是等同的,它们具有相同的变量值(但变量内存并不共享),
    打开的文件也都相同,还有其他一些相同属性。
    如果父进程改变了变量的值,子进程将不会看到这个变化。
    实际上, 子进程是父进程的一个复制(拷贝),但它们并不共享内存。
    父进程改变了变量的值,子进程中对应的变量不会有任何影响。

  • 示例2:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main()
    {
        int count = 0;  // 父子进程各自的计数器
        printf("准备创建子进程...\n");
    
        pid_t pid;
        // 循环两次创建子进程,每次fork会产生父子两个分支
        for (int i = 0; i < 2; i++) {
            pid = fork();  // 关键系统调用:创建新进程
    
            if (pid < 0) {
                perror("fork失败");  // 输出系统错误信息
                exit(1);             // 异常退出
            }
            else if (pid == 0) {
                // 子进程分支:pid为0,getpid()返回子进程ID
                printf("[新的子进程]我是子进程,pid=%d, 进程号=%d\n", pid,
                       getpid());
                count++;  // 子进程计数器加1
            }
            else {
                // 父进程分支:pid为子进程ID,getpid()返回父进程ID
                printf("我是父进程, pid=%d, 进程号=%d\n", pid, getpid());
                count++;  // 父进程计数器加1
            }
        }
    
        // 父子进程最终都会执行此语句
        // 通过最后一次fork的返回值判断当前是父进程还是子进程
        printf("我是%s, count=%d\n", pid == 0 ? "子进程" : "父进程", count);
    
        return 0;
    }



    for 循环了 2 次,实际上创建了 3 个子进程,而不是两个。

2、exec 系列函数
  • 功能:用指定程序替换当前进程(成功后原进程代码不再执行)。
  • 接口差异
    • l:参数以列表形式传递(如execl)。
    • p:从 PATH 环境变量查找程序(如execlp)。
    • v:参数通过指针数组传递(如execv)。
    • e:传递自定义环境变量(如execle)。
  • 示例
    无参的:
     
    #include <cstdio>
    #include <unistd.h>
    
    int main(int argc, char* argv[])
    {
        // 使用execl函数执行外部程序,替换当前进程映像
        // 参数1: 要执行的程序路径
        // 参数2开始: 传递给程序的命令行参数,必须以NULL结尾
        execl("/bin/pwd",         // 指定要执行的程序路径(绝对路径)
              "pwd",              // 命令行参数列表的第一个参数,通常是程序名(可自定义)
              NULL);              // 参数列表结束标记,必须为NULL
    
    
        // 如果execl调用成功,当前进程会被完全替换,不会执行到这里
        // 如果执行到这里,说明execl调用失败
        perror("execl failed");  // 打印系统错误信息
        return 1;                // 返回错误退出码
    }



    带参的:

    #include <unistd.h>
    int main(int argc, char* argv[])
    {
        // 相当于执行: ls -l /tmp
        execl("/bin/ls", "ls", "-l", "/tmp", NULL);
        return 0;
    }

3、fork 与 exec 结合

  • 场景:父进程创建子进程后,子进程执行新程序(如 Shell 命令解析)。
  • 示例逻辑
    pid_t pid = fork();
    if (pid == 0) { execv("/path/to/program", argv); } // 子进程执行新程序

 

五、进程的分类

1、前台进程
  • 定义:需与用户交互的进程(如终端运行的程序),默认启动即为前台。
  • 查看命令ps -e | grep 进程名
2、后台进程
  • 定义:无需交互,在后台运行(如服务器程序)。
  • 启动方式:命令后加&(如./a.out &)。
  • 终止命令killall 进程名
3、守护进程
  • 定义:特殊后台进程,独立于终端(如sshdhttpd),用于长期运行任务。
  • 特点
    • 不依附终端,终端关闭后仍运行。
    • 父进程通常为systemd(PID 1)。
  • 创建步骤
    1. fork后退出父进程,子进程成为孤儿进程。
    2. setsid创建新会话,脱离原终端。
    3. chdir("/")切换工作目录至根目录。
    4. umask(0)清除文件权限掩码。
    5. 关闭默认文件描述符(0、1、2)。
  • 示例代码:通过信号处理实现日志写入的守护进程。
  • 查看命令ps axj(显示 PPID、会话信息等)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2404811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cursor Rules 使用

前言 最近在使用 Cursor 进行编程辅助时&#xff0c;发现 AI 生成的代码风格和当前的代码风格大相径庭。而且有时它会输出很奇怪的代码&#xff0c;总是不符合预期。 遂引出本篇&#xff0c;介绍一下 Rules &#xff0c;它就可以做一些规范约束之类的事情。 什么是 Cursor R…

服务器数据恢复—服务器raid5阵列崩溃如何恢复数据?

服务器数据恢复环境&故障&#xff1a; 某品牌型号为X3850服务器上有一组由14块数据盘和1块热备盘组建的raid5磁盘阵列。 服务器在正常使用过程中突然崩溃&#xff0c;管理员查看raid5阵列故障情况的时发现磁盘阵列中有2块硬盘掉线&#xff0c;但是热备盘没有启用。 服务器数…

Go语言堆内存管理

Go堆内存管理 1. Go内存模型层级结构 Golang内存管理模型与TCMalloc的设计极其相似。基本轮廓和概念也几乎相同&#xff0c;只是一些规则和流程存在差异。 2. Go内存管理的基本概念 Go内存管理的许多概念在TCMalloc中已经有了&#xff0c;含义是相同的&#xff0c;只是名字有…

【DAY41】简单CNN

内容来自浙大疏锦行python打卡训练营 浙大疏锦行 知识点&#xff1a; 数据增强卷积神经网络定义的写法batch归一化&#xff1a;调整一个批次的分布&#xff0c;常用与图像数据特征图&#xff1a;只有卷积操作输出的才叫特征图调度器&#xff1a;直接修改基础学习率 卷积操作常…

使用MinIO搭建自己的分布式文件存储

目录 引言&#xff1a; 一.什么是 MinIO &#xff1f; 二.MinIO 的安装与部署&#xff1a; 三.Spring Cloud 集成 MinIO&#xff1a; 1.前提准备&#xff1a; &#xff08;1&#xff09;安装依赖&#xff1a; &#xff08;2&#xff09;配置MinIO连接&#xff1a; &…

K7 系列各种PCIE IP核的对比

上面三个IP 有什么区别&#xff0c;什么时候用呢&#xff1f; 7 series Integrated Block for PCIE AXI Memory Mapped to PCI Express DMA subsystem for PCI Express 特点 这是 Kintex-7 内置的 硬核 PCIe 模块。部分事务层也集成在里面&#xff0c;使用标准的PCIE 基本没…

natapp 内网穿透失败

连不上网络错误调试排查详解 - NATAPP-内网穿透 基于ngrok的国内高速内网映射工具 如何将DNS服务器修改为114.114.114.114_百度知道 连不上/错误信息等问题解决汇总 - NATAPP-内网穿透 基于ngrok的国内高速内网映射工具 nslookup auth.natapp.cnping auth.natapp.cn

深入解析CI/CD开发流程

引言&#xff1a;主播最近实习的时候发现部门里面使用的是CI/CD这样的集成开发部署&#xff0c;但是自己不是太了解什么意思&#xff0c;所以就自己查了一下ci/cd相关的资料&#xff0c;整理分享了一下 一、CI/CD CI/CD是持续集成和持续交付部署的缩写&#xff0c;旨在简化并…

Docke启动Ktransformers部署Qwen3MOE模型实战与性能测试

docker运行Ktransformers部署Qwen3MOE模型实战及 性能测试 最开始拉取ktransformers:v0.3.1-AVX512版本&#xff0c;发现无论如何都启动不了大模型&#xff0c;后来发现是cpu不支持avx512指令集。 由于本地cpu不支持amx指令集&#xff0c;因此下载avx2版本镜像&#xff1a; …

应用分享 | 精准生成和时序控制!AWG在确定性三量子比特纠缠光子源中的应用

在量子技术飞速发展的今天&#xff0c;实现高效稳定的量子态操控是推动量子计算、量子通信等领域迈向实用化的关键。任意波形发生器&#xff08;AWG&#xff09;作为精准信号控制的核心设备&#xff0c;在量子实验中发挥着不可或缺的作用。丹麦哥本哈根大学的研究团队基于单个量…

相机--相机标定实操

教程 camera_calibration移动画面示例 usb_cam使用介绍和下载 标定流程 单目相机标定 我使用的是USB相机&#xff0c;所以直接使用ros的usb_cam功能包驱动相机闭关获取实时图像&#xff0c;然后用ros的camera_calibration标定相机。 1,下载usb_cam和camera_calibration: …

DAY43 复习日

浙大疏锦行-CSDN博客 kaggle找到一个图像数据集&#xff0c;用cnn网络进行训练并且用grad-cam做可视化 进阶&#xff1a;把项目拆分成多个文件 src/config.py: 用于存放项目配置&#xff0c;例如文件路径、学习率、批次大小等。 # src/config.py# Paths DATA_DIR "data…

【Auto.js例程】华为备忘录导出到其他手机

目录 问题描述方法步骤1.安装下载Visual Studio Code2.安装扩展3.找到Auto.js插件&#xff0c;并安装插件4.启动服务器5.连接手机6.撰写脚本并运行7.本文实现功能的代码8.启动手机上的换机软件 问题描述 问题背景&#xff1a;华为手机换成一加手机&#xff0c;华为备忘录无法批…

单片机的低功耗模式

什么是低功耗&#xff1f; STM32的低功耗&#xff08;low power mode&#xff09;特性是其嵌入式处理器系列的一个重要优势&#xff0c;特别适用于需要长时间运行且功耗敏感的应用场景&#xff0c;如便携式设备、物联网设备、智能家居系统等。 在很多应用场合中都对电子设备的…

架构师级考验!飞算 JavaAI 炫技赛:AI 辅助编程解决老项目难题

当十年前 Hibernate 框架的 N1 查询隐患在深夜持续困扰排查&#xff0c;当 SpringMVC 控制器中错综复杂的业务逻辑在跨语言迁移时令人抓狂&#xff0c;企业数字化进程中的百万行老系统&#xff0c;已然成为暗藏危机的 “技术债冰山”。而此刻&#xff0c;飞算科技全新发布的 Ja…

手机端抓包大麦网抢票协议:实现自动抢票与支付

&#x1f680; 手机端抓包大麦网抢票协议&#xff1a;实现自动抢票与支付 &#x1f680; &#x1f525; 你是否还在为抢不到热门演出票而烦恼&#xff1f;本文将教你如何通过抓包技术获取大麦网抢票协议&#xff0c;并编写脚本实现自动化抢票与支付&#xff01;&#x1f525; …

[TIP] Ubuntu 22.04 配置多个版本的 GCC 环境

问题背景 在 Ubuntu 22.04 中安装 VMware 虚拟机时&#xff0c;提示缺少 VMMON 和 VMNET 模块 编译这两个模块需要 GCC 的版本大于 12.3.0&#xff0c;而 Ubuntu 22.04 自带的 GCC 版本为 11.4.0 因此需要安装对应的 GCC 版本&#xff0c;但为了不影响其他程序&#xff0c;需…

如何思考?分析篇

现代人每天刷 100 条信息&#xff0c;却难静下心读 10 页书。 前言&#xff1a; 我一直把思考当作一件生活中和工作中最为重要的事情。但是我发现当我想写一篇跟思考有关的文章时&#xff0c;却难以下手。因为思考是一件非常复杂的事情&#xff0c;用文字描述十分的困难。 读书…

Redis:Hash数据类型

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Redis &#x1f525; Hash哈希 &#x1f433; ⼏乎所有的主流编程语⾔都提供了哈希&#xff08;hash&#xff09;类型&#xff0c;它们的叫法可能是哈希、字典、关联数组、映射。在Redis中&#…

快捷键的记录

下面对应的ATL数字 ATL4 显示编译输出 CTRL B 编译 CTRLR 运行exe 菜单栏 ALTF ALTE ALTB ALTD ALTH