当前位置: 首页 > news >正文

Linux进程与进程控制学习总结

一、核心概念
  1. 进程定义

    • 进程是程序的一次执行实例,是资源分配和调度的基本单位。
    • 特性:动态性、并发性、独立性、异步性、结构性。
  2. 进程控制块(PCB)

    • 操作系统通过task_struct结构体(PCB)管理进程,包含:
      • 进程标识(PID、PPID)
      • 状态信息(运行、就绪、阻塞等)
      • 资源信息(内存、打开的文件、信号量等)
  3. 进程标识

    • PID(进程ID):唯一标识进程的正整数。
    • PPID(父进程ID):创建当前进程的父进程PID。
  4. 进程状态

    状态描述
    运行正在占用CPU执行指令
    就绪已获得除CPU外的所有资源,等待调度
    阻塞因等待事件(如I/O完成)暂停执行
    终止进程执行完毕或被强制终止
  5. 进程与程序的区别

    • 程序:静态的代码文件(如a.out)。
    • 进程:动态的程序执行实例,包含执行上下文和资源。

二、进程结构
  1. 内存布局

    • 代码段:存储可执行指令。
    • 数据段:存储全局变量和静态变量。
    • 堆段:动态分配的内存(如malloc)。
    • 栈段:存储局部变量和函数调用信息。
  2. 进程模式

    • 用户模式:执行用户程序,权限受限。
    • 内核模式:执行系统调用或中断处理,可操作特权指令。

三、进程操作函数
1. 创建进程(fork
#include <unistd.h>
pid_t fork(void);  // 返回值:子进程返回0,父进程返回子进程PID,失败返回-1

示例:创建子进程并区分父子进程逻辑

#include <stdio.h>
#include <unistd.h>

int main() {
    pid_t pid = fork();
    if (pid == 0) {
        printf("子进程PID: %d\n", getpid());  // 子进程逻辑
    } else if (pid > 0) {
        printf("父进程PID: %d, 子进程PID: %d\n", getpid(), pid);  // 父进程逻辑
    } else {
        perror("fork失败");
    }
    return 0;
}

输出

父进程PID: 1234, 子进程PID: 1235
子进程PID: 1235
2. 进程退出(exit vs _exit
  • exit:清理缓冲区后退出(如刷新stdio流)。
  • _exit:直接终止进程,不清理缓冲区。
#include <stdlib.h>
void exit(int status);        // 标准退出函数
#include <unistd.h>
void _exit(int status);       // 直接终止进程
3. 进程替换(exec函数族)
#include <unistd.h>
int execl(const char *path, const char *arg, ... /* NULL */);  // 路径+参数列表
int execv(const char *path, char *const argv[]);               // 路径+参数数组
int execlp(const char *file, const char *arg, ...);            // 文件名+自动搜索PATH

示例:使用execlp执行ls命令

#include <unistd.h>

int main() {
    execlp("ls", "ls", "-l", NULL);  // 替换当前进程为ls -l
    perror("exec失败");              // 若exec失败才会执行
    return 1;
}
4. 进程等待(wait
#include <sys/wait.h>
pid_t wait(int *status);  // 等待任意子进程结束,返回子进程PID

示例:父进程等待子进程结束

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>

int main() {
    pid_t pid = fork();
    if (pid == 0) {
        printf("子进程运行\n");
        sleep(2);
        _exit(0);
    } else {
        wait(NULL);  // 阻塞等待子进程结束
        printf("子进程已终止\n");
    }
    return 0;
}

输出

子进程运行
(2秒后)
子进程已终止

四、综合例题

题目:使用forkexec实现一个简单的Shell,执行用户输入的命令。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    char command[256];
    while (1) {
        printf("> ");
        fgets(command, sizeof(command), stdin);
        command[strcspn(command, "\n")] = '\0';  // 去除换行符

        pid_t pid = fork();
        if (pid == 0) {  // 子进程执行命令
            execlp(command, command, NULL);
            perror("命令执行失败");
            _exit(1);
        } else if (pid > 0) {  // 父进程等待子进程结束
            wait(NULL);
        } else {
            perror("fork失败");
        }
    }
    return 0;
}

输出示例

> ls
file1.txt  file2.txt
> pwd
/home/user

五、常见问题
  1. fork后的执行顺序:父子进程执行顺序由调度器决定,无法预知。
  2. 僵尸进程:子进程终止后未被父进程wait回收,需通过信号处理或显式回收避免。
  3. exec函数族的选择
    • execlp/execvp:自动搜索PATH环境变量。
    • execle/execve:可指定自定义环境变量。

总结

  1. 进程管理核心:通过fork创建进程,exec替换进程映像,wait同步进程。
  2. 关键区别
    • exit_exit:前者清理缓冲区,后者直接终止。
    • fork返回值:父进程返回子进程PID,子进程返回0。
  3. 应用场景
    • 多任务处理(如Web服务器)。
    • 进程间通信(需结合管道、信号等机制)。
    • 实现Shell或任务调度器。

通过掌握进程控制函数,能够编写高效的多进程程序,充分利用系统资源。

相关文章:

  • Node.js系列(6)--安全实践指南
  • JVM常用概念之压缩引用
  • 《深度学习》——YOLOv2详解
  • 3651翻转后1的数量
  • 矩阵指数的定义和基本性质
  • c#-单例模式
  • 集群环境下Redis 商品库存系统设计
  • Qt6.8.2中JavaScript调用WebAssembly的js文件<3>
  • 【JavaEE】Mybatis基础使用注解 增删改查操作
  • 【Redis】Redis中的热点key问题如何解决?
  • react 技术栈请问该如何优化 DOM 大小
  • 查看 Windows Server 2022 中容器功能是否开启
  • 贴墙砖之前要往墙面上刷胶,为什么要刷?能不能不刷?
  • LabVIEW界面布局优化
  • C++ 仿函数
  • 【Linux系列】实时监控磁盘空间:`watch -n 1 ‘df -h‘` 命令详解
  • spring boot maven一栏引入本地包
  • 雅可比行列式
  • 实用工具-Stirling-PDF
  • 数据结构 ——单链表
  • 北京网站案例/营销软文广告
  • 湘潭房产网站建设/北京网站建设公司哪家好