Linux操作系统实验十一 进程管理(中)

简介: Linux操作系统实验十一 进程管理(中)

任务描述

在上一关我们学习使用fork函数创建新进程,本关我们将介绍如何另一种创建新进程的系统调用函数。

本关任务:学会使用C语言在Linux系统中使用vfork系统调用创建一个新的进程。

相关知识

在上一关卡中,我们介绍了fork的使用方法。使用fork创建的子进程的特点是:(1)子进程采用写时复制(COW)技术来为子进程创建地址空间;(2)子进程和父进程的执行顺序是由操作系统调度器来决定的。

本关将介绍Linux系统中另一个创建进程的系统调用函数vforkvfork函数是一个历史遗留产物。vfork创建进程与fork创建的进程主要有一下几点区别:

  1. vfork创建的子进程与父进程共享所有的地址空间,而fork创建的子进程是采用COW技术为子进程创建地址空间;
  2. vfork会使得父进程被挂起,直到子进程正确退出后父进程才会被继续执行,而fork创建的子进程与父进程的执行顺序是由操作系统调度来决定。

vfork性能要比fork高,主要原因是vfork没有进行所有数据的复制,尽管fork采用了COW技术优化性能,但是也会为子进程的页表项进行复制,因此vfork要比fork快。

使用vfork时要注意,在子进程中对共享变量的修改也会影响到父进程,因此vfork在带来高性能的同时,也使得整个程序容易出错,因此,开发人员在使用vfork创建进程时,一定要注意对共享数据的修改。

由于vfork创建的子进程和父进程共享所有的数据(栈、堆等等),因此,采用vfork创建的子进程必须使用exit或者exec函数族(下一关将介绍这些函数的功能)来正常退出,不能使用return来退出。

exit函数是用来结束正在运行的整个程序,exit是系统调用级别,它表示一个进程的结束;而return 是语言级别的,它表示调用堆栈的返回。

vfork函数是系统调用函数,man 2 vfork来查看其使用方法。而exit函数是库函数,因此使用man 3 exit来查看其使用方法。

使用vfork函数创建进程

vfork函数的具体的说明如下:

  • 需要的头文件如下:
  • i. #include <sys/types.h>
  • ii. #include <unistd.h>
  • 函数格式如下: pid_t vfork(void);
  • 函数返回值说明: 调用成功,vfork函数两个值,分别是0和子进程ID号。当调用失败时,返回-1,并设置错误编号errno

注意:vfork函数调用将执行两次返回,它将从父进程和子进程中分别返回。从父进程返回时的返回值为子进程的 PID,,而从子进程返回时的返回值为0,并且返回都将执行vfork之后的语句。vfork创建的子进程必须调用exit函数来退出子进程。

案例演示1: 编写一个程序,使用vfork函数创建一个新进程,并在子进程中打印出其进程ID和父进程ID,在父进程中返回进程ID。详细代码如下所示:

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <errno.h>

  7. int main()
  8. {
  9.    pid_t pid;
  10.    pid = vfork();
  11.    if(pid == -1)
  12.    {
  13.        //创建进程失败
  14.        printf("创建进程失败(%s)!\n", strerror(errno));
  15.        return -1;
  16.    }
  17.    else if(pid == 0)
  18.    {
  19.        //子进程
  20.        sleep(2);  //睡眠2秒
  21.        printf("当前进程为子进程:pid(%d),ppid(%d)\n", getpid(), getppid());
  22.    }
  23.    else
  24.    {
  25.        //父进程
  26.        printf("当前进程为父进程:pid(%d),ppid(%d)\n", getpid(), getppid());
  27.    }
  28.    
  29.    //子进程和父进程分别会执行的内容
  30.    exit(0);
  31. }

将以上代码保存为vforkProcess.c文件,编译执行。可以看到vforkProcess创建的子进程尽管使用sleep函数睡眠了2秒,但是函数父进程的执行顺序在子进程后,这就是vfork的特性。

当我们将以上代码中的exit(0)换成return 0时,则会出现如下错误。

出现以上错误的原因是当子进程使用return退出时,操作系统也会把栈清空,那么当父进程继续使用return退出时,则会发现栈已经被清空了,这就相当于free两次同一块内存,因此会出现错误。

编程要求

本关的编程任务是补全右侧代码片段中BeginEnd中间的代码,具体要求如下:

  • 补全createProcess函数,使用vfork函数创建进程,并在子进程中输出"Children"字符串(提示:需要换行),在父进程中输出"Parent"字符串(提示:需要换行)。

测试说明

本关的测试需要用户在右侧代码页中补全代码,然后点击评测按钮,平台会自动验证用户是否按照要求去检测结果。

任务描述

在上一关我们学习使用vfork函数创建新进程,并且使用exit来结束子进程,本关我们将介绍Linux系统中结束进程的其它方法。

本关任务:学习终止进程的常见方法。

相关知识

在上一关以及看到,开发人员使用vfork创建出来的子进程可以用exit函数来结束。在 Linux 环境中,一个进程的结束,可以通过调用相应的函数实现,也可以是接收到某个信号而结束。

常见与退出进程相关的函数有:exit_exitatexiton_exitabortassert

  1. exit函数是标准C库中提供的函数,它用来终止正在运行的程序,并且关闭所有I/O标准流。
  2. _exit函数也可用于结束一个进程,与exit函数不同的是,_exit不会关闭所有I/O标准流。
  3. atexit 函数用于注册一个不带参数也没有返回值的函数以供程序正常退出时被调用。
  4. on_exit 函数的作用与atxeit函数十分类似,不同的是它注册的函数具有参数,退出状态和参数arg都是传递给该程序使用的。
  5. abort 函数其实是用来发送一个SIGABRT信号,这个信号将使当前进程终止。
  6. assert是一个宏。调用assert时,它将先计算参数表达式 expression的值,如果为0,则调用abort函数结束进程。

[exit_exit区别]

以上关于退出处理函数中只有_exit是系统调用函数,因此使用man 2 _exit来查看其使用方法,而其余函数都是库函数,因此使用man 3 函数名来查看其使用方法。

exit_exit使用方法

exit函数的具体的说明如下:

  • 需要的头文件如下:
  • i. #include <stdlib.h>
  • 函数族格式如下:
  • i. void exit(int status);

参数说明: status:设置程序退出码;

_exit函数的具体的说明如下:

  • 需要的头文件如下:
  • i. #include <unistd.h>
  • 函数族格式如下:
  • i. void _exit(int status);

参数说明: status:设置程序退出码;

  • 函数返回值说明: exit_exit均无返回值。
atexiton_exit使用方法

atexiton_exit函数的具体的说明如下:

  • 需要的头文件如下:
  • i. #include <stdlib.h>
  • 函数族格式如下:
  • i. int atexit(void (*function)(void));
  • ii. int on_exit(void (*function)(int , void *), void *arg);

参数说明: atexit函数的function参数是一个函数指针,指向无返回值和无参数的函数; on_exit函数的function参数是一个函数指针,指向无返回值和有两个参数的函数,其中第一个参数是调用exit()或从main中返回时的值,参数arg指针会传给参数function函数;

  • 函数返回值说明: atexiton_exit调用成功返回0;调用失败返回一个非零值。

注意:atexiton_exit只有在程序使用exit或者main中正常退出时才会有效。如果程序使用_exitabortassert退出程序时,则不会执行被注册的函数。

案例演示1: 使用atexit注册一个退出函数,使其在调用退出函数前被执行,详细代码如下所示:

  1. #include <stdlib.h>
  2. #include <stdio.h>

  3. void out()
  4. {
  5.    printf("程序正在被退出\n");
  6. }

  7. int main()
  8. {
  9.    if(atexit(out) != 0)
  10.    {
  11.        printf("调用atexit函数错误\n");
  12.    }
  13.    
  14.    return 0;   //或者exit(0)
  15. }

将以上代码保存为atexit.c文件,编译执行。可以看到执行atexit程序后,out函数被调用。

案例演示2: 使用on_exit注册一个退出函数,使其在调用退出函数前被执行,详细代码如下所示:

  1. #include <stdlib.h>
  2. #include <stdio.h>

  3. void out(int status, void *arg)
  4. {
  5.    printf("%s(%d)\n", (char *s)arg, status);
  6. }

  7. int main()
  8. {
  9.    if(on_exit(out, "程序正在被退出") != 0)
  10.    {
  11.        printf("调用on_exit函数错误\n");
  12.    }
  13.    
  14.    exit(1);   //或者return 1
  15. }

将以上代码保存为on_exit.c文件,编译执行。可以看到执行on_exit程序后,out函数被调用,并且status变量的值就是exit函数退出的值。

abortassert使用方法

abort函数的具体的说明如下:

  • 需要的头文件如下:
  • i. #include <stdlib.h>
  • 函数族格式如下:
  • i. void abort(void);

assert宏的具体的说明如下:

  • 需要的头文件如下:
  • i. #include <assert.h>
  • 函数族格式如下:
  • i. void assert(scalar expression);

参数说明: expression:需要被判断的表达式;

注意:assert宏通常用于调试程序。

  • 函数返回值说明: abortassert无返回值。

案例演示1: 使用abort终止一个程序,详细代码如下所示:

  1. #include <stdlib.h>
  2. #include <stdio.h>

  3. int main()
  4. {
  5.    printf("Hello world\n");
  6.    
  7.    abort();
  8. }

将以上代码保存为abort.c文件,编译执行。可以看到执行abort程序后,程序被强行终止。

编程要求

本关的编程任务是补全右侧代码片段中BeginEnd中间的代码,具体要求如下:

  • 补全exitProcess函数,使用atexit函数注册一个函数,在注册函数中打印出当前进程的ID号。

测试说明

本关的测试需要用户在右侧代码页中补全代码,然后点击评测按钮,平台会自动验证用户是否按照要求去检测结果。

相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
目录
相关文章
|
14天前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
39 1
|
3天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
35 13
|
9天前
|
SQL 运维 监控
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
|
16天前
|
C语言 开发者 内存技术
探索操作系统核心:从进程管理到内存分配
本文将深入探讨操作系统的两大核心功能——进程管理和内存分配。通过直观的代码示例,我们将了解如何在操作系统中实现这些基本功能,以及它们如何影响系统性能和稳定性。文章旨在为读者提供一个清晰的操作系统内部工作机制视角,同时强调理解和掌握这些概念对于任何软件开发人员的重要性。
|
16天前
|
安全 Linux 数据安全/隐私保护
深入Linux操作系统:文件系统和权限管理
在数字世界的海洋中,操作系统是连接用户与硬件的桥梁,而Linux作为其中的佼佼者,其文件系统和权限管理则是这座桥梁上不可或缺的结构。本文将带你探索Linux的文件系统结构,理解文件权限的重要性,并通过实际案例揭示如何有效地管理和控制这些权限。我们将一起航行在Linux的命令行海洋中,解锁文件系统的奥秘,并学习如何保护你的数据免受不必要的访问。
|
16天前
|
Linux 调度 C语言
深入理解操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅,从进程管理的基本概念出发,逐步探索到内存管理的高级技巧。我们将通过实际代码示例,揭示操作系统如何高效地调度和优化资源,确保系统稳定运行。无论你是初学者还是有一定基础的开发者,这篇文章都将为你打开一扇了解操作系统深层工作原理的大门。
|
16天前
|
搜索推荐 Linux
深入理解Linux操作系统的启动过程
本文旨在揭示Linux操作系统从开机到完全启动的神秘面纱,通过逐步解析BIOS、引导加载程序、内核初始化等关键步骤,帮助读者建立对Linux启动流程的清晰认识。我们将探讨如何自定义和优化这一过程,以实现更高效、更稳定的系统运行。
|
14天前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
15天前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
5月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
下一篇
DataWorks