操作系统之进程控制

news2024/9/30 1:33:12

进程

  • 一、进程创建
    • 1、进程
    • 2、fork函数
      • (1)概念
      • (2)创建进程执行到内核中的fork代码时,内核做的操作
      • (3)返回值
    • 3、常规用法
    • 4、代码
    • 5、执行结果
  • 二、进程终止
    • 1、进程的退出结果
    • 2、常见退出方法
      • (1)正常终止
      • (2)异常退出
    • 3、exit和_exit函数
      • (1)函数
      • (2)区别
    • 4、代码
    • 5、运行结果
  • 三、进程等待
    • 1、作用
    • 2、方法
      • (1)wait和waitpid
      • (2)参数和返回值
        • 【1】pid
        • 【2】options
        • 【3】返回值
      • (3)status
        • 【1】概念
        • 【2】分布
        • 【3】两个宏
    • 3、代码
    • 4、运行结果
    • 5、使用wait和waitpid函数的原因
  • 四、进程程序替换
    • 1、概念
    • 2、替换原理
    • 3、exec系列函数
    • 4、代码
    • 5、运行结果

一、进程创建

1、进程

  • 进程由内核数据结构和进程的代码和数据构成,内核数据结构由操作系统提供,进程的代码和数据一般从磁盘中加载,也就是程序加载之后的结果。
  • 创建子进程时,代码一般是只能读取而不能被修改(写)的,所以父子进程可以共享代码;但数据可能会被修改,所以,会采用写时拷贝的方法,在修改的时候对父子进程的数据做分离操作。

2、fork函数

(1)概念

通过复制调用该函数的进程来创建新进程,新创建的流程称为调用该函数的进程的子进程,是调用该函数的进程的精确副本。
在这里插入图片描述

(2)创建进程执行到内核中的fork代码时,内核做的操作

  • 分配新的内存块和内核数据结构给子进程。
  • 将父进程部分数据结构内容拷贝至子进程。
  • 将子进程添加到系统进程列表当中。
  • fork函数返回,调度器开始调度。

(3)返回值

  • 如果创建进程成功,子进程将会返回0,而父进程返回的是子进程的pid。
  • 如果创建进程失败,则在父进程中返回-1,不会创建子进程,并正确地设置errno。

3、常规用法

  • 父进程通过复制自己来创建子进程,父子进程在同一份代码中,同时执行不同的代码段。
  • 父进程通过复制自己来创建子进程,父进程正常执行后续代码,子进程执行另一个不同的程序。

4、代码

#include<stdio.h>                                                                                             
#include<unistd.h>
                      
int main()      
{   
   pid_t id = fork();
   if(id < 0)
   {                                                               
       perror("fork");
       return 1;
   }
   else if(id == 0)
   {
       printf("child, pid = %d, ppid = %d\n", getpid(), getppid());
   }
   else
   {
       printf("father, pid = %d, ppid = %d\n", getpid(), getppid());
       sleep(2);
   }
    return 0;
}  

5、执行结果

在这里插入图片描述

二、进程终止

1、进程的退出结果

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 代码异常终止

2、常见退出方法

(1)正常终止

  • main函数返回
  • 调用exit函数
  • 调用_exit函数

(2)异常退出

  • 程序执行时输入ctrl + c
  • 信号终止

3、exit和_exit函数

(1)函数

在这里插入图片描述
在这里插入图片描述

  • status定义了进程的终止状态。

(2)区别

在这里插入图片描述

  • _exit函数是系统调用,而exit函数是库函数,即exit函数会调用_exit函数,只不过exit函数在调用前会执行一些操作。

4、代码

int sum(int top)
{
    int sum = 0;
    int i = 1;
    for(i = 1;i <= top; ++i)
    {
        sum += i;
    }
    //exit(1);

    return sum;
}                                                                                                             

int main()
{
    printf("hello snowdragon1\n");
    printf("hello snowdragon1\n");
    printf("hello snowdragon1\n");

    int ret = sum(100);
    printf("%d\n", ret);
    
    printf("hello snowdragon2\n");
    printf("hello snowdragon3\n");
    printf("hello snowdragon4\n");
    
    exit(1);
                                                                                                              
    printf("hello snowdragon5\n");
    printf("hello snowdragon6\n");
    printf("hello snowdragon7\n");
    
    return 0;
}

5、运行结果

在这里插入图片描述

  • 下方为删除sum函数中exit函数的注释后的运行结果

在这里插入图片描述

三、进程等待

1、作用

  • 当子进程退出后,如果父进程不管它,就可能造成僵尸进程问题,进而造成内存泄漏。而进程一旦变成僵尸状态,我们就无法通过信号之类的方法去杀死它,因为无法杀死一个已经死去的进程。
  • 我们需要知道父进程派给子进程的任务完成的怎样,而当父进程进行进程等待时,如果子进程正常退出,父进程就能得知该类信息,获取子进程的退出信息。
  • 父进程可以通过进程等待的方式,回收子进程资源。

2、方法

(1)wait和waitpid

在这里插入图片描述

(2)参数和返回值

【1】pid
  • 当pid为-1时,表示等待任意一个子进程。此时,当waitpid函数的options为0时,其与wait函数的作用相同。
  • 当pid大于0时,表示等待进程id与pid相等的子进程。
【2】options
  • 当options为0时,表示父进程阻塞等待子进程。父进程一般是在内核中阻塞,等待被唤醒。
  • 当options为WNOHANG时,若pid指定的子进程没有结束(退出),则waitpid函数返回0,不予以等待。若pid指定的子进程正常结束,则返回该子进程的id。
【3】返回值
  • 当正常返回时,返回收集到的子进程的进程id。
  • 如果waitpid函数设置了选项options为WNOHANG,而调用中waitpid函数发现没有已退出的子进程可收集,则返回0。
  • 如果调用时出错,则返回-1,这时errno会被设置成相应的值以指示错误所在。

(3)status

【1】概念

status参数是一个输出型参数,由操作系统填充。如果传递给它的实参为NULL,表示不关心子进程的退出状态信息。否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。

【2】分布

在这里插入图片描述

【3】两个宏
  • WIFEXITED(status):表示子进程返回的状态,若子进程为正常终止,则为真,否则为假。
  • WEXITSTATUS(status):若WIFEXITED非零(为真),则提取子进程的退出码。

3、代码

int main()
{
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return 1;
    }
    else if(id == 0)
    {
        int cnt = 5;
        while(cnt--)                                                                                          
        {
            printf("子进程%d, pid = %d, ppid = %d\n", cnt, getpid(), getppid());
            sleep(1);
        }
    }
    else
    {
        printf("父进程,pid = %d, ppid = %d\n", getpid(), getppid());
        int status = 0;
        waitpid(id, &status, 0);
        
        //printf("等待子进程成功:%d, 子进程退出信号编号:%d,子进程退出码: %d\n", id, status & 0x7f, (status >> 8) & 0xff);
        printf("等待子进程成功:%d, 子进程退出信号编号:%d,子进程退出码: %d\n", id, WIFEXITED(status), WEXITSTATUS(status));
    }

    return 0;
}

4、运行结果

在这里插入图片描述

5、使用wait和waitpid函数的原因

  • 进程具有独立性,父子进程之间的数据会发生写时拷贝,如果采用全局变量之类的方法,父进程将无法拿到子进程的退出结果(status)。
  • 僵尸进程至少会保留该进程的PCB信息,即task_struct里面会保留所属进程退出时的退出结果信息,而父进程调用wait和waitpid函数的本质是读取子进程的task_struct结构中两个保存退出信息的变量。
  • 虽然进程具有独立性,而进程退出码是进程的数据。但是,wait和waitpid函数是系统调用。所以,父进程调用wait和waitpid函数后所进行的操作是由操作系统执行的。

四、进程程序替换

1、概念

  • 程序替换是通过特定的接口,加载磁盘上的一个全新的程序(代码和数据)到调用进程的地址空间中。
  • 进程将磁盘上新的程序加载到内存后,会和该进程的页表重新建立映射关系。

2、替换原理

  • 用fork创建子进程后,子进程可以调用exec系列函数的一种以执行另一个程序。
  • 当进程调用exec系列函数的一种时,该进程用户空间中的代码和数据完全被新程序替换,从新程序的启动例程开始执行。
  • 调用exec系列函数并不会创建新的进程,所以进程在调用exec系列函数前后,该进程的id并不会改变。

3、exec系列函数

在这里插入图片描述
在这里插入图片描述

  • 这些函数如果调用成功则加载新的程序,从启动代码开始执行,不会有返回值。
  • 如果调用出错则返回-1,所以,exec系列函数只有调用出错时才会有返回值。
  • 第二张的execve函数是系统调用。即第一张图片的六个函数最终都会调用execve函数。

4、代码

    1 #include<stdio.h>                                                                                           
    2 #include<unistd.h>
    3 #include<sys/types.h>
    4 #include<sys/wait.h>
    5 #include<stdlib.h>
    6 
    7 #define NUM 32
    8 #define SIZE 16
    9 
   10 int main()
   11 {
   12     char* _argv[NUM] = {
   13         (char*)"ls",
   14         (char*)"-l",
   15         (char*)"-a",
   16         NULL
   17     };
   18     char* const _envp[SIZE] = {              //_envp的元素是char*类型的,_envp可以加const修饰,也可以不加
   19         (char*) "snowdragon=writed 666", //字符串=前面不要加空格,否则它的名字就变了,即需要加空格才是变量名
   20         NULL
   21     };
   22     pid_t id = fork();
   23     if(id == 0)
   24     {
   25         //子进程
   26         printf("子进程,pid:%d\n", getpid());
   27         sleep(1);
   28 
   29         //_envp是用来存储环境变量的,而不是存储选项的。选项不要加-
   30         execle("./mycmd", "./mycmd", "a", NULL, _envp);
   31         //execlp("ls", "ls", "-l", "-a", NULL);
   32         //execl("/usr/bin/ls", "ls", "-l", "-a", NULL);
   33         //execv("/usr/bin/ls", _argv);
   34         //execvp("ls", _argv);
   35         exit(1);
   36     }
   37     else                                                                                                    
   38     {
   39         printf("父进程,pid:%d\n", getpid());
   40         int status = 0;
   41         pid_t res = waitpid(id, &status, 0);
   42         if(res > 0)
   43         {
   44             printf("等待子进程成功,退出码:%d\n", WEXITSTATUS(status));
   45         }                                                                                                   
   46     }
   47     return 0;
   48 }
  1 #include<stdio.h>
  2 #include<stdlib.h>                                                                                            
  3 #include<string.h>
  4 
  5 int main(int argc, char* argv[])        //argv是指针数组
  6 {
  7     if(argc != 2)
  8     {
  9         printf("%d\n", argc);
 10         printf("程序执行失败!\n");
 11         exit(1);
 12     }
 13 
 14     printf("snowdragon:%s\n", getenv("snowdragon"));        //环境变量名需要与传入的环境变量名相同才能获取到
 15 
 16     if(strcmp(argv[1], "a") == 0)       //需要用strcmp对两个值进行判断,而不能用==直接判断
 17     {
 18         printf("snowdragon receive a!\n");
 19     }
 20     else if(strcmp(argv[1], "b") == 0)
 21     {
 22         printf("snowdragon receive b!\n");
 23     }
 24     else if(strcmp(argv[1], "c") == 0)
 25     {
 26         printf("snowdragon receive c!\n");
 27     }
 28     else
 29     {
 30         printf("default\n");
 31     }
 32 
 33     return 0;
 34 }

5、运行结果

在这里插入图片描述

本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
创作不易,如果觉得博主写得不错,请点赞、收藏加关注支持一下💕💕💕

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

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

相关文章

Transformer中的编码器和解码器结构有什么不同?

Transformer背后的核心概念&#xff1a;注意力机制&#xff1b;编码器-解码器结构&#xff1b;多头注意力等&#xff1b; 例如&#xff1a;The cat sat on the mat&#xff1b; 1、嵌入&#xff1a; 首先&#xff0c;模型将输入序列中的每个单词嵌入到一个高维向量中表示&…

JavaScript包管理器:yarn的安装与配置详解

Yarn是一个流行的JavaScript包管理器&#xff0c;它允许开发者使用代码来安装、更新和删除项目中的依赖包。Yarn的安装与配置过程相对简单&#xff0c;以下将详细说明这一过程&#xff1a; 一、Yarn的安装 Yarn的安装可以通过多种方式进行&#xff0c;主要取决于你的操作系统…

深入剖析C++的 “属性“(Attribute specifier sequence)

引言 在阅读开源项目源代码是&#xff0c;发现了一个有趣且特殊的C特性&#xff1a;属性。 属性&#xff08;attribute specifier sequences&#xff09;是在C11标准引入的。在C11之前&#xff0c;编译器特有的扩展被广泛用来提供额外的代码信息。例如&#xff0c;GNU编译器&…

3年经验的B端产品经理,应该是什么水平?

问你一个问题&#xff1a;你觉得3年经验的B端产品经理&#xff0c;应该是什么水平&#xff1f;很多朋友可能也没有仔细想过&#xff0c;自己3年后应该达到一个什么水平&#xff1f;能做什么体量的业务&#xff1f;要能拿多少薪资&#xff1f; 前几天和一个B端产品经理聊天&…

LangChain教程:构建基于GPT的应用程序

ChatGPT和GPT-4的成功表明&#xff0c;通过使用强化学习训练的大型语言模型&#xff0c;可以构建可扩展且功能强大的自然语言处理应用程序。 然而&#xff0c;响应的有用性取决于提示信息&#xff0c;这导致用户探索了提示工程领域。此外&#xff0c;大多数现实世界的自然语言…

突破AI性能瓶颈 揭秘LLaMA-MoE模型的高效分配策略

获取本文论文原文PDF&#xff0c;请在公众号【AI论文解读】留言&#xff1a;论文解读 本文介绍了一种名为“LLaMA-MoE”的方法&#xff0c;通过将现有的大型语言模型&#xff08;LLMs&#xff09;转化为混合专家网络&#xff08;MoE&#xff09;&#xff0c;从而解决了训练MoE…

3DMAX卡死也要安装的10大插件

在探索3DMAX的无限创意边界时&#xff0c;有些插件如同星辰般璀璨&#xff0c;即便面对插件偶尔的“倔强”卡顿&#xff0c;设计师们依然对其爱不释手&#xff0c;誓要将其纳入麾下。以下便是那份令人心动的“卡死也要安装”的10大插件清单&#xff0c;每个都蕴含着设计师对美的…

HKT DICT解决方案,为您量身打造全方位的一站式信息管理服务

随着大数据时代的到来&#xff0c;企业对现代化管理、数据整合与呈现的解决方案需求不断增长。为满足更多企业客户的多元化信息管理发展需求&#xff0c;香港电讯&#xff08;HKT&#xff09;强势推出全面、高效、安全、可靠的一站式DICT&#xff08;Digital Information and C…

Python数据处理之高效校验各种空值技巧详解

概要 在编程中,处理空值是一个常见且重要的任务。空值可能会导致程序异常,因此在进行数据处理时,必须确保数据的有效性。Python 提供了多种方法来处理不同数据对象的空值校验。本文将详细介绍如何对Python中的各种数据对象进行空值校验,并包含相应的示例代码,帮助全面掌握…

mipi协议中的calibration和scramble模式

在MIPI(Mobile Industry Processor Interface)协议中,calibration(校准)和scramble(加扰)模式是两个重要的特性,它们分别用于优化数据传输的准确性和减少信号干扰。以下是对这两个模式的详细解析: Calibration(校准)模式 目的与功能: 校准模式主要用于优化和补偿由…

备考无忧,张驰课堂与刷题共筑六西格玛考试坚实后盾

刷题对考中质协&#xff08;中国质量协会&#xff09;的六西格玛绿带和黑带考试具有显著的帮助&#xff0c;主要体现在以下几个方面&#xff1a; 一、巩固知识点 加深理解&#xff1a;刷题可以帮助考生更深入地理解和记忆六西格玛管理的相关知识点。通过反复练习&#xff0c;…

CAD应用程序开发工具CST CAD Navigator 1.4.0.1 正式发布—— 带来了 G 代码生成功能

CST CAD Navigator是一款兼容Windows和Linux的CAD应用程序。在其简单的界面下&#xff0c;有一个可以快速查看2D图纸和3D模型的强大核心。软件可以轻松地导入和导出文件&#xff0c;获取尺寸&#xff0c;并创建截面视图。 下载最新版CST CAD Navigatorhttps://www.evget.com/p…

七款知名电脑监控软件的介绍(2024年电脑监控软件整理推荐)

在信息化迅猛发展的今天&#xff0c;电脑监控软件成为企业管理和安全防护的重要工具。这类软件不仅有助于提高员工工作效率&#xff0c;还能防范数据泄露&#xff0c;保障企业的核心利益。以下是对几款知名电脑监控软件的介绍&#xff0c;它们在各自领域内都有出色表现。 固信…

帕金森患者营养小贴士

&#x1f44b;亲爱的小伙伴们&#xff0c;今天我要给大家带来一份特别的关怀——关于帕金森患者的营养小贴士&#x1f4d8;。帕金森病虽然是一种神经系统疾病&#xff0c;但合理的营养摄入对于患者的生活质量有着不可忽视的影响哦&#xff01;&#x1f4aa; &#x1f34e;多吃水…

如何探索高效知识管理:FlowUs知识库体验很好

在当今信息爆炸的时代&#xff0c;有效的知识管理对于个人和团队的发展至关重要。FlowUs 知识库作为一款创新的知识管理工具&#xff0c;正逐渐成为众多用户的首选&#xff0c;为他们带来了高效、便捷和有条理的知识管理体验。 FlowUs 知识库的一大特色在于其简洁直观的界面设计…

算法可以赋能教育业务的哪些场景?

本文内容就一个点&#xff0c;将算法应用到教育系统中的各场景&#xff0c;让每个业务模块都实现智能化 以下列举出所有的需求点 目录 一、千人千面&#xff0c;个性化推荐流&#xff0c;推荐用户感兴趣的内容 实现方案&#xff1a;CTR模型 应用场景&#xff1a;所有的内容…

java使用poi-tl模版引擎导出word之列表循环数据渲染

目录 1.模版制作2.开启spring表达式3.编写关键代码接口4. 导出结果 poi-tl模版引擎中&#xff0c;如果区块对的值是一个非空集合&#xff0c;区块中的文档元素会被迭代渲染一次或者N次&#xff0c;这取决于集合的大小&#xff0c;类似于foreach语法。 1.模版制作 在静态资源目…

制作电子名片的小程序系统源码 快速生成电子名片

在当今数字化时代&#xff0c;传统的纸质名片已逐渐被智能电子名片所取代。电子名片小程序作为一种基于微信生态的创新名片交换方式&#xff0c;凭借其便捷性、高效性和环保性&#xff0c;成为了众多商务人士的首选。小编分享一个制作电子名片的小程序系统源码&#xff0c;无忧…

【pyqt-实训训练LOG】串口助手

串口助手 前言一、ui设计二、ui的控件命名三、ui转py使用类的方法【扩展】使用ui文件导入&#xff01;P7的小错误解决办法 总结 前言 我的惯例就是万物之始&#xff0c;拜见吾师&#x1f970;⇨pyqt串口合集 最开始的时候我想的是&#xff0c;学了那么久的pyqt&#xff0c;我…

逻辑芯片:现代电子技术的基石

在现代科技飞速发展的时代&#xff0c;逻辑芯片作为集成电路的重要组成部分&#xff0c;已经渗透到我们生活的每一个角落&#xff0c;从计算机到智能手机&#xff0c;从通信设备到工业自动化系统&#xff0c;无一不彰显着其不可或缺的作用。本文将深入探讨逻辑芯片的基本概念、…