Linux入门——10 信号

news2024/9/23 16:22:00

1.信号

1.信号------信号量(两者没有任何关系)

2.信号讲什么----->整个信号的生命周期

信号的产生-----信号的保存------信号的处理

之前的kill命令,用的就是信号。

kill -l查看系统支持的信号

名字本身就是宏,其实就是编号,我们在使用的时候,既可以使用名字也可以使用编号。

可以发现只有1~31和34~64个信号。

生活中的信号:发令枪,闹铃,红路灯,消息提醒,烽火台狼烟。

  • 人是可以识别红绿灯的,什么是识别,1,认识,2产生匹配的行为
  • 为什么可以识别红绿灯呢?有人教你----通过教你,让你的大脑记住了对应红绿灯属性的行为。
  • 是不是绿灯一亮,你就立刻过马路呢,有没有还有车在路中间,让你没有立刻过马路---》当信号到来的时候,我们不一定立刻处理这个信号,因为信号可能随时产生,但你可能在忙自己的事情(更重要事情),信号来了,不是立即处理的。这就是异步,你在打游戏,这时候外卖小哥给你送外卖。

同步:老师 让我取快递,这时候上课了,老师说等我来了,再上课。

  • 我们不一定立刻处理这个信号,当信号到来的时候,到信号被处理,这个中间会有一段时间----->时间窗口。在这期间我必须记住这个信号。
  • 处理信号的动作,默认动作(绿灯过马路),自定义动作(红灯亮你跳舞等),忽略动作(绿灯我继续等,不过马路)

信号是给进程发的

2.进程是如何识别信号

进程本身是程序员编写的属性和逻辑的集合-----程序员编码完成的

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

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

进程处理信号的时候,一般有三种动作(默认,自定义,忽略){信号被处理被称为信号被捕获}

2.1信号被进程保存到哪?如何保存?

保存在task_struct中

保存是否收到了信号[1~31]

struct task_struct

{

        .....

        unsigned int signal;

}

发送信号的本质,就是修改task_struct(PCB)中的信号位图。

PCB的管理系统是内核的数据结构,

PCB的管理者OS有权利修改里面的内容,所以无论我们学习多少种发送信号的方式,本质都是通过OS向目标进程发送信号!!-----》OS必须要提供发送信号的系统调用接口。

kill命令底层一定调用了底层系统接口

3.信号的产生

ctrl +c热键-----本质就是一个组合键---》OS将ctrl+c解释为2号信号(SIGINT)

使用man 7 signal详细查看2号信号的具体工作

3.1signal更改产生信号后的回调函数

typedef void (*sighandler_t)(int); //信号指针
sighandler_t signal(int signum, sighandler_t handler);
参数:
        int signum:信号编号
        sighandler_t handler  //自定义动作在,通过回调函数执行
返回值:
        sighandler_t //函数指针,也就是原来的信号函数

3.2sigaction()

int sigaction(int signum, const struct sigaction*act,struct sigaction *oldact);
struct sigaction {
   void (*sa_handler)(int);
   void (*sa_sigaction)(int, siginfo_t *, void *);
   sigset_t sa_mask;
   int sa_flags;
   void (*sa_restorer)(void);
}
参数:
        signum:处理的信号
        act,oldact: 处理信号的新行为和旧的行为,是一个sigaction结构体。

        sigaction结构体成员定义如下:
        sa_handler:是一个函数指针,其含义与 signal 函数中的信号处理函数类似
        sa_sigaction:另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。
sa_flags参考值如下:
        SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数
        SA_RESTART:使被信号打断的系统调用自动重新发起。
        SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
       SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。

sa_restorer:是一个已经废弃的数据域

// 处理僵尸进程
    struct sigaction act;
    act.sa_handler = child_back;
    act.sa_flags = SA_RESTART; 
     确保被中断的系统调用自动重启,如果设置为0,子进程死了,父进程跟着死
    sigemptyset(&act.sa_mask); //注意这里是取地址

    sigaction(SIGCHLD,&act,NULL);

4.信号的产生方式

4.1键盘产生信号

CTRL + c 产生2号信号,终止进程

CTRL + / 产生3号信号,终止进程

4.2系统调用

OS有发信号的能力,有能力不代表有使用他的能力,就比如你有写代码的能力,但你的老板在使用你的这种能力

4.2.1KILL命令

kill [-signal] pid

killall [-u  user | prog]

4.2.2kill()函数

可以向任意进程发送任意信号

int kill(pid_t pid, int sig);

参数:

pid_t pid:进程PID

pid:

        > 0:发送信号给指定进程

        = 0:发送信号给跟调用kill函数的那个进程处于同一进程组的进程。

        < -1: 取绝对值,发送信号给该绝对值所对应的进程组的所有组员。

        = -1:发送信号给,有权限发送的所有进程。

int sig:发送的信号编码

返回值L:

成功返回0,失败返回-1

4.2.3raise发送信号给调用者

        - 给自己发送指定的信号
        - 相当于kill(getpid(),signo);

int raise(int sig);

参数:

int sig:信号编号

返回值

4.2.4 abort函数(C语言提供的终止进程方式)

要包含头文件

给自己发送6的信号

相当于kill(getpid(),SIGABRT);

void abort(void);

4.3硬件异常产生信号

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

while(true)
{   
    std::cout << "我是一个进程,正在进行" << std::endl;  
    int a = 10;
    a = a/0;    
    sleep(1);
}

为啥除0,会终止进程?

因为当除0的时候会收到OS的8号信号(SIGFPE )。

如何证明?

用signal

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

int main(int argc, char const *argv[])
{
    while(true)
    {   
        signal (SIGFPE,catchSig);
        std::cout << "我是一个进程,正在进行" << std::endl;  
        int a = 10;
        a = a/0;    
        sleep(1);
    }
    
    return 0;
}

注意:不管这个除0,放在循环里面还是外面,只要发生,进程结束,OS就会一直发8号信号。

我进程在除0,OS是怎么知道我发生除0了呢?

在计算机硬件CPU中,有许多寄存器eax,用于计算。当计算除法的时候,寄存器的计算结果,放到下一个寄存器中,这期间有一个状态寄存器,用来计算每次寄存机的计算结果是否有问题,其中有一个溢出标志位。正常为0,当计算除0操作的时候,溢出标志位会由0至1.说明计算结果非法。CPU就会触发运算异常,OS就会知道。就会向这个进程发信号,并修改标志位。

收到信号不一定会引起进程退出,进程没有被退出,有可能还会被调度,CPU内部的寄存器只有一份,但寄存器中的内容,属于当前进程的上下文。当你没有能力或动作去修正这个问题的时候,当进程被切换,就有无数次状态寄存器被保存和修复的过程,所以每一次恢复的时候,都会让CPU识别到溢出标志位为1,OS一直向该进程发信号 .

还有一个例子

int *p = nullptr;
*p = 100; //野指针

野指针就奔溃了,发生段错误,11号信号。

OS会给当前进程发送指定的11号信号。

计算机不允许访问0号地址,当访问0号地址的时候,通过页表进行映射,其本质是通过MMU,MMU是CPU中的访问物理内存的硬件,当MMU越界访问的时候,会发生异常,OS会知道,进而发送11信号给该进程。

4.4软件条件产生信号

管道-----读端关闭,写端一直写,OS会终止写端,发送SIGPIPE(13号信号)

4.4.1alarm定时器(定时终止进程)

设置时钟时刻,发送14号(SIGALRM)信号,

一次闹钟,只响一次

取消闹钟 alarm(0);

unsigned int alarm(unsigned int seconds);

参数:

        unsigned int seconds:秒

返回值:

        返回0,或者剩余时间(被提前唤醒了)

//统计1s左右,CPU能累加多少次
alarm(1)

int cnt = 0;
while(true)
{
 std::cout<<"cnt:"<<cnt++ <<std::endl;   
}
void catchSig(int signo)
{
    cout<<cnt<<endl;
}


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

这是第一种的1000倍,访问外设和网络IO会很慢。

为什么设置闹钟就是软件条件产生的信号。

闹钟其实就是用软件实现的。

任意一个进程,都可以通过alarm系统调用在内核中设置闹钟,那么OS会存在很多闹钟,OS会对这些闹钟进行管理。先描述再组织。

OS创建闹钟的结构体,通过闹钟队列进行维护,间歇性的访问这些队列里面的when.

也可以建立小堆。进行堆排,OS检查堆顶的时间。

4.4.2ualarm ()循环发送

以useconds为单位,第一个参数为第一次产生时间,第二个参数为间隔产生

4.4.3setitimer()定时发送信号

int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
功能:定时的发送alarm信号
参数:
which:    
        ITIMER_REAL:以逝去时间递减。发送SIGALRM信号

        ITIMER_VIRTUAL: 计算进程(用户模式)执行的时间。 发送SIGVTALRM信号

        ITIMER_PROF: 进程在用户模式(即程序执行时)和核心模式(即进程调度用时)均计算时间。 发送SIGPROF信号


new_value:  负责设定 timout 时间        
old_value:   存放旧的timeout值,一般指定为NULL
struct itimerval {
        struct timeval it_interval;  // 闹钟触发周期
        struct timeval it_value;    // 闹钟触发时间
};

struct timeval {
    time_t      tv_sec;         /* seconds */
    suseconds_t tv_usec;        /* microseconds */
};

4.4.4pause()进程一直阻塞,直到被信号中断

被信号中断返回1,errno为EINTR

int pause(void);

  进程一直阻塞,直到被信号中断,返回值:-1 并设置errno为EINTR

函数行为:

1如果信号的默认处理动作是终止进程,则进程终止,pause函数么有机会返回。

2如果信号的默认处理动作是忽略,进程继续处于挂起状态,pause函数不返回

3 如果信号的处理动作是捕捉,则调用完信号处理函数之后,pause返回-1。

4 pause收到的信号如果被屏蔽,那么pause就不能被唤醒

sigprocmask(SIG_BLOCK,&set,NULL);  //sigprocmask操作当前进程的信号屏蔽字的函数。
task();
sigprocmask(SIG_UNBLOCK,&set,NULL);
pause();

执行task的时候,再发送信号,它会在sigprocmask(SIG_UNBLOCK,&set,NULL);执行完后,直接被捕捉,然直接执行。并没有等到pause,所以pause就没有接受到信号,如果想让pause接受到中间的信号使用sigsuspend函数,屏蔽信号

4.4.5sigsuspend()

int sigsuspend(const sigset_t *sigmask);

功能:将进程的屏蔽字替换为由参数sigmask给出的信号集,然后挂起进程的执行

参数:

sigmask:希望屏蔽的信号

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


void hander(int s)
{
    printf("I get sig=%d\n",s);
}

void task()
{
    printf("MY task start\n");
    sleep(3);
    printf("MY task end\n");
}


int main(int argc, char const *argv[])
{
    struct sigaction act;
    act.sa_handler = hander;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(2,&act,NULL);
    sigset_t set,set2;
    sigemptyset(&set);
    sigemptyset(&set2);
    sigaddset(&set,2);
    pause();
    printf("After pause\n");
    while(1)
    {
        sigprocmask(SIG_BLOCK,&set,NULL);
        task();
        // sigprocmask(SIG_UNBLOCK,&set,NULL);
        //      pause();
        sigsuspend(&set2);

    }
    return 0;
}

5.关于信号处理的行为的理解

有很多的情况,进程收到大部分信号,默认处理动作都是终止进程

信号的意义:信号的不同,代表不同的事件,但是对事件发生之后的处理动作可以一样!

收到信号不一定会引起进程退出,进程没有被退出,有可能还会被调度,CPU内部的寄存器只有一份,但寄存器中的内容,属于当前进程的上下文。当你没有能力或动作去修正这个问题的时候,当进程被切换,就有无数次状态寄存器被保存和修复的过程,所以每一次恢复的时候,都会让CPU识别到溢出标志位为1,OS一直向该进程发信号 .

6.进程退出时的核心转储问题

当使用man 7 signal时,会出现Term和Core两种终止进程的行为

在a[100],a[1000]并没有发生报错。越界并不一定会使编译器报错,当在栈上创建变量的时候,栈开多大空间,你不知道,你只是使用了你所开的空间。给你分配的空间可能会比较大,所以越界,也可能在有效栈区内,不会报错。除非你访问不是你的空间。即你可能在不知情的情况修改一些你的数据。

Term代表正常结束,OS不做其他操作

Core代表不仅结束进程,OS还做其他操作

在云服务器上。默认进程是Core退出的,我们暂时看不到明显的现象,如果想看到,打开一个选项 ulmit -a //系统给我们所设置的资源上限

如果想看,就设置后面的选项

再运行程序出现核心转储,core dumped(核心转储),当前目录下还有一个core.24892(24892引起核心转储的进程的pid)的文件

核心转储:当进程出现异常的时候,我们将进程的对应时刻,,在内存中的有效数据转储到磁盘汇中。

为什么要有核心转储?支持调试---》如何支持?----》gcc后加-g选项。

直接进程gbd 程序名

然后 core-file core.24892

直接出现出错信息,在哪一行出现的错误

这种方式叫事后调试

7.在OS内禁止对9号信号做捕捉

9号信号为管理员信号

8.信号的保存

  • 实际执行信号的处理动作称为信号递达(Delivery)
  • 信号从产生到递达之间的状态,称为信号未决(Pending)。
  • 进程可以选择阻塞 (Block )某个信号。
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
  • 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

8.1进程阻塞信号

老师在课堂上布置作业,然后继续上课,并不是立即去写作业。

我很讨厌这个老师,所以我就先不写你的作业。这个时候就是阻塞。当我把别的作业都做完的时候,再做你的作业,这个时候就是解除阻塞。

阻塞和忽略是不一样的,是两种状态

阻塞是该信号是不会被递达,直到解除阻塞。

忽略本身是递达的一种、

没有信号产生,也可以进行阻塞(我讨厌这个老师,这个老师没布置作业,我依然讨厌他)

信号阻塞和进程阻塞是没有关系的

进程可以选择阻塞某个信号,

由于要访问外设,进程进入阻塞状态

8.2内核中信号的保存

  • 进程收到信号,不会被立即处理,所以要保存,
  • 进程采用位图结构来保存收到的信号

8.2.1pending表(未决表)本质是位图

当信号被置于pending位图中,就说明该信号处于未决状态

8.2.2block表(阻塞表)专业称为信号屏蔽字

当信号被置于block位图中,就说明该信号处于阻塞状态

信号在OS内的被处理

 8.2.3(信号捕捉方法)函数指针&函数指针数组

所以我们可以得到signal(signo,handler)函数的本质就是:拿着信号编号signo,到指定的数组中找。将handler对应方法的地址填入表中。

后面当信号产生的时候,修改上面的pending表中的值,根据block表查看是否阻塞,如果没有阻塞,就进行处理。OS根据信号位置找到编号,根据编号找到对应到函数指针数组中函数的地址,调用方法去处理该信号。

8.2.4结论

  1. 如果一个信号没有产生,不妨碍它可以被阻塞
  2. 进程之所以识别信号,是OS已经设置好上述三种技术,可以识别并处理信号
  3. Linux系统当同一信号被传过来多次,只能被保存一次,相当于其他信号进行了丢失、这是针对普通信号。对于实时信号,OS会产生消息队列,来处理这些信号。

9.信号的捕捉

9.1.1什么是内核态 &用户态

  1. 我们写的代码都是在用户态的,在用户态的时候,我们可能会访问两种资源,1是操作系统自身的资源,2是硬件资源。无论是那种资源都是在OS之下的,我们要通过OS提供的接口。通过这些接口,我们称为系统调用。
  2. (你毕业称为教师(内核态),到你的毕业小学当老师,曾经一些你小学时候(用户态)进不了的地方,现在可以进去了,你依旧是你,但身份发生了变化,所以权限级别发生了变化)实际执行系统调用的“人”,是你的进程,身份是内核。
  3. 往往系统调用比较费时间一些,尽量;尽量避免频繁调用系统调用。

9.1.2我怎么知道我是用户态,还是内核态?

  • 进程在实际执行时,一定会把自己的上下文信息投递到CPU之中,CPU中存在大量寄存器,我们可以将寄存器划分为两类,1,可见寄存器,2、不可见寄存器
  • 凡是和当前进程强相关的,都称为上下文数据。寄存器只有一套,但寄存器中的值可能有多套,当进程切换的时候,他可以把上下文数据带走,回来再拿回来。
  • CPU中有一个寄存器可以直接指向当前运行进程的pcb,这就是知道那个进程在运行的原因
  • 还有的寄存器保存当前进程对应的页表起始地址,还有MMU单元,通过页表找到对应的内存地址。
  • CR3寄存器:里面有比特位,表征当前进程的运行级别,

9.1.3理解进程怎么跑到内核OS中调用方法

每个进程都有自己独立的用户级页表,除此之外,OS内部还维护了一张内核级别页表,它是为了映射从虚拟到物理内存之间的OS的代码。在开机的时候,OS代码也会加载到内存,但是只有一份,所以内核级页表只有一份就够了。也可以理解为CPU有一个寄存器,对应着OS的内核级页表,进程切换的时候,该寄存器不变。

每一个进程都要有自己的地址空间(是独占的),内核空间(被影射到每一个进程的内核空间,占3~4G),所以要访问OS的接口,其实只需要在自己的地址空间上跳转就 可以了。本质就是,跳转到内核空间找到对应的地址,通过内核页表,找到内存中OS的代码,然后再返回到用户空间,进行继续执行。

每个进程都共享一个内核级页表,无论进程如何切换,都不会更改任何3~4G的内核空间。

9.1.4用户执行访问内核的接口或者数据

只要要跳转的时候,更改一下CPU中的CR3寄存器的运行级别就可以了。

系统调用的接口,起始位置会帮你把CR3的值由3(用户态)改为0(内核态)

在Linux有一个终端编号,汇编指令int 80-----陷入内核,修改为内核态

9.1.5信号被处理的时间

信号产生的时候,不会被立即处理,而是在合适的时候,那么这个合适的时候是什么时候呢?

从内核态到信号态的时候,会被处理。-----》曾经我一定进入了内核态!

什么时候进入过内核态呢?

  1. 系统调用
  2. 进程切换:进程切换的时候,没被执行完,这个进程一定会被放到运行队列中,放进去,一定要放到内核态中,以OS的身份进行执行,把进程唤醒的时候,要通过内核态把进程放到运行对列中

9.2信号捕捉

进程由用户态到内核态,好不容易来一次,肯定要干点事情,于是就找到,task_struct中的信号位图,开始按位处理信号,那么,我们能不能以内核态的身份,执行用户的代码呢?

答案是并不能,因为OS不相信任何人。所以当处理信号的时候,会通过特定的方法,将自己的身份重新更改为用户态在执行,执行完,通过特定的系统调用,跳转到内核,将所有信号处理结束,再跳转到用户态。

ABCD代表四次,用户切换。如果是默认或者忽略的时候,就走到信号的检查过程,就停止了,不再往后走了。

9.3sigset_t数据类型,专门为信号设置的数据类型

每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。下一节将详细介绍信号集的各种操作。 阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。

9.4信号集操作函数

9.4.1常用函数

#include <signal.h>
sigset_t set;//自定义信号集64bit  128bit  
int sigemptyset(sigset_t *set);//清空,全设0
int sigfillset(sigset_t *set);//全设 1
int sigaddset (sigset_t *set, int signo);//添加一个信号
int sigdelset(sigset_t *set, int signo);//删一个信号
int sigismember(const sigset_t *set, int signo);//判断信号在吗

函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有效信号。

函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系统支持的所有信号。

注意,在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信比特科技号。

这四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种 信号,若包含则返回1,不包含则返回0,出错返回-1。

9.4.2sigprocmask(更改进程的block表,信号屏蔽字)

#include

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

参数:how(怎么修改进程信号):下面三个选项

SIG_BLOCK(追加):本来作业就是一篇作文,又加了3道题

SIG_UNBLOCK(去除):3道题里面前两题不用写了

SIG_SETMASK(重置):明天考试,作业不用写了

sigset_t *set:信号结构体,传入函数,根据how进行修改

sigset_t *oset:传出函数,万一重置了,想改回原来的,这就是之前的信号屏蔽字

返回值:若成功则为0,若出错则为-1

9.4.3 sigpending(读取当前进程的未决(pending)信号集)

sigpending读取当前进程的未决信号集,通过set参数传出

int sigpending(sigset_t *set);

参数:sigset_t *set:传出函数,用来获取pending未决信号集的数据

返回值:调用成功则返回0,出错则返回-1。

10.捕捉信号的方法

10.1signal更改产生信号后的回调函数

10.2sigaction()

这两个函数在第3.1和3.2。

 

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

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

相关文章

java—1 封装

目录 一、零碎内容 一、输入、输出 二、idea项目结构 三、Java标识符的命名规范&#xff08;约定俗成&#xff09; 四. 方法和函数 二、快捷键 三、数组 1. 声明数组 2. 静态初始化 3. 数组动态初始化 4. 声明和初始化一起完成 5. 数组地址 四、面向对象编程 1. …

进程函数练习

创建子父进程&#xff0c;子进程将1.txt内容拷贝到2.txt中&#xff0c;父进程将3.txt内容拷贝到4.txt中。 #include <myhead.h>int main(int argc, const char *argv[]) {pid_t ID;ID fork();if(ID>0)//父进程{printf("父进程ID:%d\n",ID);int fd open(&…

C HTML格式解析与生成

cmake报错替换 if(NOT MyHTML_BUILD_WITHOUT_THREADS OR NOT MyCORE_BUILD_WITHOUT_THREADS) set(CMAKE_THREAD_PREFER_PTHREAD 1) if (WIN32) set(CMAKE_USE_WIN32_THREADS_INIT ON) set(CMAKE_THREAD_PREFER_PTHREADS TRUE) set(THREADS_PR…

Covalent Network(CXT)第二季度委托激励增长83%

Covalent Network&#xff08;CXT&#xff09;是一家领先的区块链数据解决方案供应商&#xff0c;拥有超过 230 条链的集成和数十亿个结构化数据点&#xff0c;专注于去中心化应用&#xff08;dApps&#xff09;和长期数据可用性。Covalent Network&#xff08;CXT&#xff09;…

C语言 之 整数在内存中的存储、大小端字节序和字节序的判断

文章目录 整数在内存中的存储大小端字节序和字节序判断大小端有大小端的原因高位和地位怎么区分&#xff1f;图例判断机器大端还是小端的例题 整数在内存中的存储 整数的2进制表示方法有三种&#xff0c;即 原码、反码和补码 三种表示方法均有符号位和数值位两部分&#xff0c…

DHCP DNS 欺骗武器化——实用指南

DHCP 枚举 在我们之前的文章中,我们分享了 DHCP DNS 欺骗背后的理论。实际上,需要几条信息才能有效地执行我们描述的攻击。对于攻击者来说幸运的是,发现DHCP 服务器并了解其配置的能力是 DHCP 协议的一部分,这使得侦察过程变得微不足道。 在以下章节中,我们将描述攻击者…

基于华为昇腾910B和LLaMA Factory多卡微调的实战教程

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于大模型算法的研究与应用。曾担任百度千帆大模型比赛、BPAA算法大赛评委,编写微软OpenAI考试认证指导手册。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。授权多项发明专利。对机器学…

PostgreSQL 与对象存储的结合: 在 MinIO 中访问外部数据

数据领域最激动人心的发展之一是湖仓一体功能在所有主要数据库供应商中的兴起。Snowflake 和 SQL Server 长期以来一直采用这一点&#xff0c;现在 PostgreSQL 正在通过 pg_lakehouse 拥抱这种范式转变&#xff0c;使得利用现代数据湖进行分析、AI 等比以往任何时候都更容易。随…

【Java 搜索二维矩阵 I II,多数元素 I II,分治法 二分法 摩尔投票法】

搜索二维矩阵 I II&#xff0c;多数元素&#xff0c;分治法 & 二分法 & 摩尔投票法 题目1&#xff1a;力扣-搜索二维矩阵[https://leetcode.cn/problems/search-a-2d-matrix/description/](https://leetcode.cn/problems/search-a-2d-matrix/description/)分治-排除法分…

了解精密零部件加工制造的关键技术

在现代工业领域中&#xff0c;精密零部件的加工制造起着至关重要的作用。从高端电子设备到航空航天领域&#xff0c;都离不开高精度的零部件。时利和详细解析了解精密零部件加工制造的关键技术&#xff0c;对于推动工业发展的重要性。 高精度的加工设备是实现精密零部件制造的基…

嬴图 | 从数据到智能,解密物流业的“智慧大脑”——图数据库的颠覆性力量

前言 早在3000年前&#xff0c;古埃及、希腊、罗马就开始利用航运系统开启了物资运输和分配&#xff0c;并形成了奥斯提亚、亚历山大港等古代超级物流中心&#xff1b;而在古中国和古印加&#xff0c;完善的驿站系统又将物资、人员、供给等方面进行了升级支持&#xff0c;从制…

MATLAB进阶:矩阵代数

今天我们学习矩阵在MATLAB中的运算。 运算符 与数组运算相同&#xff1a; A. ’转罝 A’&#xff08;共轭&#xff09;转罝 共轭转置&#xff08;A’或A†&#xff09;&#xff1a; 对于一个复数矩阵A&#xff0c;其共轭转置记作A’或A†。共轭转置不仅将矩阵A的行和列互…

用于超声电影中同时检测关键帧和地标的贝叶斯网络| 文献速递-大模型与多模态诊断阿尔茨海默症与帕金森疾病应用

Title 题目 A Bayesian network for simultaneous keyframe and landmark detection inultrasonic cine 用于超声电影中同时检测关键帧和地标的贝叶斯网络 01 文献速递介绍 超声电影&#xff08;Ultrasonic cine&#xff09;是超声检查中常见的保存形式&#xff0c;允许实…

ComsolMatlab 可扩展设计的高效低频阻抗调制声学超材料

参考文献&#xff1a;Zeng K, Li Z, Guo Z, et al. Acoustic metamaterial for highly efficient low-frequency impedance modulation by extensible design[J]. Extreme Mechanics Letters, 2022, 56: 101855. 我们提出了一种创新的低频宽带高效吸声材料&#xff0c;其设计原…

基于Kotlin Multiplatform实现静态文件服务器(五)

Netty简介 Netty 是一个利用 Java 的高级网络的能力&#xff0c;隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。 文件服务 文件服务基于Netty框架实现&#xff0c;关于Netty&#xff0c;可以了解&#xff1a;https://netty.io/。 class BootStrapServe…

“购物也能赚钱?‘随机返利‘模式颠覆你的消费体验!“

近期&#xff0c;关于“消费即享随机返利”的话题在张三与李四之间频繁提及&#xff0c;这一新颖的消费机制究竟是何方神圣&#xff1f; 实质上&#xff0c;它并非某种实体物品&#xff0c;而是一种创新的营销策略&#xff0c;旨在促进商品销售。去年&#xff0c;一位精明的商家…

【25届秋招】饿了么0817算法岗笔试

目录 1. 第一题2. 第二题3. 第三题 ⏰ 时间&#xff1a;2024/08/17 &#x1f504; 输入输出&#xff1a;ACM格式 ⏳ 时长&#xff1a;100min 本试卷还有单选和多选部分&#xff0c;但这部分比较简单就不再展示。 最近终于有时间继续整理之前的笔试题了&#xff0c;因为时间仓促…

Gartner发布2024年终端和工作空间安全成熟度曲线:24项相关技术发展和应用状况及趋势

由于攻击者使用人工智能来增强网络钓鱼和终端攻击&#xff0c;企业需要高级安全措施来阻止入侵行为。此技术成熟度曲线可帮助安全和风险管理领导者识别可增强终端和工作空间保护的技术。 需要知道什么 网络安全创新层出不穷&#xff0c;但区分真正的进步与短暂的趋势却很困难。…

如何在Python中使用IP代理

在网络爬虫、数据抓取等应用场景中&#xff0c;使用IP代理可以有效避免IP被封禁&#xff0c;提高爬取效率。本文将详细介绍如何在Python中使用IP代理&#xff0c;帮助你在实际项目中灵活应用。 准备工作 在开始之前&#xff0c;你需要准备以下工具和资源&#xff1a; Python环…

Go Convey测试框架入门(go convey gomonkey)

Go Convey测试框架入门 介绍 GoConvey是一款针对Golang的测试框架&#xff0c;可以管理和运行测试用例&#xff0c;同时提供了丰富的断言函数&#xff0c;并支持很多 Web 界面特性。 Golang虽然自带了单元测试功能&#xff0c;并且在GoConvey框架诞生之前也出现了许多第三方测…