《Linux C编程实战》笔记:进程操作之创建进程

进程是一个动态的实体,是程序的一次执行过程。进程是操作系统资源分配的基本单位。

以下是一些概念,我就直接抄书了

进程是操作系统的知识,简单理解的话,你写的代码运行起来算一个进程?

创建进程

每个进程由进程ID号标识,进程被创建时系统会为其分配一个惟一的进程ID。当一个进程向其父进程(创建该进程的进程)传递其终止消息时,意味这个进程的整个生命周期的结束。此时,该进程占用的所有资源包括进程ID被全部释放。
创建进程有两种方式,一是由操作系统创建;二是由父进程创建。由操作系统创建的进程,它们之间是平等的,一般不存在资源继承关系。而对于由父进程创建的进程(通常称为子进程),它们和父进程存在隶属关系。子进程又可以创建进程,这样形成一个进程家族。子进程可以继承其父进程几乎所有的资源。在系统启动时,操作系统会创建一些进程,它们承担着管理和分配系统资源的任务,这些进程通常被称为系统进程。
系统调用fork是创建一个新进程的惟一方法(创建一个进程通常也称为fork一个进程),除了极少数以特殊方式创建的进程,如 init进程,它是内核启动时以特殊方式创建的。进程调用fork函数就创建了一个子进程。
创建了一个子进程之后,父进程和子进程争夺CPU,抢到CPU 者执行,另外一个挂起等待。如果想要父进程等待子进程执行完毕以后再继续执行,可以在 fork 操作之后调用 wait或waitpid。一个刚刚被fork的子进程会和它的父进程一样,继续执行当前的程序。几个进程同时执行一个应用程序通常用处不大。更常见的使用方法是子进程在被fork后可以通过调用exec函数执行其他程序

fork函数

#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);

一般情况下,函数最多有一个返回值,但fork函数非常特殊,它有两个返回值,即调用一次返回两次。成功调用fork函数后,当前进程实际上已经分裂为两个进程,一个是原来的父进程,另一个是刚刚创建的子进程。父子进程在调用fork函数的地方分开,fork函数有两个返回值,一个是父进程调用fork函数后的返回值,该返回值是刚刚创建的子进程的ID;另一个是子进程中fork函数的返回值,该返回值是0。fork函数返回两次的前提是进程创建成功,如果进程创建失败,则只返回-1。两次返回不同的值,子进程返回值为0,而父进程的返回值为新创建的子进程的进程ID,这样可以用返回值来区别父、子进程。

示例程序1

演示fork函数的用法

#include<cstdlib>
#include<cstring>
#include <cstdio>
#include<ctime>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
using namespace std;

int main(){
    pid_t pid;
    printf("Process creation study\n");
    pid=fork();//创建子进程
    switch (pid)
    {
    case 0://子进程
        printf("Child process is running,CurPid is %d,ParentPid is %d\n",pid,getppid());
        break;
    case -1://创建失败
        perror("Process creation failed\n");
        break;
    default://父进程
        printf("Parent process is running,ChildPid is %d,ParentPid is %d\n",pid,getpid());
        break;
    }
    exit(0);
}

运行结果

可以看到,fork在父子进程的返回值不同。程序在fork执行完后在此处创建一个子进程,父子进程会分别一起执行fork之后的语句。

结果里是父进程先于子进程执行,但一般来说是不固定的

示例程序2

为了演示这种不固定,可以看以下示例程序

#include<cstdlib>
#include<cstring>
#include <cstdio>
#include<ctime>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
using namespace std;

int main(){
    pid_t pid;
    const char *msg;
    int k;
    printf("Process creation study\n");
    pid=fork();
    switch (pid)
    {
    case 0:
        msg="Child process is running\n";//子进程会输出的字符串
        k=3;
        break;
    case -1:
        perror("Process creation failed\n");
        break;
    default:
        msg="Parent process is running\n";//父进程会输出的字符串
        k=5;
        break;
    }
    while (k>0)
    {
        fputs(msg,stdout);
        sleep(1);
        k--;//子进程和父进程的k不相同
    }
    
    exit(0);
}

运行结果:

可以看出父子进程交替执行

sleep() 函数是一个标准C库函数,用于让程序暂停执行指定的时间,以秒为单位。在Linux/Unix系统中,它的声明在头文件 <unistd.h> 中。

前面提到 fork在创建进程失败时,返回-1。失败的原因通常是父进程拥有的子进程的个数超过了规定的限制,此时errno值为EAGAIN。如果可供使用的内存不足也会导致进程创建失败,此时errno值为 ENOMEM

子进程会继承父进程的很多属性,主要包括用户ID、组ID、当前工作目录、根目录、打开的文件、创建文件时使用的屏蔽字、信号屏蔽字、上下文环境、共享的存储段、资源限制等。子进程与父进程有一些不同的属性,主要有如下这些。

  1. 子进程有它自己惟一的进程ID。
  2. fork的返回值不同,父进程返回子进程的ID,子进程的则为0。
  3. 不同的父进程ID。子进程的受进程D为创建它的父进程ID。
  4. 子进程共享父进程打开的文件描述符,但父进程对文件描述符的改变不会影响子进程中的文件描述符。
  5. 子进程不继承父进程设置的文件锁。
  6. 子进程不继承父进程设置的警告。
  7. 子进程的未决信号集被清空。

示例程序3

本示例演示孤儿进程

如果一个子进程的父进程先于子进程结束,子进程就成为一个孤儿进程,它由 init进程收养,成为init进程的子进程。

#include<cstdlib>
#include<cstring>
#include <cstdio>
#include<ctime>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
using namespace std;
int main(){
    pid_t pid;
    pid=fork();
    switch (pid)
    {
    case 0://子进程进入一个死循环,一直不会结束
        while (1)
        {
            printf("A background process,PID:%d\n,ParentID:%d\n",getpid(),getppid());
            sleep(3);
        }
        
    case -1:
        perror("Process creation failed\n");
        exit(-1);
    default://父进程先结束
        printf("I am parent process,my pid is %d\n",getpid());
        exit(0);
    }   
    return 0;
}

这我就不运行了,子进程根本结束不了,预期的运行结果是这样的

父进程打印一次就执行完毕了,此后子进程就成了孤儿进程,由init进程收养,可以看到此时子进程的父进程ID变为1.

vfork函数

vfork也可以用来创建新进程,和fork相比,他有独特用处。

  1. 返回值:

    • fork() 的返回值是新创建的子进程的进程ID(PID)在父进程中,而在子进程中返回0。因此,通过返回值的不同可以在父进程和子进程中采取不同的操作。
    • vfork() 的返回值也是新创建的子进程的PID,但与fork() 不同的是,父进程和子进程共享地址空间,所以在子进程中对地址空间的修改会影响到父进程。
  2. 地址空间:

    • fork() 中,子进程获得父进程的完整地址空间的副本,父子进程互不干扰,各自独立执行。
    • vfork() 中,子进程共享父进程的地址空间,子进程运行在父进程的地址空间中,直到调用 exec 函数或者 exit()
  3. 执行时机:

    • fork() 中,父进程和子进程之间的执行是并发的,即它们可以同时运行。
    • vfork() 中,父进程会被挂起,直到子进程调用 exec 函数或者 exit(),因为子进程共享父进程的地址空间,如果父进程继续执行可能会影响到子进程的执行。
  4. 用途:

    • fork() 适用于创建一个独立的进程,父子进程各自执行,相互不影响。通常用于多任务并发执行。
    • vfork() 主要设计用于在子进程中立即执行一个新程序,它更加轻量级,因为不需要复制整个地址空间,但是需要小心使用,以避免父子进程之间的竞态条件。

总体而言,fork() 适用于大多数常规情况,而 vfork() 更适用于特殊情况,例如在子进程中立即执行一个新程序,而不涉及大量的地址空间复制。

简单来说,vfork创建的子进程会先执行,执行完毕父进程才能继续,而且子进程对变量的修改父进程可见且受影响,因为它们共享地址空间。

使用vfork需谨慎。

示例程序4

下面例子说明两者的不同

#include<cstdlib>
#include<cstring>
#include <cstdio>
#include<ctime>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
using namespace std;
int globVar=5;//全局变量

int main(){
    pid_t pid;
    int var=1,i;
    pid=fork();
    /*pid=vfork();*/
    switch (pid)
    {
    case 0:
        i=3;
        while (i-->0)
        {
            printf("Child process is running\n");
            globVar++;
            var++;
            sleep(1);
        }
        printf("Child's globVar =%d,var=%d\n",globVar,var);
        break;
    case -1:
        perror("Process creation failed\n");
        exit(-1);
    default:
        i=5;
        while (i-->0)
        {
            printf("Parent process is running\n");
            globVar++;
            var++;
            sleep(1);
        }
        printf("Parent's globVar =%d,var=%d\n",globVar,var);
        break;
    }   
    exit(0);
}

首先我们运行fork版本的

可以看到子进程和父进程不管是全局的globvar还是局部的var都增加了对应的值,证明子父进程地址空间是独立的。而且子父进程的执行顺序不确定。

下面我们把fork注释,把vfork取消注释,再次运行

可以看到父进程结束时的var的值均是在子进程结束时的基础上加的5,证明它们共享地址空间。而且子进程执行完毕后父进程才能继续执行。

书内还有守护进程的内容,不过感觉 跨度有点大,就先跳过了

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在vue项目中,数据已经在页面渲染,但在后续操作时获取不到数据

如下图 产生这个问题的原因 异步问题 如何解决 方法一&#xff1a;可以将其存放在一个setTimeout里面&#xff08;利用一个极小的延迟来获取数据&#xff09;&#xff0c;如下图 效果 方法二&#xff1a;将操作放入axios里面&#xff0c;如下图

昂首资本发现原油价差这样用,难怪银行这么富

难怪银行这么富&#xff0c;原来是发现一个稳定产生利益的投资策略。虽然这个利润可能看起来比较少。但是昂首资本需要提醒各位投资者的是&#xff1a;首先&#xff0c;这个策略几乎没有风险。第二&#xff0c;这是一个可以复制的投资策略。 下面昂首资本就通过原油的价差进行实…

开发信怎么写回复率高?写外贸邮件的技巧?

如何打造高回复率的开发信&#xff1f;有效的开发信模板推荐&#xff1f; 如何写一封能够引起客户兴趣并提高回复率的开发信变得至关重要。开发信是建立联系、促进销售和扩大业务的关键工具之一。蜂邮EDM将探讨一些关键策略&#xff0c;帮助你提高开发信的回复率&#xff0c;确…

爬虫图片验证码处理

图片验证码处理 目前&#xff0c;很多网站为了防止爬虫爬取&#xff0c;登录时需要用户输入验证码。下面我们学习如何在爬虫程序中识别验证码。 其中包含验证码。 页面中的验证码图片对应一个<img>元素&#xff0c;即一张图片&#xff0c;浏览器加载完登录页面后&#…

Spring 6(二)【IOC原理】

前言 IOC 是Spring的两大核心概念之一&#xff0c;它是一种思想&#xff0c;需要极其熟练的掌握。 今日摘录&#xff1a; 低能无聊的人太多。说他们勤勉&#xff0c;不过是因困为不会合理分配时间&#xff1b;说他们积极&#xff0c;不过是逃避其他困难工作而已。即便说工作只…

DNN二分类模型

import os import datetime#打印时间 def printbar():nowtime datetime.datetime.now().strftime(%Y-%m-%d %H:%M:%S)print("\n"""*8 "%s"%nowtime)#mac系统上pytorch和matplotlib在jupyter中同时跑需要更改环境变量 os.environ["KMP_DUP…

【网络安全】-Linux操作系统—操作系统发展历史与Linux

文章目录 操作系统发展历史初期的操作系统分时操作系统个人计算机操作系统 Linux的诞生UNIX与GNU项目Linux内核的创建 Linux的特点开放源代码多样性社区支持 Linux的应用服务器和超级计算机嵌入式系统桌面系统 总结 操作系统发展历史 操作系统&#xff08;Operating System&am…

详细教程 - 从零开发 Vue 鸿蒙harmonyOS应用 第五节 (基于uni-app封装鸿蒙接口请求库)

随着鸿蒙系统的兴起,越来越多的app会采用鸿蒙开发。而鸿蒙开发必不可少的就是调用各种接口服务。为了简化接口的调用流程,我们通常会做一层封装。今天就来讲解一下,如何用uni-app封装鸿蒙的接口请求库。 一、新建项目 首先我们要新建一个鸿蒙项目啦&#xff01;当然选择第一个…

neuq-acm预备队训练week 9 P1119 灾后重建

解题思路 本题可以用最短路算法——Floyd AC代码 #include<bits/stdc.h> #define inf 1e9 using namespace std; const int N 2e2 50; int n, m, q, now 0, a, b, c, t[N], G[N][N];int main() {scanf("%d%d", &n, &m);for(int i 0;i<n;i)sc…

044.Python异常处理_手动抛出异常自定义异常

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

ubuntu 磁盘挂载

1.前提 给自己的计算机加了一个新硬盘&#xff0c;怎么在ubuntu中使用呢 特别提示&#xff01;对磁盘操作存在一定丢失数据的风险&#xff0c;本篇是在一个新购买的硬盘上进行操作&#xff01;如果你使用的是一个带数据的硬盘&#xff0c;请勿参考本篇文章&#xff01; 2.找…

解决:AttributeError: module ‘scipy.misc’ has no attribute ‘imread’

解决&#xff1a;AttributeError: module ‘scipy.misc’ has no attribute ‘imread’ 文章目录 解决&#xff1a;AttributeError: module scipy.misc has no attribute imread背景报错问题报错翻译报错位置代码报错原因解决方法方法一 scipy版本回退&#xff08;不推荐&#…

记录 | Visual Studio报错:const char*类型的值不能用于初始化char*类型

Visual Studio 报错&#xff1a; const char *”类型的值不能用于初始化“char *”类型的实体错误 解决办法&#xff1a; 1&#xff0c;强制类型转换&#xff0c;例如&#xff1a; char * Singer::pv[] {(char*)"other", (char*)"alto", (char*)"c…

会旋转的树,你见过吗?

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;强烈推荐优质专栏: &#x1f354;&#x1f35f;&#x1f32f;C的世界(持续更新中) &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;…

工业数据的特殊性和安全防护体系探索思考

随着工业互联网的发展&#xff0c;工业企业在生产运营管理过程中会产生各式各样数据&#xff0c;主要有研发设计数据、用户数据、生产运营数据、物流供应链数据等等&#xff0c;这样就形成了工业大数据&#xff0c;这些数据需要依赖企业的网络环境和应用系统进行内外部流通才能…

字体包引入以及使用

将UI给的字体包下载到assets文件夹下 app.vue全局定义 <style> * {margin: 0;padding: 0; }font-face {font-family: PangMenZhengDao-3;src: url(/assets/fonts/庞门正道标题体3.0.TTF) format(truetype);font-weight: bold;font-style: normal; } </style>页面使…

亚马逊云科技re:Invent推出生成式AI技术堆栈及关键服务和工具

亚马逊云科技于29日推出“生成式AI技术堆栈”后&#xff0c;又在30日的re:Invent 2023大会上宣布了一系列支持这一全新堆栈的关键服务和工具。 亚马逊云科技数据和人工智能副总裁Swami Sivasubramanian在主题演讲中&#xff0c;将生成式人工智能与“超新星爆炸”进行了比较&am…

ffmpeg可以做什么

用途 FFmpeg是一个功能强大的多媒体处理工具&#xff0c;可以处理音频和视频文件。它是一个开源项目&#xff0c;可在各种操作系统上运行&#xff0c;包括Linux、Windows和Mac OS X等。以下是FFmpeg可以做的一些主要任务&#xff1a; 转换媒体格式&#xff1a;可将一个媒体格式…

云原生基础入门概念

文章目录 发现宝藏云原生的概念云原生的关键技术为何选择云原生&#xff1f;云原生的实际应用好书推荐 发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【宝藏入口】。 云原生的概念 当谈及现…

http跟https的区别

只要上过网的朋友一定接触过“HTTP”&#xff0c;每次开网页的时候&#xff0c;不管是什么网址&#xff0c;其前面都会出现HTTP字样&#xff0c;比如 “http&#xff1a;55049sjad.com”、“http&#xff1a;544.65.5.6.com”等等&#xff0c;而有些时候打开如银行等对安全性要…
最新文章