Linux C 多进程编程(面试考点)
嵌入式开发为什么要移植操作系统?
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函数,父进程会等待子进程的终止并回收其终止状态信息,从而防止子进程变成僵尸进程,并及时释放子进程占用的资源。这样可以保持系统的正常运行和资源的有效利用。
相关文章:

Linux C 多进程编程(面试考点)
嵌入式开发为什么要移植操作系统? 1.减小软硬件的耦合度,提高软件的移植性 2. 操作系统提供很多库和工具(QT Open CV),提高开发效率 3.操作系统提供多任务机制,______________________? (提高C…...
c++一级
与7无关的数 #include<iostream> #include<iomanip> using namespace std; int main() { int n,a,sum0,c0; cin>>n; for(int i1;i<n;i){ if(i%7!0){ ai; c0; …...

Code Lab - 34
GAT里面有一些地方看的不是太懂(GAT里Multi Attention的具体做法),暂时找了参考代码,留一个疑问 1. 一个通用的GNN Stack import torch_geometric import torch import torch_scatter import torch.nn as nn import torch.nn.fun…...
后端返回文件流,前端怎么导出、下载(8种方法可实现)
在前端导出和下载后端返回的文件流时,可以使用以下几种方法: 使用window.open()方法: 在前端使用window.open()方法打开一个新的窗口或标签页,并将后端返回的文件流作为URL传递给该方法。浏览器会自动下载该文件。例如:…...
什么是 ThreadLocal?
ThreadLocal 是 Java 中的一个类,用于在多线程环境下,为每个线程提供独立的变量副本。每个线程可以通过 ThreadLocal 存储和获取数据,而不会影响其他线程的数据。这在某些情况下非常有用,特别是当多个线程需要访问共享数据,但又希望保持数据的隔离性时。 ThreadLocal 主要…...

CANOCO5.0实现冗余分析(RDA)最详细步骤
在地理及生态领域会常使用RDA分析,RDA的实现路径也有很多,今天介绍一下CANOCO软件的实现方法。 1.软件安装 时间调整到2010年 2.数据处理 得有不同的物种或者样点数值,再加上环境因子数据。 3.软件运行 4.结果解读 结果解读主要把握这几点…...
【tkinter 专栏】掷骰子游戏
文章目录 前言本章内容导图1. 需求分析2. 系统功能结构3. 设计流程4. 系统开发环境5. 系统预览6. 窗口布局7. 功能实现用户和电脑选择骰子的点数大小摇骰子过程实现判断游戏结果单击开始按钮进行游戏源代码汇总前言 本专栏将参考《Python GUI 设计 tkinter 从入门到实践》书籍…...

19 NAT穿透|python高级
文章目录 网络通信过程NAT穿透 python高级GIL锁深拷贝与浅拷贝私有化import导入模块工厂模式多继承以及 MRO 顺序烧脑题property属性property装饰器property类属性 魔法属性\_\_doc\_\_\_\_module\_\_ 和 \_\_class\_\_\_\_init\_\_\_\_del\_\_\_\_call\_\_\_\_dict\_\_\_\_str…...

2023常见前端面试题
以下是一些2023年秋招常见的前端面试题及其答案: 1. 请解释一下什么是前端开发? 前端开发是指使用HTML、CSS和JavaScript等技术来构建网页和用户界面的过程。前端开发人员负责将设计师提供的视觉设计转化为可交互的网页,并确保网页在不同设备…...
登录校验-JWT令牌-生成和校验
目录 JWT-生成 具体代码 运行结果如下 JWT-校验 具体代码 运行结果如下 小结 JWT-生成 具体代码 /*** 测试JWT令牌的生成*/Testpublic void TestJWT() {// 设置自定义内容Map<String, Object> claims new HashMap<>();claims.put("id", 1);claims…...

GIT 常用指令
基础指令 $ git init #初始化仓库,在该文件夹创建的为workspace$ git add . #已暂存 [.通配符,全部添加]$ git commit -m "log add file" #提交到仓库,并写了日志 ”log add file“$ git status #查看状态,可查看被修改的文件…...

多目标优化
https://zhuanlan.zhihu.com/p/158705342 概念 单目标优化只有一个优化目标,所以可以比较其好坏。 但是多目标优化,在需要优化多个目标时,容易存在目标之间的冲突,一个目标的优化是以其他目标劣化为代价的,所以我们要…...

odoo的优势
plus,主要是为了能尽早通过开发者审核,加入到chatgpt4 api的开发中去,接入到我们odoo aiCenter中。4的回答,明显比3.5的更聪明了。 可能是由于国内的特殊情况吧,我们的chatgpt模块很受欢迎,我也被问了不少…...

Spring Boot(Vue3+ElementPlus+Axios+MyBatisPlus+Spring Boot 前后端分离)【三】
😀前言 本篇博文是关于Spring Boot(Vue3ElementPlusAxiosMyBatisPlusSpring Boot 前后端分离)【三】的分享,希望你能够喜欢 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我…...

Kali 软件管理
kali 更新 1. 查看发行版本 ┌──(root㉿kali)-[~] └─# lsb_release -a No LSB modules are available. Distributor ID: Kali Description: Kali GNU/Linux Rolling Release: 2023.2 Codename: kali-rolling2. 查看内核版本 ┌──(root㉿kali)-[~] └─…...

加油站【贪心算法】
加油站 在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。 给定两个整数数组 gas 和…...

java八股文面试[多线程]——死锁、活锁、饥饿
DCL双重锁:TODO 如何预防死锁: 如何查看线程死锁: 知识来源: 【2023年面试】描述一下线程安全活跃态问题,以及竞态条件_哔哩哔哩_bilibili 【2023年面试】如何预防死锁_哔哩哔哩_bilibili 【并发与线程】阿里一面&…...

设计模式——装饰器模式
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。 装饰器模式通过将对象包装在装饰器类中,以便动态…...

①matlab的命令掌握
目录 输入命令 命名变量 保存和加载变量 使用内置的函数和常量 输入命令 1.您可以通过在命令行窗口中 MATLAB 提示符 (>>) 后输入命令 任务 使用命令 3*5 将数值 3 和 5 相乘。 答案 3*5 2.除非另有指定,否则 MATLAB 会将计算结果存储在一个名为 ans…...

MySQL----索引
一、索引的概念 索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址(类似于c语言的链表通过指针指向数据记录的内存地址)。使用索引后可以不用扫描全表来定位某行的数据,而是先通过索引表找到该…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...