Linux C 多进程编程(面试考点)

news2025/1/31 2:54:56

嵌入式开发为什么要移植操作系统?

1.减小软硬件的耦合度,提高软件的移植性

2. 操作系统提供很多库和工具(QT  Open CV),提高开发效率

3.操作系统提供多任务机制,______________________?        (提高CPU的效率)

4.操作系统提供了丰富的网络协议栈,实现远程传输

Linux C 多进程编程(多进程、多线程)

1.什么是多任务

单任务————多任务

并发————并行

单核CPU ————多核CPU

2.多任务操作的实现方式

进程和线程

程序和进程的区别

        程序:

 是一组指令和数据的集合,是静态的、存储在磁盘或其他存储介质上的文件。

程序可以被看作是一段代码的集合,描述了在计算机上执行的任务。

程序本身是静态的,需要加载到内存中才能执行。

        进程:

是程序的抽象

是动态的

每个进程都都有独立的运行空间(虚拟地址空间),

每个进程都是一个独立的运行单位,拥有各自的权力和责任;(互不干扰)

进程是   安全的任务机制

缺点:开销大(进程创建和进程切换)

        进程PID编号

父进程创建子进程

获取进程PID

获取父进程PID

 ps命令、top命令和htop命令

 进程调度

在许多个已经处于就绪态的进程中,选择决定哪个进程进行调度(基于进程三态)

进程状态:

就绪态、执行态和等待态(阻塞态)

操作系统的核心就是任务(进程)管理

           

主要分为两大类:抢占式(设置优先级)和非抢占式(不设置优先级)

有如下策略

1.先到先服务;        2.短进程优先;        3.时间片轮转(使用最多);        4.高优先级优先

实时操作系统是一种响应速度快,准确性高(抢占式)

不同任务之间通过双向链表链接

进程分类:

        处理器消耗型
渴望获取更多的CPU时间,并消耗掉调度器分配的全部时间片·常见例子:无限死循环、科学计算、影视特效渲染
        I/O消耗型
由于等待某种资源通常处于阻塞状态,不需要较长的时间片 常见例子:等待用户输入、GUI程序、文件读写I/O程序

进程同步

多个进程访问同一个文件时;需要互斥访问,否则易产生错误;

操作系统把一次只允许一个进程访问的资源成为临界资源,需要互斥访问

进程的创建

1.fork函数

 作业:利用多进程实现,分别从键盘和鼠标读数据:

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    int count = 0;
    pid_t pid = fork();
    if (pid < 0)        // 错误处理
    {
        perror("pid error");
        exit(-1);
    }
    if (pid > 0)        // 父进程执行鼠标读操作
    {
        int fd1 = open("/dev/input/mouse0", O_RDWR);
        if (fd1 == -1)
        {
            perror("fd1 error");
            exit(-1);
        }
        int location = 0;
        while (1)
        {
            int r_num1 = read(fd1, &location, sizeof(int));
            if (r_num1 > 0)
            {
                printf("mouse loaction=%d\n", location);
            }
        }
    }
    if (pid == 0)      // 子进程执行键盘读写操作
    {
        char buffer[1024];
        memset(buffer, 0, sizeof(buffer));
        while (1)
        {
            int r_num = (read(0, buffer, sizeof(buffer) - 1));
            if (r_num > 0)
            {
                buffer[r_num] = '\0';
                printf("%s\n", buffer);
            }
            memset(buffer, 0, sizeof(buffer));
        }
    }
    return 0;
}

通过多进程实现父子进程对同一个文件进行写操作

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    int count = 0;
    int fd = open("a.txt", O_RDWR | O_APPEND | O_CREAT, 0777);
    if (fd == -1)
    {
        perror("fd error");
        exit(-1);
    }

    pid_t pid = fork();

    if (pid < 0) // 错误处理
    {
        perror("pid error");
        exit(-1);
    }
    if (pid > 0) // 父进程操作
    {

        write(fd, "hello", 5);
        write(fd, "world", 5);
        write(fd, "\n", 1);
    }
    if (pid == 0) // 子进程执行操作
    {

        write(fd, "FFFFF", 5);
        write(fd, "KKKKK", 5);
        write(fd, "\n", 1);
    }
    return 0;
}

父子进程的运行顺序,暂时是不需要明白;内部有进程调度算法

2.exec函数族

使用execl函数时,原函数在execl函数后的代码段会不起作用

表头文件:#include <unistd.h>

1. int execl c const char *path,const char *arg,...)
函数说明:

execl()用来执行参数path字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的argv[0]、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
返回值﹔如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中

2. int execv ( const char *path,char *const argv[]);
函数说明:

execv ()用来执行参数path字符串所代表的文件路径,与execl ()不同的地方在于execve ()只需两个参数,第二个参数系利用指针数组来传递给执行文件。
返回值﹔如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。

3. int execlp (const char*file,const char *arg,...);
函数说明: execlp ( )会从 PATH环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后将第二个以后的参数当作该文件的argv[0]、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
返回值﹔如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。

4.int execvp ( const char *file,char *const argv[]);
函数说明: execvp ( )会从 PATH 环境变量所指的目录中查找符合参数file 的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件。
返回值:如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
错误代码:请参考execve ()
 

5.int execve ( const char *filename,char *const argv [].char *const envp[]);

函数说明:execve ()用来执行参数filename字符串所代表的文件路径,第二个参数系利用指针数组来传递给执行文件,最后一个参数则为传递给执行文件的新环境变量数组
返国值:如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno
中。

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    // 1.参数表直接写入,    以NULL结尾
    execl("./write1", "./write1", "hello", "world", NULL);

    // 2.参数表用指针数组的形式,    以NULL结尾
    char *arg1[] = {"./write2", "welcome", "zhaodeming", NULL};
    xecv("./write2", arg1);

    // 3.
    execlp("/home/zdm/241/PROCESS_CODE/write1", "./write1", "hello", "world", NULL);
    //execlp("./write1", "./write1", "hello", "world", NULL); // 也可以

    // 4.
    char *arg2[] = {"./write2", "welcome", "zhaodmeing", NULL};
    execvp("/home/zdm/241/PROCESS_CODE/write2", arg2);

    // 5.   e---环境变量
    char *env[] = {"USR=admin", "PASSWD=12345"};
    execve("/home/zdm/241/PROCESS_CODE/write2", arg2, env);

    printf("exce demo ok\n");
}

每个调用exec函数,会覆盖掉后面的代码

常常与fork函数联用:

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    int count = 0;
    pid_t pid = fork();
    if (pid < 0) // 错误处理
    {
        perror("pid error");
        exit(-1);
    }
    if (pid > 0) // 父进程执行操作
    {
        execl("./write1", "./write1", "hello", "world", NULL);
    }
    if (pid == 0) // 子进程执行操作
    {
        char *arg1[] = {"./write2", "welcome", "zhaodeming", NULL};
        execvp("/home/zdm/241/PROCESS_CODE/write2", arg1);
    }
    return 0;
}

3.vfork    系统调用

对fork的改进对fork的改进更为彻底、简单粗暴
vfork是为子进程立即执行exec的程序而专设计的
无需为子进程复制虚拟内存页或页表,子进程直接共享父进程的资源,直到其成功执行exec或是调用exit退出
在子进程调用exec之前,将暂停执行父进程

子进程中无exec时,则先执行子进程,后执行父进程;

子进程有exec函数时,exec函数前的代码段先执行;执行到exec函数时候,父子进程调用顺序则又不确定(和fork一样)

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    int count = 0;
    pid_t pid = vfork();
    
    if (pid < 0)
    {
        perror("pid error");
        exit(-1);
    }
    if (pid > 0)
    {
        printf("%d\n",count);
    }
    if (pid == 0)
    {
        //count++;      //尽量避免在子进程中修改全局变量,容易引发段错误;
        //exit(-1);     
        execl("./write1","./write1",NULL);
    }
    return 0;
}

4.system(const char*command)——库函数

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

int main(int aargc, char **argv)
{
    system("./write1  system_using read");
    sleep(3);

    system("clear");
    sleep(3);

    system("ls -l");
    sleep(3
    );

    system("clear");
    sleep(3);
    return 0;
}

进程的退出

_exit——系统调用

更多用于异常退出;不会刷新stdio缓冲区;

exit——库函数

内部封装了_exit;会刷新stdio缓冲区;atexit/on_exit注册了退出管理程序,则应使用exit

正常退出:        main 调用return

异常退出:        1.任意地方调用exit/_exit;         2.被信号杀死;       3. 调用abort函数

abort函数

以异常方式结束进程:abort ( )将引起进程异常的终止,此时所有已打开的文件流会自动关闭,所有的缓冲区数据也会自动写回。

进程等待

回收进程资源

进程运行终止后,不管进程是正常终止还是异常终止的,必须回收进程所占用的资源。如何查看进程资源?                                                       

————————ps命令


为什么要回收进程的资源?                       

————————不回收资源会导致系统性能下降


父进程运行结束时,会负责回收子进程资源

./a.out进程的父进程是谁?

0,1,2三个进程:OS启动后抑制默默运行,直到关机OS结束运行;

pid=0的进程,称作调度进程

pid=1的进程,  1.init进程,跟前端用户做交互;2.托管孤儿进程;3.原始父进程(位于/sbin/init目录下,可以restart*stop);

pid=2的进程;页精灵进程

僵尸进程和孤儿进程

僵尸进程:子进程终止后,父进程还在运行,那么在父进程没有回收子进程资源前,此时的子进程就是僵尸进程

孤儿进程:子进程还未结束,父进程先结束,子进程的资源无法回收,此时子进程就是孤儿进程

为了能够回收孤进程终止后的资源,孤儿进程会被托管给我们前面介绍的pid==1的init进程,每当被托管的子进程终止时,init会立即主动回收孤儿进程资源,回收资源的速度很快,所以孤儿进程没有变成僵尸进程的机会。
 

wait函数

只能父进程等待子进程

函数原型 pid_t wait(int status)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
    pid_t pid = fork();

    if (pid == 0)
    {
        for (int i = 0; i < 3; ++i)
        {
            printf("children aaa\n");
            sleep(1);
        }
        exit(3);
    }
    if (pid > 0)
    {
        printf("parents is ok\n");

        // 1.获取子进程退出状态
        int ret;
        wait(&ret);
        int num = WEXITSTATUS(ret);
        printf("%d\n", ret);

        // 2.wait(NULL); // 阻塞,直到子进程结束,再执行下面代码
    }
    return 0;
}

waitpid函数

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

//父进程等子进程,子进程等子子进程
void die(const char *msg)
{
    perror(msg);
    exit(1);
}
void child2_do()
{
    printf("In child2: execute 'date'\n");
    sleep(5);
    if (execlp("date", "date", NULL) < 0)
    {
        perror("child2 execlp");
    }
}
void child1_do(pid_t child2, char *argv)
{
    pid_t pw;
    do
    {
        if (*argv == '1')
        {
            pw = waitpid(child2, NULL, 0); // 一直等
        }
        else
        {
            pw = waitpid(child2, NULL, WNOHANG); // 立刻返回
        }
        if (pw == 0)
        {
            printf("In child1 process:\nThe child2 process has not exited\n");
            sleep(1);
        }
    } while (pw == 0);
    if (pw == child2)
    {
        printf("Get child2 %d.\n", pw);
        sleep(5);
        if (execlp("pwd", "pwd", NULL) < 0)
        {
            perror("child1 execlp");
        }
    }
    else
    {
        printf("error occured!\n");
    }
}
void father_do(pid_t child1, char *argv)
{
    pid_t pw;
    do
    {
        if (*argv == '1')
        {
            pw = waitpid(child1, NULL, 0); // 一直等待
        }
        else
        {
            pw = waitpid(child1, NULL, WNOHANG); // 立刻返回
        }
        if (pw == 0)
        {
            printf("In father process: \nThe child1 process has not exited.\n");
            sleep(1);
        }
    } while (pw == 0);
    if (pw == child1)
    {
        printf("Get child1 %d.\n", pw);
        if (execlp("ls", "ls", "-l", NULL) < 0)
        {
            perror("father execlp");
        }
    }
    else
    {
        printf("error occured ! \n");
    }
}
int main(int argc, char **argv)
{
    pid_t child1, child2;
    if (argc < 3)
    {
        printf("Usage: waitpid [0 1] [0 1]\n");
        exit(1);
    }
    child1 = fork();
    if (child1 < 0)
    {
        die("child1 fork");
    }
    else if (child1 == 0)
    {
        child2 = fork();
        if (child2 < 0)
        {
            die("child2 fork");
        }
        else if (child2 == 0)
        {
            child2_do();
        }
        else
        {
            child1_do(child2, argv[1]);
        }
    }
    else
    {
        father_do(child1, argv[2]);
    }
    return 0;
}

当父进程没有调用waitpid或wait函数来回收子进程的终止状态时,子进程的终止状态信息将一直保留在系统的进程表中,并使子进程成为僵尸进程。僵尸进程不占用系统资源,但是如果产生大量的僵尸进程却没有及时回收,可能会导致系统资源被占用完。

通过在父进程中调用waitpid或wait函数,父进程会等待子进程的终止并回收其终止状态信息,从而防止子进程变成僵尸进程,并及时释放子进程占用的资源。这样可以保持系统的正常运行和资源的有效利用。

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

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

相关文章

Zenity 简介

什么使 Zenity Zenity 是一个开源的命令行工具&#xff0c;它提供了一种简单的方式来创建图形化的用户界面&#xff08;GUI&#xff09;对话框&#xff0c;以与用户进行交互。它基于 GTK 库&#xff0c;可以在 Linux 和其他 UNIX-like 系统上使用。 Zenity 可以通过命令行或脚…

最新政策丨政务服务电子文件归档和电子档案管理办法说了什么?

随着数字化时代的持续演进&#xff0c;我国政府部门正积极推动数字政府建设&#xff0c;以优化政务服务&#xff0c;提升办事效率。为了适应这一背景&#xff0c;国务院发布了《政务服务电子文件归档和电子档案管理办法》&#xff0c;旨在规范电子档案管理&#xff0c;加强政务…

为什么使用消息队列?消息队列能够做什么?消息队列有哪些?怎么选择?

❤ 作者主页&#xff1a;李奕赫揍小邰的博客 ❀ 个人介绍&#xff1a;大家好&#xff0c;我是李奕赫&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 记得点赞、收藏、评论⭐️⭐️⭐️ &#x1f4e3; 认真学习!!!&#x1f389;&#x1f389; 文章目录 为什么使用消…

msvcp110.dll下载安装方法分享,教你怎么快速的修复msvcp110.dll文件

当你的电脑出现msvcp110.dll文件缺失时&#xff0c;这时候不要慌张&#xff0c;其实要解决这个问题很简单&#xff0c;我们只要重新下载安装msvcp110.dll文件就可以了&#xff0c;今天主要是来给大家讲解一下这方面的信息&#xff0c;教大家如何下载安装msvcp110.dll。 一.了解…

MPDIoU:有效和准确的边界框回归的损失

文章目录 摘要1、简介2、相关工作2.1、目标检测和实例分割2.2. 场景文本识别2.3、边界框回归的损失函数 3、点距最小的并集交点4、实验结果4.1、 实验设置4.2、数据集4.3、 评估协议4.4、 目标检测的实验结果4.5、 字符级场景文本识别的实验结果4.6、 实例分割的实验结果 5、 结…

Anomalib:异常检测的深度学习库 -- 应用Anomalib训练自己的图片

文章目录 资料汇总 Github链接&#xff1a;https://github.com/openvinotoolkit/anomalib/blob/main/README.md 论文链接&#xff1a;https://arxiv.org/pdf/2202.08341v1.pdf 其他参考资料&#xff1a;https://paperswithcode.com/paper/efficientad-accurate-visual-anomaly-…

突破限制,创造佳绩!国内工作流厂商助您腾飞!

随着业务量的激增&#xff0c;很多企业单位都想在办公领域更上一层楼&#xff0c;实现飞跃式地腾飞。采用什么样的软件设备能助力企业降本增质&#xff1f;国内工作流厂商流辰信息作为研发低代码技术平台的服务商&#xff0c;一直深知行业形式和发展动态&#xff0c;将全力以赴…

前端开发工具: VSCode

VSCode 安装使用教程&#xff08;图文版&#xff09; | arry老师的博客-艾编程 1. 下载 在官方网站&#xff1a;https://code.visualstudio.com/ 下载最新版本的 VSCode 即可 2. VSCode 常见插件安装 所有插件安装后,需要重启一下才生效 2.1 简体中文语言包 2.2 编辑器主…

四信重磅推出5G RedCap AIoT摄像机 RedCap轻量级5G终端新品首发!

6月6日&#xff0c;四信受邀出席移动物联网高质量发展论坛&#xff0c;并在移动物联网新产品发布环节隆重推出5G RedCap AIoT摄像机&#xff0c;再次抓紧需求先机&#xff0c;为行业用户创造无限可能&#xff01; 两大应用场景 助推RedCap走深向实 火遍全网络的RedCap应用场景可…

Git gui教程---第七篇 Git gui的使用 返回上一次提交

1&#xff0e; 查看历史&#xff0c;打开gitk程序 2&#xff0e; 选中需要返回的版本&#xff0c;右键&#xff0c;然后点击Rest master branch to here 3.出现弹窗 每个选项我们都试一下&#xff0c;从Hard开始 返回的选项 HardMixedSoft Hard 会丢失所有的修改【此处的…

List 去重两种方式:stream(需要JDK1.8及以上)、HashSet

1、使用Stream 方法 使用JDK1.8及以上 /*** Java合并两个List并去掉重复项的几种做法* param args*/public static void main(String[] args) {String[] str1 {"1", "2", "3", "4", "5", "6"};List<String&…

Protobuf 原理大揭秘

一、定义 Google推出的一种 结构化数据 的数据存储格式&#xff08;类似于 XML、Json &#xff09;。 多个版本的源码地址 https://github.com/protocolbuffers/protobuf/ 1、为什么选择它 优点&#xff1a; 效率高&#xff1a;Protobuf 以二进制格式存储数据&#xff0c;比…

汇编语言调试工具:DosBox及debug安装配置使用教程

前言 学习汇编语言时&#xff0c;需要进入dos模式并使用debug工具调试。但是64位win10系统没有自带这些工具。因此&#xff0c;需要额外安装DosBox和debug.exe两个软件。本文介绍如何下载、安装、配置这两个工具软件。 1、DosBox下载 简介 DOSBox 是一个 DOS 模拟程序&#xf…

SMOKE多模式排放清单处理技术及EDGAR/MEIC清单制作与VOCs排放量核算

1、掌握大气污染源排放清单不确定性来源及定量分析方法&#xff1b; 2、以VOCs排放为例&#xff0c;掌握排放源核算及组分清单建立方法; 3、掌握基于SMOKE模型的大气污染源排放清单处理技术方法&#xff1b; 4、掌握基于SMOKE的多模式排放清单输入制备方法&#xff1b;5、通过…

时序预测 | Matlab实现SO-CNN-BiGRU蛇群算法优化卷积双向门控循环单元时间序列预测

时序预测 | Matlab实现SO-CNN-BiGRU蛇群算法优化卷积双向门控循环单元时间序列预测 目录 时序预测 | Matlab实现SO-CNN-BiGRU蛇群算法优化卷积双向门控循环单元时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 时序预测 | Matlab实现SO-CNN-BiGRU蛇群算法优化…

基于web的成语接龙游戏java jsp趣味学习mysql源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 基于web的成语接龙游戏 系统有1权限&#xff1a;管理…

可以在线做平面设计的软件推荐

互联网是一个视觉场所。如果你想拥有惊人的设计&#xff0c;你必须有专业的在线平面设计工具来帮助你。如今&#xff0c;市场上有大量的在线平面设计工具可供选择。他们提供的平面设计功能几乎可以让任何人成为视觉内容的创作者。但哪一个最适合你呢&#xff1f;本文将为您介绍…

如何理解α、β一类错误和二类错误?

原假设 H0&#xff1a;一般是想要推翻的结论&#xff0c;如指标没有变化&#xff0c;实验组和对照组的该结果指标没有差异等。 备择假设 H1&#xff1a;一般是想要证明的结论&#xff0c;如实验组的指标是显著提升的&#xff0c;指标提升10%等。 弃真错误/一类错误/显著性水平…

物业维修管理有什么好用的软件?“的修”报修工单系统有哪些优势?

随着城市化进程的加速&#xff0c;物业管理的重要性日益凸显。而在物业管理中&#xff0c;维修管理是必不可少的一环。为了更高效地处理业主的报修请求&#xff0c;“的修”报修工单系统应运而生。这款系统不仅方便实用&#xff0c;而且功能强大&#xff0c;为物业管理和业主带…

(四)k8s实战-服务发现

一、Service 1、配置文件 apiVersion: v1 kind: Service metadata:name: nginx-svclabels:app: nginx-svc spec:ports:- name: http # service 端口配置的名称protocol: TCP # 端口绑定的协议&#xff0c;支持 TCP、UDP、SCTP&#xff0c;默认为 TCPport: 80 # service 自己的…