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

第26天进程(一)

目录

进程概念

查看进程

进程的特点(理解)

进程的资源分配(理解)

进程的状态(记住)

进程管理--PID

进程间关系(重点)

函数名:getpid()

函数名:getppid()

创建进程

函数名:fork() 

 函数名:vfork()

结束进程

函数名:_exit()

函数名:exit()

特殊进程

等待进程

函数名:wait()

函数名:waitpid()


进程概念

运行状态下的(硬盘中存储)程序(静态)----(内存中运行)进程(动态)

查看进程

Windows 任务管理器

2.2.2 Linux 终端指令

top #动态监控
ps -ef #显示系统中所有进程的详细信息

进程的特点(理解)

动态性:进程的实质是一次程序运行的过程,进程是动态产生、动态消亡的。

并发性:任何进程都可以和其他进程一起并发执行

独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的基本单位

异步性:进程的执行具有间断性,多个进程间按照各自独立的不可预知的速度向下运行

进程的资源分配(理解)

系统会给每个进程开辟 4G 的虚拟内存( =3G 用户内存+1G 的内核内存)

----当物理内存中的数据需要参与时,便加载到4G的虚拟内存中

  • 操作系统在为进程分配内存空间时,将其划分为堆、栈、数据段、未初始化数据段和文本段这五大区域。以达到基于内存管理和程序运行时数据组织有效划分的目的。

堆:手动开辟

栈:局部变量

数据段:全局变量和静态变量

                未初始化数据段:默认值为 0

                初始化数据段:会初始化设置的初始值

文本段:常量和代码

进程的状态(记住)

执行态:进程正在占用 CPU(CPU 正在执行该进程)

就绪态:进程已经满足本 CPU 分配时间片的条件(可以被 CPU 执行),在等待 CPU 给他分配时间片

等待态:进程没有满足被 CPU 分配时间片的条件,需要等到条件满足 CPU 才会给它分配时间片

进程管理--PID

一般情况下都是通过进程的 PID 号来操作进程

进程间关系(重点)

父子关系:进程 2 是由进程 1 创建的,那么进程 1 就是进程 2 的父进程,进程 2 就是进程 1 的子进程。 进程间有且只有父子关系

函数名:getpid()

头文件:

#include <sys/types.h>

#include <unistd.h>

函数原型:pid_t getpid(void);

函数功能:获取进程 pid

函数参数:无

函数返回值:返回 PID 号

函数使用:

#include <sys/types.h>
#include <unistd.h>
int main(){
    pid_t pid = getpid();
    printf("pid=%d\n",pid);
    sleep(10);//拉长时间,验证
    return 0;
}

 查找进程,验证。可以使用

ps -ef | grep "./a.out"

函数名:getppid()

头文件:

#include <sys/types.h>

#include <unistd.h>

函数原型:pid_t getppid(void);

函数功能:获取父进程 pid

函数参数:无

函数返回值:获取的 PPID

函数使用:

#include <sys/types.h>
#include <unistd.h>
int main(){
    pid_t ppid = getppid();
    printf("ppid=%d\n",ppid);
    sleep(10);//拉长时间,验证
    return 0;
}

创建进程

方法一:直接运行可执行程序

方法二:在程序中去调用进程创建函数去创建进程--fork、vfork。

函数名:fork() 

子进程复制父进程资源,然后和父进程从同一位置向下运行,执行的先后关系不确定

头文件:

#include<sys/types.h>

#include<unistd.h>

函数原型:pid_t fork(void);

函数功能:创建子进程

函数参数:无

函数返回值:-1 代表子进程创建失败,0 代表子进程,大于 0 代表父进程

函数使用:

#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
int main(){
    pid_t pid =0;
    pid= fork();
    if(pid<0){
        perror("fork");
        return 0;
    }
    else if(pid==0){
        printf("pid=%d\n",getpid());
        printf("ppid=%d\n",getppid());
    }else{

        printf("this pid=%d\n",getpid());
    }
    return 0;
}

 函数名:vfork()

子进程占用父进程资源,然后子进程先使用资源执行,子进程结束后,父进程才能使用资源

头文件:

#include<sys/types.h>

#include<unistd.h>

函数原型:pid_t vfork(void);

函数功能:创建子进程,返回子进程 PID

函数参数:无

函数返回值:父进程返回子进程 PID 子进程返回 0

函数使用:

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

int main(){
    pid_t pid;  // 定义进程 ID 变量

    pid = vfork();  // 创建子进程,并获取子进程的进程 ID

    if(pid<0){  // 如果创建子进程失败
        perror("vfork");  // 打印错误信息
        return 1;  // 程序返回 1 并结束
    }else if(pid==0){  // 如果是子进程
        printf("pid=%d ",pid);  // 打印子进程的进程 ID
        printf("子进程先执行\n");  // 输出提示信息
        sleep(2);  // 子进程暂停 2 秒
        _exit(0);  // 子进程退出
    }else{  // 如果是父进程
        printf("pid=%d ",pid);  // 打印子进程的进程 ID
        printf("父进程等待子进程结束\n");  // 输出提示信息
        sleep(4);  // 父进程暂停 4 秒
    }
    return 0;  // 程序正常结束返回 0
}

当 vfork 被调用时,确实创建了一个新的子进程。在父进程中,pid 存储了新创建子进程的进程 ID(非 0 值),然后执行父进程中对应的代码块(第三个代码块)。

而在新创建的子进程中,pid 的值为 0 ,子进程执行第二个代码块。

需要注意的是,vfork 会让子进程先运行,并且子进程要尽快使用 exec 系列函数或 _exit 函数,,因为子进程和父进程共享地址空间。

结束进程

方法一:外部信号

在终端 ctrl+c 可以结束进程

在终端输入:kill -9 PID 可以结束进程

方法二:内部程序

1. main()中调用 return

2. 进程退出函数---exit()、_exit()

函数名:_exit()

头文件:#include<unistd.h>

函数原型:void _exit(int status);

函数功能:退出进程,不清空缓冲区

函数参数:获取进程退出时的状态 一般填 0

函数返回值:无

函数使用:

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

void write_to_buffer() {
    printf("This is written to the buffer.");
}
int main() {
    write_to_buffer();
    _exit(0);
    return 0;
}

函数名:exit()

头文件:#include

函数原型:void exit(int status);

函数功能:退出进程,清空缓冲区

函数参数:获取进程退出时的状态 一般填 0

函数返回值:无

函数使用:

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
void _EXIT_();
int main(){
    pid_t pid =0;
    pid= fork();
    if(pid<0){
        perror("fork");
        return 0;
    }
    else if(pid==0){
         sleep(3);
        printf("B");
       _EXIT_();
       printf("5678\n");
        
    }else{
         sleep(8);
        printf("A");
        exit(0);
    }
    return 0;
}
void _EXIT_(){
    exit(0);
    //return ;//5678
    printf("1234\n");
}

特殊进程


 0 号进程:操作系统启动的引导程序 祖先进程

1 号进程:操作系统启动的第一个程序


  • 孤儿进程: 父进程死了,子进程还在运行。孤儿进程会被系统中设置好的一个进程收养,并且等孤儿进程 死了之后给它收尸
  • 僵尸进程: 子进程死了,父进程没有给自己成收尸。这个时候子进程就会变成一个僵尸进程,僵尸进程所 占用的资源基本上会被完全释放,除了它所占用的进程号。父进程在退出之前,一定会给所有的僵尸进程收 尸。但是有些工程中的父进程需要长时间不停机运行,会导致僵尸进程的资源无法得到及时的回收处理。所以 咱们在编写代码的过程中,一定要尽量避免僵尸进程的产生。 可以通过在父进程中调用,下面的进程的等待函数来避免僵尸进程的产生。

等待进程

函数名:wait()

头文件:

#include<sys/types.h> 

#include<sys/wait.h>

函数原型:pid_t wait(int *wstatus);

函数功能:阻塞等待任意子进程结束

函数参数:进程结束状态 一般填 NULL

函数返回值:返回结束的子进程 pid

函数使用:

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

int main() {
    pid_t pid;
    pid = fork();  // 创建子进程
    if (pid < 0) {  // 子进程创建失败
        perror("Fork failed");
        return 1;
    } else if (pid == 0) {  // 子进程
        printf("I'm the child process!\n");
        sleep(3);  // 子进程暂停 3 秒
        _exit(0);  // 子进程退出
    } else {  // 父进程
        int status;
        pid_t child_pid = wait(&status);  // 父进程等待子进程结束
        if (child_pid == -1) {  // 等待失败
            perror("Wait failed");
            return 1;
        }
        printf("Child process %d exited. Status: %d\n", child_pid, status);
    }
    return 0;
}

这时候,我有一个问题:fork+wait=vfork?

答案:不对,fork + wait 并不等同于 vfork 。

fork 会创建一个新进程,子进程拥有父进程的完整副本,包括数据空间、堆、栈等。父进程和子进程可以并发执行。

vfork 创建的子进程会直接使用父进程的地址空间,并且子进程必须尽快调用 exec 系列函数或者 _exit 函数来替换当前进程的地址空间或者退出,否则可能会导致错误。

wait 函数用于父进程等待子进程结束并获取子进程的状态。

vfork 虽然也是创建子进程,但它与 fork 的行为有很大的不同,不能简单地认为 fork 加上 wait 就等于 vfork 。

函数名:waitpid()

头文件:

#include<sys/types.h> 

#include<sys/wait.h>

函数原型:pid_t waitpid(pid_t pid, int *wstatus, int options);

函数功能:等待子进程结束

函数参数:

  • pid

    • 负数:表示等待进程组 ID 等于该负数绝对值的任意子进程。
    • 0:等待当前进程组中的任意子进程。
    • 正数:等待指定进程 ID 的子进程。
  • wstatus:这是一个整数指针,用于接收子进程的退出状态信息。通过对这个状态值的解析,可以获取子进程退出的原因、是否正常退出等详细信息。通常,如果您不关心具体的状态细节,可以将其设置为 NULL

  • options:这是一个位掩码,用于控制 waitpid 的行为。

    • WNOHANG:表示非阻塞等待。如果没有子进程结束,函数会立即返回 0 ,而不会阻塞。
    • 0:表示阻塞等待,直到指定的子进程结束。

函数返回值:

  • 如果成功等待到指定子进程结束,返回结束子进程的进程 ID 。
  • 如果设置了 WNOHANG 选项且没有子进程结束,返回 0 。
  • 如果出现错误,返回 -1 ,同时会设置 errno 来指示错误类型。

函数使用:

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

int main() {
    pid_t pid;
    pid = fork();  // 创建子进程
    if (pid < 0) {  // 子进程创建失败
        perror("Fork failed");
        return 1;
    } else if (pid == 0) {  // 子进程
        printf("I'm the child process!\n");
        sleep(3);  // 子进程暂停 3 秒
        _exit(0);  // 子进程退出
    } else {  // 父进程
        int status;

        // 阻塞等待指定子进程结束
        pid_t result = waitpid(pid, &status, 0);  
        if (result == -1) {
            perror("Waitpid failed");
            return 1;
        }
        printf("Child process %d exited. Status: %d\n", pid, status);

        // 非阻塞等待当前进程组中的任意子进程结束
        result = waitpid(-1, &status, WNOHANG);  
        if (result > 0) {
            printf("Another child process exited. PID: %d, Status: %d\n", result, status);
        } else if (result == 0) {
            printf("No child process exited yet.\n");
        } else {
            perror("Waitpid (WNOHANG) failed");
            return 1;
        }
    }
    return 0;
}

http://www.kler.cn/a/399266.html

相关文章:

  • 华东师范大学数学分析第五版PDF习题答案上册及下册
  • 黑马智慧商城项目学习笔记
  • Oracle 19c PDB克隆后出现Warning: PDB altered with errors受限模式处理
  • 红外遥控信号解码
  • 【Mysql】Mysql的多表查询---多表联合查询(上)
  • 【项目开发】理解SSL延迟:为何HTTPS比HTTP慢?
  • 创建型设计模式与面向接口编程
  • w040基于web的社区医院信息平台
  • 【MYSQL】锁详解(全局锁、表级锁、行级锁)【快速理解】
  • STL关联式容器介绍
  • 预处理(1)(手绘)
  • 【Axure原型分享】轮播表格_开始暂停效果
  • 基于语法树的SQL自动改写工具开发系列(2)-使用PYTHON进行简单SQL改写的开发实战
  • LeetCode题解:18.四数之和【Python题解超详细】,三数之和 vs. 四数之和
  • redis类型介绍
  • docker .vhdx文件压缩
  • Linux性能优化之火焰图简介
  • Failed to create a temp file - Jenkins 无法创建任务
  • SpringCloud篇(服务保护 - Sentinel)
  • [C++] 惯用法
  • 【windows笔记】04-windows下设置端口转发规则(局域网组网实用)
  • 优选算法 - 5 ( 栈 队列 + 宽搜 优先级队列 9000 字详解 )
  • Windows下 TortoiseGit 的使用
  • Python绘制雪花
  • 2.STM32之通信接口《精讲》之USART通信
  • 执行flink sql连接clickhouse库