进程控制 | 认识fork函数 | 进程终止 | 进程等待

进程创建

初始fork函数

fork函数是为了创建子进程而生的,通过fork函数之后,我们的父进程的代码和数据是共享的,我们这里是可以通过man手册进行查询的,查询之后是可以发现fork函数是会返回两个值的至于为什么会返回两个值,我们下面来进行探讨。

首先在我们之前的文章里讲过,fork函数是会进行返回两个值的,这个值相当于给父进程进行写入的。而且我们如果通过打印的化不难发现父进程返回的值就是我们创建出来子进程的pid,而我们的子进程在fork函数之后返回的其实是0,我们之前解释过为什么fork函数之后是会返回两个值的。

解释原因:进程其实本质上就是内核的某种数据结构(地址空间(mm_struct)+ 页表 +PCB(Linux下是task_struct))+ 进程的代码和数据组成的,然后在我们父进程如果进行创建子进程的过程中,子进程的代码和数据其实就是继承父进程的代码和部分数据,这里我们需要注意的是子进程的代码和父进程的代码是共享的,但是我们子进程的数据和父进程的数据其实是独立的,这就造成我们的返回值就可能是两个,还有一个原因就是我们的数据大部分其实都是拷贝父进程的数据,但是有些数据会进行写实拷贝,啥是写时拷贝我后面再讲。

这样就确保我们每个进程都是独立的,也就导致最后我们的返回值是两个

所以fork函数之后我们的操作系统其实上做了四点

分配新的内存块和内核数据结构给子进程

将父进程部分数据结构内容拷贝至子进程
添加子进程到系统进程列表当中
fork 返回,开始调度器调度

 fork函数返回值的意义

子进程返回的就是0

父进程返回的就是子进程的pid

fork函数的常规用法

我们知道fork函数是为了创建子进程的,那就证明子进程肯定是为了给父进程分担一些任务的,比如父进程一写任务就会分给子进程,还有的时候子进程会会自己单独执行一个程序

fork函数失败

fork函数有的时候创建进程的时候也是会失败的,比如当我们的进程队列过多的时候,fork函数就是会失效,这和我们以前文章里不断进行malloc开空间是一样的道理,我们在堆上开空间的时候,本质就是给我们的程序创造空间,程序最后实际上就是一个一个的进程,所以fork函数也是会失败的。

所以fork 之后创建一批结构,代码以共享的方式,数据以写时拷贝的方式,两个进程必须保证 "独立性",做到互不影响。在这种共享机制下子进程或父进程任何一方挂掉,不会影响另一个进程。

写时拷贝

写时拷贝能让我们的进程具有独立性,其中最大的特点应该是发生在数据上的不同。

为了让大家更好的理解写时拷贝,我们先引出话题,我们为什么要进行写时拷贝。

解释:fork之后我们可以认为我们子进程是继承父进程的代码和数据,但是我们知道我们的进程是具有独立性的,看视两个相互矛盾的话题,这就引出了我们为什么要进行写时拷贝的原因了,我们如果为了确保独立性的化为什么不直接拷贝一份数据给子进程,父子进程的代码和数据都拷贝一份自己的不就行了

但是这就是会造成空间的浪费,我们子进程的大部分数据都是可以用父进程的,所以如果拷贝一份确实会浪费空间,我们这里也可以给出一个结论就是我们父子进程进行继承的时候,子进程是会拷贝一份父进程的“东西的”,他们分别就是task_struct + mm_struct(地址空间) + 页表 + 代码和数据。

所以就能很好的减少成本,提高我们操作系统的效率了。

总结

  • 有浪费空间之嫌:父进程的数据,子进程不一定全用;即便使用,也不一定全部写入。
  • 最理想的情况,只有会被父子修改的数据,进行分离拷贝。不需要修改的数据,共享即可。但是从技术角度实现复杂。
  • 如果 fork 的时候,单独生成一份子进程,会增加 fork 的成本(内存和时间)

所以这就是我们为什么要进行写时拷贝的原因,我们下面可以通过一些图片来了解写时拷贝到底是什么

了解写时拷贝的过程

我们写时拷贝的时候通过这个图就可以看到虚拟地址是不变的,但是通过页表进行映射的时候,在内存种指向的空间就会发生拷贝,我们可以认为新开出来的内存就是发生了写时拷贝的这样一个过程。

这里的写时拷贝的时候还是会发生一个小细节的,我们不要以为创建子进程之后马上就是会发生写时拷贝的,其实不是的,我们只会在我们要用这个的时候,才会进行使用,我们可以认为操作系统对我们这个过程是有延时的,因为我们可能不是马上就用这个空间,只是开了出来,这和我们C语言学的是一样的比如就是我们的malloc函数,我们使用的时候是不是先开出来空间,然后才是进行的使用,而不是开出来就马上进行使用,对吧!!!!

进程的终止

我们是可以给我们的进程分成三种情况的

第一种 :代码正常运行 代码跑完,结果也是正确的

第二种:代码运行,代码跑完之后,但是结果是不正确的

第三种:就是异常,如果在我们语言方面是可以讲成程序崩溃

下面有个问题,我们为什么要进行进程终止???进程终止究竟是在干嘛???

在这之前我们还要讲一个东西,我们都知道我们如果写一个代码的化,然后进行预处理,编译,汇编,链接这四步之后,我们会产生我们的可执行的程序,我们可执行程序如果要运行,就会形成进程,进程其实本质上就是内核的某种数据结构(地址空间(mm_struct)+ 页表 +PCB(Linux下是task_struct))+ 进程的代码和数据组成的,这我们上面是讲过的,但是这里我们还是需要想想我们是先将我们的代码和数据放在内存里,还是先创建task_struct和地址空间和页表呢??
我们可以这么想,在我们上大学的时候,我们填完志愿之后,学校录取你之后,是你马上就要去学校吗?我们肯定是会在要开学的时候才是会去学校的,这个时候我们的信息是不是已经在学校里了,但是我们人还是不需要去学校的,所以我们的进程就是先创建PCB和地址空间还有我们的页表。然后才是我们程序的代码和数据。

那话又说回来,进程终止到底是在干嘛呢,答案就是先释放我们的代码和数据,然后就是我们进程的内核的数据结构,释放的顺序大家一定要记住的,我们后面就会讲退出码和信号的时候,就会用到。

我们先来写一下世界上最简单的代码。

这么简单的代码我肯定是故意打错的。

我们来改一下我们的代码吧.

[tjl@hecs-67680 3_22]$ make
gcc -o myprocess myprocess.c
[tjl@hecs-67680 3_22]$ ./myprocess 
hello word
[tjl@hecs-67680 3_22]$ 

看完操作之后来看看我们的代码

#include <stdio.h>
int main()
{printf("hello word\n");return 0;
}

接下来我们需要思考的就是我们为什么每次代码都需要return 0 ,为什么不是return 100 这些,难道它这个返回值也是有不同的意义的。

进程的退出码

我们在我们主函数程序结束的时候会返回一个值,这个值就是我们的退出码。

退出码是很重要的,它主要就是给我们的父进程bash来看的,这也就有有了我们上面进程终止的三种情况。

第一种 :代码正常运行 代码跑完,结果也是正确的

第二种:代码运行,代码跑完之后,但是结果是不正确的

第三种:就是异常,如果在我们语言方面是可以讲成程序崩溃

echo $?

我们用这个指令就是可以查询我们的退出码了。

我们之前说过ls这些简单的指令也是一个进程,我们也可以查出他们的退出码。

[tjl@hecs-67680 3_22]$ ls adafa
ls: cannot access adafa: No such file or directory
[tjl@hecs-67680 3_22]$ echo $?
2
[tjl@hecs-67680 3_22]$ 

当我们输入一个不认识的指令,它也是会进行返回一个值的,所以这个值是有它自己的意思,我们可以来写一个代码,来查看这些退出码的信息,这个时候我们需要认识一个函数。

那我们来写一个循环的代码来进行查询。

0 :Success
1 :Operation not permitted
2 :No such file or directory
3 :No such process
4 :Interrupted system call
5 :Input/output error
6 :No such device or address
7 :Argument list too long
8 :Exec format error
9 :Bad file descriptor
10 :No child processes
11 :Resource temporarily unavailable
12 :Cannot allocate memory
13 :Permission denied
14 :Bad address
15 :Block device required
16 :Device or resource busy
17 :File exists
18 :Invalid cross-device link
19 :No such device
20 :Not a directory
21 :Is a directory
22 :Invalid argument
23 :Too many open files in system
24 :Too many open files
25 :Inappropriate ioctl for device
26 :Text file busy
27 :File too large
28 :No space left on device
29 :Illegal seek
30 :Read-only file system
31 :Too many links
32 :Broken pipe
33 :Numerical argument out of domain
34 :Numerical result out of range
35 :Resource deadlock avoided
36 :File name too long
37 :No locks available
38 :Function not implemented
39 :Directory not empty
40 :Too many levels of symbolic links
41 :Unknown error 41
42 :No message of desired type
43 :Identifier removed
44 :Channel number out of range
45 :Level 2 not synchronized
46 :Level 3 halted
47 :Level 3 reset
48 :Link number out of range
49 :Protocol driver not attached
50 :No CSI structure available
51 :Level 2 halted
52 :Invalid exchange
53 :Invalid request descriptor
54 :Exchange full
55 :No anode
56 :Invalid request code
57 :Invalid slot
58 :Unknown error 58
59 :Bad font file format
60 :Device not a stream
61 :No data available
62 :Timer expired
63 :Out of streams resources
64 :Machine is not on the network
65 :Package not installed
66 :Object is remote
67 :Link has been severed
68 :Advertise error
69 :Srmount error
70 :Communication error on send
71 :Protocol error
72 :Multihop attempted
73 :RFS specific error
74 :Bad message
75 :Value too large for defined data type
76 :Name not unique on network
77 :File descriptor in bad state
78 :Remote address changed
79 :Can not access a needed shared library
80 :Accessing a corrupted shared library
81 :.lib section in a.out corrupted
82 :Attempting to link in too many shared libraries
83 :Cannot exec a shared library directly
84 :Invalid or incomplete multibyte or wide character
85 :Interrupted system call should be restarted
86 :Streams pipe error
87 :Too many users
88 :Socket operation on non-socket
89 :Destination address required
90 :Message too long
91 :Protocol wrong type for socket
92 :Protocol not available
93 :Protocol not supported
94 :Socket type not supported
95 :Operation not supported
96 :Protocol family not supported
97 :Address family not supported by protocol
98 :Address already in use
99 :Cannot assign requested address
100 :Network is down
101 :Network is unreachable
102 :Network dropped connection on reset
103 :Software caused connection abort
104 :Connection reset by peer
105 :No buffer space available
106 :Transport endpoint is already connected
107 :Transport endpoint is not connected
108 :Cannot send after transport endpoint shutdown
109 :Too many references: cannot splice
110 :Connection timed out
111 :Connection refused
112 :Host is down
113 :No route to host
114 :Operation already in progress
115 :Operation now in progress
116 :Stale file handle
117 :Structure needs cleaning
118 :Not a XENIX named type file
119 :No XENIX semaphores available
120 :Is a named type file
121 :Remote I/O error
122 :Disk quota exceeded
123 :No medium found
124 :Wrong medium type
125 :Operation canceled
126 :Required key not available
127 :Key has expired
128 :Key has been revoked
129 :Key was rejected by service
130 :Owner died
131 :State not recoverable
132 :Operation not possible due to RF-kill
133 :Memory page has hardware error
134 :Unknown error 134
135 :Unknown error 135
136 :Unknown error 136
137 :Unknown error 137
138 :Unknown error 138
139 :Unknown error 139

这些都是我们的退出码代表的,我们来看看我们的代码是怎么写的。

#include <stdio.h>
#include <string.h>
int main()
{
//  printf("hello word\n");
//  return 0;int i = 0;for(i = 0; i < 255; i++){printf("%d :%s\n",i,strerror(i));}return 0;
}

所以通过打印退出码所对应的信息我们是不难发现0就是代表成功的意思,非0就是代表失败的意思,从1 ——139都有对应的错误对应信息。这些信息就是来提示用户你这里是有问题的。所以这也就是为什么父进程bash需要获取子进程的退出码的原因了。

那我们今天还是需要来了解kill的一些选项,也结合退出码更好的来判断信号。但是我们这里只是看效果,并不是学信号,信号的部分后面是会继续分享给大家的。

这个时候如果再来看我们的退出码就已经是没有用了,因为这个时候不是合理退出,我们的代码是没有跑完的,我们代码写的内容其实就是一个死循环,里面打印的就是当前进程的pid,我们来看看我们的代码然后在进行解释。

这个时候我们称作为异常,代码没有跑完,我们的程序就已经进行退出的时候,这个时候就称作为异常。

我们之前经常再语言层面对于异常来说的时候,就是指我们的程序崩溃了,但是现在在我们的操作系统的层面的时候我们其实就是可以认为进程被操作系统杀掉了。因为我们的进程做了不应该做的事,然后被操作系统知道之后进行拦截了。

如果大家在牛客网上之前是写过代码的话,有时候报错的时候就会显示段错误,这个时候也就是我们的进程做了不该做的事情导致的段错误,这个时候我们的退出码就是没有意义了。

退出码有的时候也不一定是操纵系统给我们安排的,因为我们也可以使用自定义的退出码。

所以我们看我们进程退出的时候,可以先来看我们的进程信号,先检查是否是异常导致的,然后再来看我们的退出码。

总结 :衡量一个进程是否正确,只需要来看它的退出信号和退出码就可以了。

再来分享一个知识点,就是我们的子进程再退出的时候,我们是先把它的代码和数据销毁,然后在是内核数据的结构,这个时候,我们的子进程处于一种僵尸进程,他是需要等待父进程来进行回收的,所以就是会保留退出码和退出信号,等着父进程来进行回收的。

如何终止我们的进程??

我们可以用C语言的库函数来进行终止我们的程序。

exit函数

这就是我们exit函数,中间的参数就是我们的退出码,没错,因为退出码是可以自定义的,所以我们也能进行对我们的退出码进行修改。

exit表示进程的终止。直接退出。

下面来看看代码和具体效果。

那还是有一个函数就是_exit,其实他们两个没有什么不同,唯一不同的就是_exit是系统接口。我们来继续写一个函数可以来对比一些下他们的不一样的地方。

 这两个图其实就是可以说明了,因为我们的代码没有加上\n,所以是不会刷新缓冲区的,但是等到代码结束的时候就是会强制刷新的,然后_exit是没有进行刷新,(因为我们之前也是讲过我们的不能直接访问操作系统,而是会通过系统接口进行访问的,然后库函数exit其实还是会去调用_exit这个系统接口的),那么这里其实就是告诉大家我们的内存不是在系统中的,在哪里后面继续讲。先埋下伏笔。

进程等待

我们上来就给出结论,结论就是我们的父进程是会等待子进程的退出的!!!!

如果子进程退出的时候,父进程不进行等待,就会导致子进程一直处于僵尸进程,我们知道僵尸进程是会一直存在的,我们也不能用kill进行杀掉,僵尸进程需要等待父进程来进行回收,父进程回收的话需要收集子进程的退出信息。

那么我们为什么要进行进程等待呢,如果不发生进程等待是会发生什么呢?

父进程通过等待就可以解决子进程的信息,会进行资源的回收(一定要进行考虑的)

获取子进程的退出信息(退出信号和退出码)(不一定要进行考虑的)

那我们这里需要来了解系统调用的函数,分别是wait/waitpid,我们先来看看wait,但是我们主要还是看waitpid。先来写我们的代码。

可以看到我们的效果是这样的,的确发现父进程就是在等待我们的子进程,而且等待过程中子进程是处于僵尸进程。

#include <stdio.h>
#include <unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/wait.h>
void ChildRun()
{int cnt = 5;while(cnt){printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);sleep(1);cnt--;}
}int main()
{printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());pid_t id = fork();if(id == 0){// childChildRun();printf("child quit ...\n");exit(123);}sleep(7);// fahterpid_t rid = wait(NULL);
//     int status = 0;// pid_t rid = waitpid(id, &status, 0);if(rid > 0){printf("wait success, rid: %d\n", rid);}else{printf("wait failed !\n");}sleep(3);return 0;
}

所以这就是wait的用法,这个就可以说明我们的父进程是会等待子进程的,这里我们需要再进一步的解释一个问题,就是父进程在等待子进程的过程中,父进程是一直等待的,只有等子进程结束之后,那么父进程也就真正的结束掉了,因为父进程是需要收集子进程的退出信息的。

下面我们就来解释一下waitpid是个怎么样子的。

我们可以来看看waitpid的参数

第一个其实是子进程的pid,第二个就是我们的输出型的参数,是和我们之前认识scanf函数是一样的。然后就是来看看使用。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>void ChildRun()
{int *p = NULL;int cnt = 5;while(1){printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);sleep(1);cnt--;*p = 100;}
}int main()
{printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());pid_t id = fork();if(id == 0){// childChildRun();printf("child quit ...\n");exit(123);}sleep(7);// fahter//pid_t rid = wait(NULL);int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){printf("wait success, rid: %d\n", rid);}else{printf("wait failed !\n");}sleep(3);printf("father quit, status: %d, child quit code : %d, child quit signal: %d\n", status, (status>>8)&0xFF, status & 0x7F);
}

 

wait waitpid ,都有一个 status 参数,该参数是一个输出型参数,由操作系统填充。
如果传递 NULL ,表示不关心子进程的退出状态信息。
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
status 不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究 status 16 比特 位)
那今天分享的内容就到这里,我们下次再见

 

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

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

相关文章

牛,The O-one ——通过语音交互控制电脑的开源语言模型

模型介绍 The O-one &#xff1a;一个创新的开源语言模型计算机 可以让你通过语音交互来和你的计算机进行对话&#xff0c;完成询问、指令下达等任务。灵感居然来自Andrej Karpathy 的 LLM 操作系统。O1运行一个代码解释语言模型&#xff0c;并在计算机内核发生特定事件时调用…

社科赛斯考研:二十二载岁月铸辉煌,穿越周期的生命力之源

在考研培训行业的浩瀚海洋中&#xff0c;社科赛斯考研犹如一艘稳健的巨轮&#xff0c;历经二十二载风礼&#xff0c;依然破浪前行。在考研市场竞争白热化与学生对于考研机构要求越来越高的双重影响下&#xff0c;社科赛斯考研却以一种分蘖成长的姿态&#xff0c;扎根、壮大&…

六、C#快速排序算法

简介 快速排序是一种常用的排序算法&#xff0c;它基于分治的思想&#xff0c;通过将一个无序的序列分割成两个子序列&#xff0c;并递归地对子序列进行排序&#xff0c;最终完成整个序列的排序。 其基本思路如下&#xff1a; 选择数组中的一个元素作为基准&#xff08;pivot…

基于python+vue共享单车信息系统的设计与实现flask-django-php-nodejs

课题主要分为二大模块&#xff1a;即管理员模块和用户模块&#xff0c;主要功能包括&#xff1a;用户、区域、共享单车、单车租赁、租赁归还、报修信息、检修信息等&#xff1b;快速发展的社会中&#xff0c;人们的生活水平都在提高&#xff0c;生活节奏也在逐渐加快。为了节省…

实在智能受邀参加中国人工智能产业发展联盟大会(AIIA)主题分享,共筑智能体机遇新篇章

近日&#xff0c;中国人工智能产业发展联盟&#xff08;AIIA&#xff09;在海口召开第十一次全体会议&#xff0c;作为该联盟成员单位&#xff0c;实在智能合伙人&核心算法负责人欧阳小刚受邀出席大会&#xff0c;并以《从RPA到智能体&#xff0c;数字员工的发展及在金融行…

《逆水寒》“公费追星”被骂上热搜,玩家为何如此抗拒剧游联动?

游戏行业最近真是风波不断。 《逆水寒》手游因为和武侠剧《莲花楼》深入联动而遭到玩家抵制&#xff0c;网易游戏测评总监被质疑“公费追星”&#xff0c;还波及到了成毅、陈都灵等多位演员。 尤其是《莲花楼》的男主角成毅&#xff0c;遭到大量《逆水寒》手游玩家的吐槽调侃…

微信向量检索分析一体化数仓探索:OLAP For Embedding

作者&#xff1a;WeOLAP 团队 数据挖掘团队 擅长 OLAP 分析的 ClickHouse 不仅可以用于 vector search&#xff0c;还可承担起整条 embedding 的加工处理工作,All in one Pipeline 也让速度远超传统批处理框架数倍&#xff1b;检索性能虽无法与专业 sim 检索服务相媲美&#xf…

Docker 【安装MongoDB】

文章目录 前言一、安装二、使用三、mongodb常用指令总结 前言 MongoDB是一个非关系型数据库&#xff0c;它主要的应用场景有这些 显示 相比mysql&#xff0c;MongoDB没有事务&#xff0c;索引之类的东西。最小单位是文档。 可能有人说&#xff0c;为什么这个场景我要用mongo…

MO尺度(大气边界层)

在大气表面层( atmospheric surface layer)中,MO参数是用来决定流动是中性或者非中性的一个重要参数。其定义是 z / L z/L z/L&#xff0c;其中 L L L为Obukhov长度&#xff0c;其含义是浮力产生的湍动能和剪切产生的湍动能之比(Hj h AIP 2023)(Monin IAS,1954)&#xff0c;具体…

文件怎么做扫码预览?创建文件活码的步骤有哪些?

现在文件可以通过扫描二维码的方式来获取&#xff0c;与传统的通过聊天软件来传输相比&#xff0c;二维码方式的应用更加的方便&#xff0c;其他人只需要通过扫描一张二维码就可以在手机上浏览或者下载文件&#xff0c;通过手机就可以预览、存储。 文件二维码的制作方法也很简…

“龙腾九洲———孙文佳师生书画作品国际交流展”首展成功举行

应美国纽约罗切斯特大学、美国满煜国际文化艺术交流公司的邀请&#xff0c;“龙腾九洲———孙文佳师生书画作品国际交流展”首展开幕式于2024年1月23日上午12时在美国纽约罗切斯特大学西蒙商学院会展中心举行。 开幕式上&#xff0c;罗切斯特大学负责人Priya和美国满煜文化公司…

第1讲-introduction(4)ALU

ALU(Arithmetic and logic unit) ALU的核心部件是加法器。 算术逻辑单元(Arithmetic Logic Unit,简称 ALU)是中央处理器(CPU)的执行单元,是所有中央处理器的核心组成部分。它负责执行所有的算术和逻辑运算,例如加减乘除、与或非等操作。 ALU的主要功能包括: 1. 执…

重学SpringBoot3-Profiles介绍

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-Profiles介绍 Profiles简介如何在Spring Boot中使用Profiles定义Profiles激活ProfilesIDEA设置active profile使用Profile-specific配置文件 条件化Bean…

Unity-UGUI系统

UGUI是什么 UGUI是Unity引擎内自带的UI系统官方称之为:Unity Ul 是目前Unity商业游戏开发中使用最广泛的UI系统开发解决方案 它是基于Unity游戏对象的UI系统&#xff0c;只能用来做游戏UI功能 不能用于开发Unity编辑器中内置的用户界面 六大基础组件 概述 Canvas EventS…

想做【数据分析师】?需要什么条件?

进入数据时代&#xff0c;数据分析师成为各大企业争先抢夺的主要人才之一。数据分析师是在不同行业中&#xff0c;专门从事行业数据收集、整理、分析并且依据相关的数据做出行业研究、评估和预测的专业人员。那做数据分析师需要满足什么条件呢&#xff1f; 首先我们要看一下数据…

性能测试如何定位分析性能瓶颈?

对于一般公司普通测试工程师来说&#xff0c;可能性能测试做的并不是很复杂&#xff0c;可能只是编写下脚本&#xff0c;做个压测&#xff0c;然后输出报告结果&#xff0c;瓶颈分析和调优的事都丢给开发去做。 在一些大厂都有专门的性能测试团队去定位分析系统性能瓶颈&#…

流畅切换Linux的应用程序

流畅切换Linux的应用程序 流畅切换Linux的应用程序一.Linux启动一个程序在后台执行1. 使用nohup和&&#xff1a;2. 使用ctrlZ&#xff1a;3.使用screen&#xff1a;3.1 创建会话3.2 要重新连接到此会话&#xff1a;3.3 中途退出会话&#xff0c;但程序继续运行:3.4 结束一个…

第十节HarmonyOS 常用容器组件3-GridRow

1、描述 栅格容器组件&#xff0c;仅可以和栅格子组件&#xff08;GridCol&#xff09;在栅格布局场景中使用。 2、子组件 可以包含GridCol子组件。 3、接口 GridRow(options:{columns: number | GridRowColumnOption, gutter?: Length | GutterOption, Breakpoints?: B…

把 Taro 项目作为一个完整分包,Taro项目里分包的样式丢失

现象&#xff1a; 当我们把 Taro 项目作为原生微信小程序一个完整分包时&#xff0c;Taro项目里分包的样式丢失&#xff0c;示意图如下&#xff1a; 原因&#xff1a; 在node_modules/tarojs/plugin-indie/dist/index.js文件里&#xff0c;限制了只有pages目录下会被引入app.w…

Springboot实现qq邮件的发送

一、打开必要的邮件设置 首先登录qq邮箱官网登录之后&#xff0c;在设置中将传输协议给打开&#xff0c;我们需要用这个秘钥作为发件人的邮箱授权。 这里开启之后&#xff0c;记住这个秘钥。 二、代码编写 首先我们将作为发送邮件的账户信息写入配置文件。 spring:mail:hos…