【linux】信号——信号产生

news2024/11/27 19:52:28

信号产生

  • 1.预备知识
  • 2.信号产生
    • 2.1通过键盘发送信号
    • 2.2系统调用接口向进程发送信号
    • 2.3硬件异常产生信号
    • 2.4软件条件
    • 2.5总结

自我名言只有努力,才能追逐梦想,只有努力,才不会欺骗自己。在这里插入图片描述
喜欢的点赞,收藏,关注一下把!在这里插入图片描述

首先说明一点信号不是信号量。不能把这两个东西放在一起。

那信号讲什么呢?
在这里插入图片描述

1.预备知识

那信号是怎么回事,这里只能这样说,信号是针对进行发送某种信号到来的一种机制,让信号能被进程处理。,让我们在后面的知识中,更能理解这句话的含义。

先见识见识信号。

在这里插入图片描述
前面数字是信号的编号,后面大写的是宏。
就比如杀死一个进程

kill -9  进程pid

这里可以使用编号,也可以使用SIGKILL

再可以数一数信号有多少个。
其实并没有64个,0,32,33信号是没有的。
【1,31】普通信号
【34,64】实时信号 (我们不学这个)

先说信号的概念帮我们简单了解一下信号,但再说信号一些概念之前,我们先从生活角度中的信号来帮我们理解。

生活中的信号:
1.红绿灯
2.闹钟
3.信息通知
4.劳资蜀道三
5.女朋友把你拉黑
6.烽火台狼烟
等等这些都是我们生活中的信号。

我们以红绿灯为例。人是能够识别红绿灯的。
这里识别有两层意思。
在这里插入图片描述

第一个问题可能会觉得很奇怪,你为什么能够识别红绿灯?

在这里插入图片描述

第二个问题,当信号来的时候,你不一定会立即处理这个信号

在这里插入图片描述

信号的产生是异步的。

举个栗子,你正在宿舍打着游戏,这时外卖小哥给你打电话让你下楼取餐,但是你忙着打着游戏并没有立刻下楼去取,而是让他把外卖放在楼下。当你打完游戏,记起还有外卖没拿,所以去楼下拿外卖。当然还有另一种情况,你打游戏上头了。然后忘记有外卖在楼下这回事。

在这里插入图片描述

当绿灯到达的时候,你有三种处理动作

在这里插入图片描述

如何把上面的这些概念迁移到进程中呢?

这里要有一个共识:信号是给进程发的

  1. 进程是如何识别信号的?(认识+动作)
    进程本身就是程序员编写的属性和逻辑的集合。所以这里先粗略的说是由程序员编码完成的。(后面学了信号更多知识就可以详细说明了)。

  2. 当进程收到信号的时候,进程可能正在执行更重要的代码,所以信号不一定会被立即处理

  3. 进程本身必须要有对于信号的保存能力

  4. 进程在处理信号的时候,一般有三种动作(默认,自定义,忽略)【信号被捕捉】

在我们现在还没有学过信号,上面1,2,4我们都不能具体解释,不过3我们可以根据以往学过的知识来分析分析。

如果一个信号是发给进程的,而进程要保存,那么应该保存在哪里?
task_struct(PCB)结构体中。

如何保存呢? 更准确来说如何保存是否收到了指定信号【1,31】。
是否是一种两态,我们是不是可以在task_struct结构体里,当然task_struct结构体中包含其他一大堆的属性,可以存在一个unsigned int signal32位比特位。

在这里插入图片描述
所以在进程中是不是只要存在对应的位图结构,然后当我们收到信号时,是不是只要将对应信号的位置由0->1,就代表我们已经完成了信号的发送,并且让进程暂时把这个信号保存起来了。

那如何理解信号的发送呢?
发送信号的本质:修改PCB的信号位图!
PCB是内核维护的数据结构对象----->PCB的管理者是OS,谁有权力修改PCB中的内容呢?------>OS!!

所以无论未来我们学习多少种发送信号的方式,本质都是通过OS向目标进程发送的信号!!
未来想让用户也能发送信号------->OS必须要提供发送信号,处理信号的相关系统调用!

我们使用kill命令,底层一定调用了对应的系统调用!

2.信号产生

2.1通过键盘发送信号

int main()
{
    int cnt=0;
    while(true)
    {
        printf("我是一个进程,我正在运行%d\n",cnt++);
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
ctrl+c热键,终止前台进程。
本质ctrl+c是一个组合键---->OS识别---->OS将ctrl+c解释成为2号信号,2)SIGINT----->处理(三种动作)。但我们对2号信号没做任何改变,所以是默认处理。

man 7 signal //查看信号对应的手册

在这里插入图片描述

在这里插入图片描述
Action(行为):Term (Terminal终端)结束进程
Comment(解释):从键盘中断

所以2号信息的默认动作,结束进程。

接下来验证一下是不是发送了2号信号。

先介绍一个函数signal对指定的信号设置一个自定义动作。
在这里插入图片描述
signum:信号编号(捕捉那个信号)
handler:函数指针(捕捉这个信号后,你想怎么做,这是一个回调函数)

接下来验证

#include<iostream>
#include<unistd.h>
#include<cstdio>
#include<signal.h>

void handler(int signo)
{
    cout<<"捕捉到信号:"<<signo<<endl;
}

int main()
{
    signal(2,handler);
    int cnt=0;
    while(true)
    {
        printf("我是一个进程,我正在运行%d\n",cnt++);
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
我不是对2号信号进行捕捉吗,并且代码还做了修改,为什么运行结果没什么变化。

注意,这里是signal函数的调用,并不是handler的调用,并且仅仅是设置了对信号的捕捉方法,并不代表方法被调用了。所以一般这个方法不会执行,除非收到对应的信号。

在这里插入图片描述

在这里插入图片描述
这两种方法都可以发送信号。然后signal函数对2号信息进行捕捉。

在这里插入图片描述

现在发2号信号,虽然能被捕捉,但是进程怎么退不出来了。
这是因为我们把2号信号默认动作,改成了自定义动作。
在这里插入图片描述
如果想退出怎么办?

kill -9 编号  //杀死进程

在这里插入图片描述
在这里插入图片描述
或者在自定义动作种加一个exit。

void handler(int signo)
{
    cout<<"进程捕捉到了一个信号,信号编号是:"<<signo<<endl;
    exit(0);
}

在这里插入图片描述

其实还有一个组合建ctrl+\,发送的是3号信号。也能终止进程。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里留了一个问题,Core和Trem都是终止进程。为什么OS要设置两种不同的行为有什么用?

2.2系统调用接口向进程发送信号

在这里插入图片描述

kill可以给任意进程发送任意信号。

pid:目标进程pid
sig:发送几号信号
在这里插入图片描述
成功返回0,识别返回-1。
我们给进程发信号底层用的就是这个。

前面说过,信号是由OS向进程发送的。OS有这个能力,但不代表有权限使用这个能力。
信号的发送是由用户发起而OS执行的。

接下来我们写的代码想呈现这样的效果,一个进程正在运行,另一个进程在命令行给这个进程发送任意信号。

//mysignal.cc

#include<iostream>
#include<unistd.h>
#include<cstdio>
#include<signal.h>
#include<sys/types.h>
#include<string>

using namespace std;


void Usage(const string& proc)
{
    cout<<"\nUsage "<<proc<<" pid signo\n"<<endl;
}

// ./mysignal pid signo------>命令行参数
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    
    pid_t id=stoi(argv[1]);
    int signo=stoi(argv[2]);
    int n=kill(id,signo);
    if(n != 0)
    {
        perror("kill");
    }

    return 0;
}

//mytest.cc

#include<iostream>
#include<unistd.h>

int main()
{
    int cnt=0;
    while(true)
    {
        printf("我是一个进程,pid:%d,我正在运行%d\n",getpid(),cnt++);
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
在这里插入图片描述
这个不就是和我们在命令行执行kill命令一样的原理吗。

kill(),可以向任意进程发送任意信号

raise,给自己发送指定信号。----->就相当于 kill(getpid(),任意信号)

在这里插入图片描述

int main()
{
    int cnt=0;
    while(true)
    {
        printf("cnt:%d,pid:%d\n",cnt++,getpid());
        if(cnt == 10)
            raise(9);
        sleep(1);
    }
        return 0;
}

在这里插入图片描述

abort,给自己发送指定的信号(6号信号)。------>相当于kill(getpid(),SIGABRT)

在这里插入图片描述
在这里插入图片描述

int main()
{
    int cnt=0;
    while(true)
    {
        printf("cnt:%d,pid:%d\n",cnt++,getpid());
        if(cnt == 10)
           abort();
        sleep(1);
    }
        return 0;
}

在这里插入图片描述

关于信号处理的行为的理解:有很多的情况,进程收到大部分的信号,默认处理动作都是终止进程。

既然大部分信号默认都是终止进程,那有那么多类的信号有什么用?

信号的意义:信号的不同,代表不同的事情,但是对事情发生之后的处理可以一样。
也就是说进程意外终止了,我们可以根据信号不同来确定是什么原因导致的。

2.3硬件异常产生信号

信号产生,不一定非得是用户显示发送的。

看下面一段代码

int main()
{
    int cnt=0;
    while(true)
    {
         printf("cnt:%d,pid:%d\n",cnt++,getpid());
         int a=10;
         a/=0;       
    }
    return 0;
}

在这里插入图片描述

为什么除0会终止进程?
因为当前进程会收到来自OS发送的信号。SIGPFE。
在这里插入图片描述
在这里插入图片描述

如何证明呢?

void handler(int signo)
{
    cout<<"进程捕捉到了一个信号,信号编号是:"<<signo<<endl;
}

int main(int argc,char* argv[])
{
    signal(SIGFPE,handler);
    int cnt=0;
    while(true)
    {
         printf("cnt:%d,pid:%d\n",cnt++,getpid());
         int a=10;
         a/=0;       
    }
     return 0;
}

在这里插入图片描述
我确实捕捉到了8号信号,但为什么OS一直发送信号呢?
在这里插入图片描述
难道是我这里一直在死循环的原因?

修改一下代码

int main()
{
    signal(SIGFPE,handler);
    int cnt=0;
    int a=10;
    a/=0;   
    while(true)
    {
        printf("cnt:%d,pid:%d\n",cnt++,getpid());
    }
    return 0;
}

在这里插入图片描述
发现还是一直在发送8号信号。
这到底是为什么?

先来解答,OS如何得知应该给当前进程发送8号信号呢?或OS怎么知道我除0了呢?

在这里插入图片描述
CPU运算异常了,OS会不会知道?
OS肯定会知道CPU运算出现了问题,因为OS是软硬件资源的管理者。

OS查看到状态寄存器溢出位由0->1,OS就识别到CPU内部出错了。
谁导致CPU出错了?
CPU当前正在调度谁,就是那个进程出现了问题,OS向目标进程发送8号信息,目标进程收到8号信号,后序处理就会终止自己了。

那为什么一直发信息呢?
收到信号,不一定会引起进程退出,没有退出,进程可能还会被CPU调度。
CPU内部的寄存器只有一份,但是寄存器种中的内容,属于当前进程的上下文,CPU内部状态寄存器溢出标记位由0->1,你是没有能力或者动作去修改这个问题的。
当进程被切换的时候,就有无数次状态寄存器被保存和恢复的过程,所以每一次恢复的时候,就让OS识别到了CPU内部的状态寄存器中标记位是1,每一次都会发8号信号。

再看一种由硬件异常产生的信号。

int main()
{
    signal(SIGFPE,handler);
    int cnt=0; 
    while(true)
    {
        printf("cnt:%d,pid:%d\n",cnt++,getpid());
        int* ptr=NULL;
        *ptr=10;
    }
    return 0;
}

在这里插入图片描述

为什么野指针就奔溃了?
因为OS会给当前进程发送指定的11号信号。

在这里插入图片描述
在这里插入图片描述

证明一下。

void handler(int signo)
{
    cout<<"进程捕捉到了一个信号,信号编号是:"<<signo<<endl;
}


int main()
{
    signal(11,handler);
    int cnt=0; 
    while(true)
    {
        printf("cnt:%d,pid:%d\n",cnt++,getpid());
        int* ptr=NULL;
        *ptr=10;
    }
    return 0;
}

在这里插入图片描述

OS怎么知道我野指针了呢?

在这里插入图片描述
根据我们以前学的知识,虚拟地址—>物理地址的转换,要经过页表。今天我要告诉你除了页表还有一种硬件MMU。MMU是内存管理单元。
在这里插入图片描述
MMU其实是通过读取页表中的内容,在内部形成对应的物理地址,然后再去访问我们对应的物理地址。
在这里插入图片描述
当我们ptr解引用,访问的是0号地址。经过页表映射,发现在映射的时候,当前进程是不允许去访问对应的0号地址的。不允许访问当然可以拦截不让你访问。但更重要的是,你为什么会访问,所以OS觉得你犯错了就应该付出相应的代价,所以MMU这个硬件因为对应的越界访问(野指针访问)发送异常。OS知道当前硬件发生异常,所以OS将异常转换成11号信号发送给目标进程。

2.4软件条件

在管道我们说过匿名管道的一个场景,读端关闭,写端一直写没有任何意义,OS会给当前写进程发送SIGPIPE信号,然后进程终止了。

所谓的进程,OS,管道,尤其是管道和这一整套OS发信号的原因和OS发信号的过程,和硬件都没有关系。而是仅仅因为读端关闭了这一软件条件所触发的OS发送信号给目标进程,这种场景我们就称之为软件条件会触发信号。
在这里插入图片描述

下面我们要说的是一种定时器软件条件。给当前进程设定闹钟,alarm()

在这里插入图片描述
设置一个时钟时刻发送信号。

seconds:多少秒之后发送信号
返回值是0或者是以前设定的闹钟时间还余下的秒数

在这里插入图片描述
发送的是SIGALRM(14)信号。

int main()
{
	//这个闹钟是给现在设的还是给未来设的?
	//是不是我调用了alarm,我的进程会立马收到对应的闹钟呢?
	//答案:并不是。这是给未来设置的闹钟。是1秒之后向我这个进程发信号。
    alarm(1);
    int cnt=0;
    while(true)
    {
        printf("cnt: %d\n",cnt++);
    }
    return 0;
}

在这里插入图片描述

根据运行结果,请问我们这段代码有什么用呢?

其实这是统计1S左右,我们计算机能够将数据累计多次次。

修改一下代码再看一下效果。

int cnt=0;

void catchSig(int signo)
{
    cout<<"进程捕捉到了一个信号,信号编号是:"<<signo<<" cnt :"<<cnt<<endl;
}

int main()
{
    signal(SIGALRM,catchSig);
    alarm(1);
    while(true)
    {
        cnt++;
    }
    return 0;
}

在这里插入图片描述
次数多了很多次,这是因为printf会访问外设,而访问外设比较慢。
还有就是这个闹钟是一次性闹钟,响了之后就不响了。

如果想响多次,要重新在设定闹钟。

void catchSig(int signo)
{
    cout<<"进程捕捉到了一个信号,信号编号是:"<<signo<<" cnt :"<<cnt<<endl;
    alarm(1);
}

在这里插入图片描述

alarm(0),取消闹钟,并且返回闹钟剩下多少时间。

int main(int argc,char* argv[])
{
    signal(SIGALRM,catchSig);
    alarm(5);
    while(true)
    {
        cnt++;
        if(cnt == 3)
        {
        	int n=alarm(0);
        	cout<<n<<endl;
        }
        sleep(1);
    }
    return 0;
}

在这里插入图片描述

为什么设闹钟就是软件条件了呢?
"闹钟"其实就是用软件条件实现的。

在这里插入图片描述

2.5总结

1.上面所说的所有信号产生,最终都要有OS来进行执行,为什么?

OS是进程的管理者

  1. 信号的处理是否是立即处理的?

在合适的时候(什么合适的时候,下面说)。

3.信号如果不是被立即处理,那么信号是否需要暂时被进程记录下来?记录在哪里最合适呢?
4.一个进程在没有收到信号的时候,能否能知道,自己应该对合法信号作何处理呢?
5.如何理解OS向进程发送信号?能否描述一下完整的发送处理过程?

上面的问题,我们都可以从接下来信号的学习中得到答案。

信号产生这里还有最后一个问题。

man 7 signal  //信号手册

在这里插入图片描述
Stop暂停进程,Cont继续进程,Ign忽略进程(这个信号说完最后面解释)这些都没有问题。
Term,Core都是终止进程,有什么区别?
其实这有关于,进程退出时,核心转储问题

看下面一段代码

int main()
{
    //核心转储
    while(true)
    {
        int a[10];
        a[100]=10;
    }
    return 0;
}

在这里插入图片描述

int main()
{
    //核心转储
    while(true)
    {
        int a[10];
        a[1000]=10;
    }
    return 0;
}

在这里插入图片描述
数组明明都越界了啊,怎么进程没有奔溃报错?
其实在C的时候就说过,数组越界不一定会报错,因为对数组的检查是随机的。这是我们在语言层面的理解。

int main()
{
    //核心转储
    while(true)
    {
        int a[10];
        a[10000]=10;
    }
    return 0;
}

在这里插入图片描述
那这次怎么就检测出来了。按照语言层面解释可能是因为这次越界被检测到了。

接下来我们从底层理解:

编译器上编译你的代码时,在栈上给你开辟多大空间和编译器是强相关的,你要申请10个int大小元素的数组,它确实给你的就是10个元素,指的是数组的元素,但是并不代表给你的代码块或者函数分配栈帧结构是10个元素的大小,可能给你的会很大,所以呢,即便你越界了,但是你还是在有效栈区里,所以没有报错,除非你访问了一个完全不是你的空间。比如你现在访问的时候,访问的是系统的地址空间中或者访问到一个不让你访问的区域,那么此时OS系统就能识别出来。所以OS在识别越界的问题上有可能也死别不出来,从而出现把数据改变了,但用户不知情的情况。

在这里插入图片描述
这个信号是11号信号,段错误,它的终止方式是Core。
像Trem这种结束,是正常结束,OS不会做额外操作的。而以Core这种结束,OS除了终止进程,它还要做其他工作。

但是以Core为终止,我也没见OS做什么额外工作啊, 除了给我打印出一个错误描述,像Trem终止进程不也是给我打印出一个错误描述吗。

在云服务器上,默认如果进程是Core退出的,我们暂时看不到明显现象,如果想看到需要打开一个选项。

ulimit -a  //可以看到系统给我们当前资源设置的上限

在这里插入图片描述
core file size 大小为0,这是云服务器默认关闭了core file选项。
想要打开,ulimit就带上你想要设置谁,-c(选项),大小为多少。

ulimit -c 1024  //打开云服务器core file选项,默认可以向OS中形成最大为1024个block的数据块

然后运行同样的代码
在这里插入图片描述
相比较我们之前运行的时候,除了段错误后面还跟了一个(core dumped)
在这里插入图片描述
发现我们当前目录下多了一个以core命名的文件。
在这里插入图片描述
所谓的核心转储:当进程出现异常的时候,我们将进程在对应的时刻,在内存中的有效数据(二进制数据)转储到磁盘中。

该文件我们用vim打开是一堆乱码。我们是无法识别的。
那形成核心转储有什么意义呢?或者说为什么要有核心转储?

一般进程在运行的时候出现崩溃,其实我们更想知道的是,为什么会崩溃,在哪里崩溃。所以OS为了便于我们后期做调试,会将进程在运行期间出现崩溃的代码的相关上下文数据全部dump到磁盘中,用来进行支持调试

如何支持呢?
linux下默认编译都是release不能调试,debug才能调试,因此我们编译时带上-g选项。
在这里插入图片描述

在这里插入图片描述
当前自动帮我们评判,进程收到11号信号引起的段错误,报错是在mysignal.cc的第37行,代码是a[10000]=10引起的错误。直接就帮我们找到了错误。
这种直接快速定位到出问题的方式,我们称之为事后调试。

在这里插入图片描述

在这里插入图片描述
像这种以2号信号,Trem终止进程,并不会在当前目录下形成core文件。

Trem,Core都是进程终止,它们的区别是,以Core退出的可以被核心转储的以便于后序快递定位问题,以Trem退出就是正常终止进程。

以后进程出现异常退出,你可以查看是什么信号的什么行为导致的,如果是Core,把Core打开,再执行一下,gdb快速定位问题。

信号产生到目前为止差不多讲完了,但这里可能有人会有这样的疑问。如果我们把所有信号都捕捉,换成自定义动作,不让进程退出,那进程是不是无法被杀死了。

void catchSig(int signo)
{
    cout<<"进程捕捉到了一个信号,信号编号是:"<<signo<<endl;
}

int main()
{
    for(int signo=1;signo<=31;++signo)
    {
        signal(signo,catchSig);
    }
    while(true) sleep(1);
    
    return 0;
}

在这里插入图片描述
难道真的无法杀死了?
在这里插入图片描述
kill -9还是可以杀死进程,无论你怎么修改,无法对9号信号设定捕捉,即使你做了,OS也不会给你设置。

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

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

相关文章

区分(GIOU、DIOU、CIOU)(正则化、归一化、标准化)

一、IOU IoU 的全称为交并比&#xff08;Intersection over Union&#xff09;。IoU 计算的是 “预测的边框” 和 “真实的边框” 的交集和并集的比值。 1.GIOU&#xff1a;预测框&#xff08;蓝框&#xff09;和真实框&#xff08;绿框&#xff09;的最小外接矩形C。来获取预…

没想到吧!成功的图标设计,只需遵循这几个原则

图标在任何用户界面环境中都是不可或缺的元素。虽然许多图标小到可能被忽视&#xff0c;但它们在解决设计难题和用户体验问题上却起着决定性的作用。作为一名UI设计师&#xff0c;你必须要掌握的基本技巧之一就是图标设计。理解并应用图标设计的原则不仅可以帮助设计师快速定位…

如何有效地开发客户关系?

如何有效地开发客户关系&#xff1f; 有效地开发客户关系&#xff0c;是企业在竞争激烈的市场中获得优势的关键。通过深入了解客户需求、提供优质的产品和服务、建立良好的沟通渠道、提供个性化的体验以及建立长期合作关系等方式&#xff0c;企业可以有效地开发客户关系&#…

CSS特效020:涌动的弹簧效果

CSS常用示例100专栏目录 本专栏记录的是经常使用的CSS示例与技巧&#xff0c;主要包含CSS布局&#xff0c;CSS特效&#xff0c;CSS花边信息三部分内容。其中CSS布局主要是列出一些常用的CSS布局信息点&#xff0c;CSS特效主要是一些动画示例&#xff0c;CSS花边是描述了一些CSS…

WebUI自动化学习(Selenium+Python+Pytest框架)004

接下来&#xff0c;WebUI基础知识最后一篇。 1.下拉框操作 关于下拉框的处理有两种方式 &#xff08;1&#xff09;按普通元素定位 安装普通元素的定位方式来定位下拉框&#xff0c;使用元素的操作方法element.click()方法来操作下拉框内容的选择 &#xff08;2&#xff09…

Java容器合集

目录 浅谈 Array数组 初始化(动与静) 动态初始化 静态初始化 CRUD 增 查 索引取值 遍历 改 删 走进底层 栈与堆 一个数组的诞生 多数组 避坑指南 索引越界 空指针异常 小试牛刀 Collection List部落 介绍和特点 方法 ArrayList 介绍 方法 遍历 Li…

武汉凯迪正大KDZD5289硫化曲线测试仪(电脑无转子硫化仪)

电脑无转子硫化仪 硫化时间测试仪 硫化曲线仪 硫化曲线测试仪 武汉凯迪正大KDZD5289产品概述 KDZD5289硫化曲线测试仪&#xff08;电脑无转子硫化仪&#xff09;采用电脑控制进口温控仪进行准确控温&#xff0c;计算机适时进行数据处理并可进行统计、分析、存储对比等&#xff…

刚提离职,当天晚上公司就派人偷偷翻看我的电脑!

你被公司恶心过吗&#xff1f; 一位网友分享了被“恶心”的经历&#xff1a;提了离职&#xff0c;当天晚上电脑就被打开&#xff0c;提示有人登录自己微信&#xff0c;所有电脑记录都被偷偷翻看&#xff0c;她一怒之下在群里问&#xff0c;有人承认用了她的电脑&#xff0c;理由…

uniApp应用软件在运行时,未见向用户告知权限申请的目的,向用户索取(存储、相机、电话)等权限,不符合华为应用市场审核标准。

根据应用市场审核标准。我们开发的软件想要过审就必须要在应用在运行时&#xff0c;向用户告知权限申请的目的&#xff0c;向用户索取&#xff08;存储、相机、电话&#xff09;等权限&#xff01;&#xff01; 但是我们会发现做了提示弹框后又会驳回弹窗评频繁弹窗等等一系列…

【数据结构】单链表---C语言版

【数据结构】单链表---C语言版 一、顺序表的缺陷二、链表的概念和结构1.概念&#xff1a; 三、链表的分类四、链表的实现1.头文件&#xff1a;SList.h2.链表函数&#xff1a;SList.c3.测试函数&#xff1a;test.c 五、链表应用OJ题1.移除链表元素&#xff08;1&#xff09;题目…

Linux中的内存回收:Swap机制(图文并茂)

1、Swap机制是什么 &#xff1a; Swap机制是一种利用磁盘空间来扩展内存的方法。当系统的物理内存不足时&#xff0c;可以把一些不常用的内存数据写入到磁盘上的Swap分区&#xff0c;从而释放出更多的内存给其他需要的进程。当这些内存数据再次被访问时&#xff0c;系统会把它们…

多模态大模型总结2(主要2023年)

LLaVA-V1&#xff08;2023/04&#xff09; 论文&#xff1a;Visual Instruction Tuning 网络结构 如下图 所示为 LLaVA-v1 的模型结构&#xff0c;可以看出其简化了很多&#xff0c;但整体来说还是由三个组件构成&#xff1a; Vision Encoder&#xff1a;和 Flamingo 模型的 V…

Agent举例与应用

什么是Agent OpenAI 应用研究主管 Lilian Weng 在一篇长文中提出了 Agent LLM&#xff08;大型语言模型&#xff09;记忆规划技能工具使用这一概念&#xff0c;并详细解释了Agent的每个模块的功能。她对Agent未来的应用前景充满信心&#xff0c;但也表明到挑战无处不在。 现…

用VR+科普点亮科技之光VR航天科普体验巡展

11月22日至26日&#xff0c;第十一届中国(绵阳)科技城国际科技博览会圆满闭幕。本届科博会以“科技引领创新转化开放合作”为主题&#xff0c;创新办展办会模式&#xff0c;搭建高能级科技合作交流平台&#xff0c;展示了国内外科技创新发展成就和最新成果&#xff0c;举办了多…

铝合金轮毂金属部件全自动三维精密测量工业光学3d智能检测仪器-CASAIM-IS(2ND)

一、背景介绍 汽车轮毂是汽车零部件的重要组成部分。对于汽车而言&#xff0c;轮毂等同于腿对人的重要性。车辆将在行驶过程中产生横向和纵向载荷&#xff0c;车轮也将承受车辆和货物的所有载荷。随着汽车的速度越来越快&#xff0c;对车轮的动态稳定性和可靠性的要求也越来越…

最简单的Python程序员编辑器——学习Python的第二篇

第二章 基础 安装python 按照上一期的教程安装&#xff0c;如果有什么问题可以提出阿莱 安装完之后&#xff0c;在电脑的程序中查找SHELL 这里是Win10&#xff0c;win11你也是到程序菜单查找。 你可以把把它点击右键&#xff0c;保存到桌面上&#xff0c;或者固定到开始菜单…

版本控制系统Git学习笔记-Git基本知识介绍

目录 前言一、版本控制系统1.1 什么是版本控制系统1.2 本地版本控制系统1.3 集中化的版本控制系统1.3 分布式版本控制系统 二、Git简介2.1 数据处理方式2.2 几个特点2.2.1 几乎所有操作都是本地执行2.2.2 Git保证完整性2.2.3 Git一般只添加数据 2.3 Git中文件状态2.3.1 三种文件…

【UE】中文字体 发光描边材质

效果 步骤 1. 先将我们电脑中存放在“C:\Windows\Fonts”路径下的字体导入UE 点击“全部选是” 导入成功后如下 2. 打开导入的“SIMSUN_Font”&#xff0c;将字体缓存类型设置为“离线” 点击“是” 这里我选择&#xff1a;宋体-常规-20 展开细节面板中的导入选项 勾选“使用距…

教你用AI做艺术字,2个月,在小红书接广赚7200元

有段时间没给大家拆账号和完整地上教程了&#xff01;今天就来安排~如何用AI写艺术字&#xff0c;并且在小红薯实现商单BIANXIAN的系统教程.账号很多&#xff0c;我就拿这个AI艺术字搭配治愈系文案来展示下&#xff0c;这个比较有意思&#xff0c;艺术字治愈文案&#xff0c;视…

linux命令解析神器

遥想刚迈入职场时&#xff08;当时的工作环境&#xff0c;需要频繁使用linux&#xff0c;登录设备后台操作&#xff09;&#xff0c;偶然间听到我的领导和其他同事说 &#xff1a;“XXX&#xff0c;多学一学。大佬们太厉害了&#xff0c;太低级的问题不要直接问&#xff0c;你登…