当前位置: 首页 > news >正文

[Linux]进程信号

[Linux]进程信号

文章目录

  • [Linux]进程信号
    • 进程信号的定义
    • 信号的特点
    • 信号的生命过程
    • 发送信号的原理
    • 进程处理信号的方式分类
    • 使用指令查看Linux系统定义的信号
    • 信号产生
      • 使用终端按键产生信号
      • 使用指令向进程发送信号
      • 调用系统调用向进程发送信号
      • 由软件条件产生信号
      • 硬件异常产生信号
    • 核心转储
      • 进程信号和核心转储的关系
      • 使用指令操作系统核心转储信息
      • 核心转储文件的使用
      • 核心转储和退出信号的关系
    • 阻塞信号
    • 信号在内核的表示(信号保存)
    • 信号集操作
      • 信号集数据类型
      • 信号集操作函数
      • sigprocmask函数
      • sigpending函数
    • 捕捉信号(信号处理)
      • 用户态和内核态
      • 信号处理的时机
      • 信号处理的过程
      • signal函数
      • sigaction函数
    • volatile关键字
    • SIGCHLD信号

进程信号的定义

进程之间事件异步通知的一种方式。它是一种软件中断,用于向进程发送通知和指令,以便对其进行控制或传递信息。进程信号由整数值来标识,每个值对应一个特定的信号。不同的信号对应不同的状况。

信号的特点

  • 信号产生前,进程就知道如何处理
  • 信号一旦产生,进程能够识别信号。
  • 进程接收到信号后,不一定会立即处理,进程在收到信号后会先记录下来。
  • 信号的产生对于进程是异步的。

信号的生命过程

信号的生命过程分为三个阶段:

  • 信号产生
  • 信号保存
  • 信号处理

image-20230913193125910

发送信号的原理

进程信号有许多个,操作系统为了管理进程接收的信号,需要使用一定的结构描述信号,操作系统采用了位图结构来记录不同的信号,该位图结构记录在进程的pcb中,当操作系统向进程发送信号时,就向进程pcb中描述信号的位图结构写入数据。

进程处理信号的方式分类

  1. 忽略此信号。
  2. 执行该信号的默认处理动作。
  3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉 (Catch)一个信号。

使用指令查看Linux系统定义的信号

使用kill-l可以查看Linux系统定义的信号列表:

image-20230912131515776

使用man 7 signal可以查看Linux系统定义的信号详细说明:

image-20230912132501892

信号产生

使用终端按键产生信号

在Linux系统中输入crtl + c可以给进程发送2号信号,2号信号默认的处理动作是终止进程。给进程发送2号信号的示例如下:

键盘发送信号

使用指令向进程发送信号

Linux系统中提供了kill -信号数 进程pid指令用于向指定进程发送信号:

指令信号

调用系统调用向进程发送信号

kill接口

Linux系统中提供了kill系统调用用于向指定进程发送信号:

//kill所在的头文件和声明
#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);
  • pid参数 – 要发送信号的进程编号。
  • sig参数 – 要给进程发送的信号。
  • 成功返回0,失败返回-1,错误码被设置。

raise接口

Linux系统中提供了raise系统调用用于向进程自身发送信号:

//raise所在的头文件和声明
#include <signal.h>int raise(int sig);
  • sig参数 – 向进程发送的信号。
  • 成功返回0,失败返回失败原因对应的非0值。

abort接口

Linux系统下C语言库中提供了abort库函数用于向进程自身发送信号 SIGABRT

//abort所在的头文件和声明
#include <stdlib.h>void abort(void);
  • abort是C语言库中提供的接口。
  • abort向调用进程自身发送6号信号 SIGABRT

由软件条件产生信号

Linux系统了调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。

//alarm所在的头文件和声明
#include <unistd.h>unsigned int alarm(unsigned int seconds);
  • 调用alarm函数操作系统会创建闹钟并建立对应数据结构组织起来。

硬件异常产生信号

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除 以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

核心转储

Linux操作系统可以在进程异常时,将核心代码部分和相关的内存数据全部导出到磁盘外设中,这一功能被称作核心转储。

进程信号和核心转储的关系

image-20230912170508835

不同的进程异常情况,进程会收到不同的进程信号,使用man 7 signal指令查看进程信号的信息,其中Action列中为Core的信号发送给进程后,进程就会进行核心转储。

使用指令操作系统核心转储信息

查看核心转储信息

使用ulimit -a指令可以查看到系统核心转储文件的信息:

image-20230912170936766

云服务中默认设定核心转储文件的大小上限为0,也就是不进行核心转储。

修改核心转储信息

使用ulimit -c指令可以修改核心转储文件的大小上限:

image-20230912171123251

核心转储文件的使用

核心转储文件的主要作用是当程序发生崩溃或异常终止时,通过分析核心转储文件,可以了解程序崩溃的原因、定位错误的位置以及查找潜在的缺陷。

运行一个进程用于,然后给它发送8号信号,让他产生核心转储文件:

image-20230912171514803

使用gdb调试可执行程序,然后使用core-file指令打开核心转储文件,就能从调试中看到进程异常原因:

image-20230912171807299

核心转储和退出信号的关系

如果某一进程产生异常生成了核心转储文件,Linux系统下进程使用系统接口等待回收该进程,获得的退出信息中倒数第八位会被置为1:

image-20230912173222507

也就是core dump标志被置为1。

阻塞信号

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

信号在内核的表示(信号保存)

信号在内核中的表示示意图如下:

image-20230912194206031

操作系统在内核数据结构task_struct中为信号维护了三张表:

  • block: 位图结构,比特位的位置,表示一种信号,比特位的内容,对应的信号是否被阻塞。
  • pending: 位图结构,比特位的位置,表示一种信号,比特位的内容,对应的信号是否接收到了。
  • handler: 函数指针数组,数组的下标,表示信号的编号,数组的特定下标的内容,表示信号的递达动作。(用户自定义递达动作就是通过修改该数组实现的)

信号集操作

信号集数据类型

  • 每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。 因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集

  • sigset_t类型可以表示每个信号 的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有 效”和“无效”的含义是该信号是否处于未决状态。

  • sigeset_t类型的底层实现也是一种位图结构。

信号集操作函数

sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,这个类型内部如何存储这些bit则依赖于系统实现,用户不需要关心具体实现,只要能够按需求操作信号集即可,因此Linux系统提供了如下函数来操作信号集:

//信号集操作函数所在的头文件及函数声明
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
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类型的变量之前,一定要调 用sigemptysetsigfillset做初始化,使信号集处于确定的状态。

  • 初始化sigset_t变量之后就可以在调用sigaddsetsigdelset在该信号集中添加或删除某种有效信号。

  • 前四个函数都是成功返回0,出错返回-1。

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

sigprocmask函数

调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集–block表)。

//sigprocmask所在的头文件及函数声明
#include <signal.h>int sigprocmask(int how, const sigset_t *set, sigset_t *oset); 
  • 如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。
  • 如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。
  • 如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。
  • 返回值:若成功则为0,若出错则为-1

假设当前的信号屏蔽字为mask,下表说明了how参数的可选值:

SIG_BLOCKset包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask|set
SIG_UNBLOCKset包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~set
SIG_SETMASK设置当前信号屏蔽字为set所指向的值,相当于mask=set

sigpending函数

调用函数sigpending可以读取当前进程的未决信号集,通过set参数传出:

//sigpending所在的头文件及函数声明
#include <signal.h>int sigpending(sigset_t *set);
  • 调用成功则返回0,出错则返回-1。

捕捉信号(信号处理)

用户态和内核态

Linux系统中进程地址空间分为两个部分,一个部分是用户空间,另一个部分是内核空间,两个部分都有自身对应的页表,一个是用户级页表,另一个是内核级页表,操作系统计算机开机时会将自身的代码和数据加载到内存中,这部分代码和数据通过同一张内核级页表映射到每个进程的地址空间中的内核部分,示意图如下:image-20230913193735144

由于操作系统的代码和数据通过同一张内核级页表映射到每个进程的地址空间中的内核部分,因此在进程运行时,只需要像跳转到动态库一样,跳转到操作系统的代码和数据,使得进程代码和操作系统代码的切换可以在一张进程地址空间中跳转完成,但操作系统为了不让用户通过进程地址空间中的内核部分的随意访问操作系统的代码和数据,操作系统设置了两种状态:**用户态和内核态,当处于用户态时,进程只能访问用户进程的代码和数据,当处于内核态时,进程只能访问操作系统的代码和数据。**为了记录当前处于用户态还是内核态,将标志信息记录在CPU的寄存器中。

小总结一下:

  • 所有进程地址空间的用户空间中的内容是不一样的,每一个进程都有自己的用户级页表,看到属于自己的代码和数据。
  • 所有进程地址空间的内核空间中的内容是一样的,每一个进程都会看到同一个内核级页表,看到同一个操作系统。
  • 操作系统实际是通过进程的地址空间完成运行。
  • 使用系统调用的本质就是跳转到进程地址空间的内核空间部分运行。
  • 为了保护操作系统中的代码和数据,操作系统设置了用户态和内核态。

信号处理的时机

当进程从内核态切换回用户态的时候(信号记录在内核中,只能在内核态访问),进程会在操作系统的指导下,进程信号的检测和处理。

用户态切换至内核态的情况如下:

  • 发生系统调用:当用户程序需要访问受保护的系统资源或请求操作系统提供的服务时,它会发起系统调用。操作系统会接收到系统调用请求,然后执行相应的系统代码来处理该请求,并返回结果给用户程序。
  • 异常或中断事件:当发生硬件故障、软件错误或外部中断等事件时,操作系统需要对其进行处理。操作系统会通过中断处理程序或异常处理程序来响应这些事件,并执行必要的系统代码来处理它们。
  • 定时器事件:操作系统通常会使用定时器来进行时间管理和调度。当定时器触发时,操作系统会响应该事件,执行系统代码以更新任务调度和执行状态。

补充说明: 计算机存在一个计时器硬件,当计时器记录进程运行到一定时间后,操作系统会执行对应的中断方法,检查时间片,如果时间片到了就会调用操作系统中的调度函数,完成进程的切换。

信号处理的过程

在进程运行执行对应代码时,遇到需要切换至内核态的情况,产生中断跳转至内核态,进入内核态后完成对应中断处理后,开始检测信号,进行,执行信号对应的处理动作,若是默认处理或者忽略处理后就会直接进入用户态跳转回用户态中断前代码,如果是自定义处理动作,会进入用户态跳转执行对应的自定义处理动作,然后调用sigreturn回到内核态,再调用sys_sigreturn进入用户天跳转回用户态中断前代码。执行自定义处理动作的过程示意图如下:

image-20230913213158930

  • 在检测信号后,会将要处理的信号在block表对应位置置为1,将pending表中对应位置置为0,然后再执行处理动作。

signal函数

调用函数signal可以将对应信号的处理动作改为自定义处理动作:

//signal所在的头文件及函数声明
#include <signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);
  • signum参数 – 要修改的信号。
  • handler参数 – 自定义处理函数的函数指针。传入SIG_DFL为默认处理动作,SIG_IGN为忽略动作。
  • 返回值是一个函数指针,如果成功设置信号处理程序,则返回先前的信号处理程序的函数指针;如果出现错误,则返回 SIG_ERR
  • SIGKILLSIGSTOP 信号不能被修改为自定义处理动作。

编写如下代码进行测试:

#include <iostream>
#include <unistd.h>
#include <signal.h>using namespace std;void handler(int signo)
{cout << "get the signal: " << signo << endl;
}int main()
{signal(SIGINT, handler);while(true){cout << "i am running, pid: " << getpid() << endl;sleep(1);}return 0;
}

编译代码运行查看结果:

signal

sigaction函数

sigaction函数可以读取和修改与指定信号相关联的处理动作。

//sigaction所在的头文件及函数声明
#include <signal.h>int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  • signo参数 – 指定信号的编号。
  • 若act指针非空,则根据act修改该信号的处理动作。
  • 若oact指针非空,则通过oact传出该信号原来的处理动作。
  • 调用成功则返回0,出错则返回- 1。

sigaction函数需要使用struct sigaction数据类型作为参数,其结构定义如下:

struct sigaction {void     (*sa_handler)(int);void     (*sa_sigaction)(int, siginfo_t *, void *);sigset_t   sa_mask;int        sa_flags;void     (*sa_restorer)(void);
};

其主要字段如下:

  • sa_handler: 赋值为一个函数指针表示用自定义函数捕捉信号
  • sa_mask: 在进行信号处理时需要额外屏蔽的信号
  • sa_flags: 默认为0即可。

sigaction函数使用结构体作为参数修改信号处理动作,相比signal功能更为强大。

volatile关键字

在C语言中,volatile 是一个关键字,用于告诉编译器某个变量是易变的(volatile)并且不应该进行优化。

为了体会volatile关键字的作用,创建如下文件及文件内容:

makefile:

mysignal:mysignal.ccg++ -o $@ $^ -std=c++11 -O2//注意此处采用O2优化级别.PHONY:clean
clean:rm -rf mysignal

mysignal.cc:

#include <iostream>
#include <signal.h>using namespace std;int quit = 0;void handler(int signo)
{cout << "get the signal: " << signo << endl;quit = 1;cout << "quit: " << quit << endl;
}int main()
{signal(2, handler);while(!quit);cout << "process quit" << endl;return 0;
}

编译代码并查看运行结果:

volatile1

从现象中可以看出,在键盘中输入ctrl+c传入二号信号后,执行了自定义的处理动作quit改为了1,但是循环依旧继续,程序并未终止。这是因为编译器的优化,在无优化的情况下,从汇编来看应该是在每次循环时将quit数据从内存加载到CPU寄存器中进行判断,但是由于main函数中没有quit修改的代码,编译器以为quit不会修改,直接将汇编优化成了只从内存中加载一次quit,然后只进行判断,信号处理后修改的是内存的数据,因此不会使得循环停止。

使用volatile关键字修改代码:

#include <iostream>
#include <signal.h>using namespace std;volatile int quit = 0;void handler(int signo)
{cout << "get the signal: " << signo << endl;quit = 1;cout << "quit: " << quit << endl;
}int main()
{signal(2, handler);while(!quit);cout << "process quit" << endl;return 0;
}

编译代码并查看运行结果:

volatile2

volatile关键字让quit不再优化,每次判断都从内存加载quit到CPU中,因此现象如上图。

SIGCHLD信号

子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号 的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可,具体自定义代码如下:

void handler(int signo)
{while(true){pid_t id = waitpid(-1, nullptr, WNOHANG);if (id > 0) printf("waitpid: %d, my pid: %d\n", id, getpid());else break;}
}

说明:

  • waitpid传入-1让其接收任意一个僵尸状态的子进程。
  • 循环调用waitpid是为了解决如果一个进程回收时,其他进程进入僵尸状态。
  • 采用WNOHANG非阻塞是为了解决暂时没有子进程需要回收,进入阻塞状态。

除了以上自定义处理外,Linux系统还提供了另一种方法解决僵尸状态的子进程:父进程调用sigactionSIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不会产生僵尸进程,也不会通知父进程。(此方法对于Linux可用,但不保证在其它UNIX系统上都可用。)

相关文章:

[Linux]进程信号

[Linux]进程信号 文章目录 [Linux]进程信号进程信号的定义信号的特点信号的生命过程发送信号的原理进程处理信号的方式分类使用指令查看Linux系统定义的信号信号产生使用终端按键产生信号使用指令向进程发送信号调用系统调用向进程发送信号由软件条件产生信号硬件异常产生信号 …...

PostgreSQL 数据类型

文章目录 PostgreSQL数据类型说明PostgreSQL数据类型使用单引号和双引号数据类型转换布尔类型数值类型整型浮点型序列数值的常见操作 字符串类型日期类型枚举类型IP类型JSON&JSONB类型复合类型数组类型 PostgreSQL数据类型说明 PGSQL支持的类型特别丰富&#xff0c;大多数…...

智慧港口4G+UWB+GPS/北斗RTK人员定位系统解决方案

港口人员定位系统能够帮助企业实现对港口作业人员的全面监控和管理&#xff0c;不仅可以保障人员的人身安全&#xff0c;还可以提高人员的作业效率&#xff0c;为港口的可持续发展提供有力保障。接下来为大家分享智慧港口人员定位系统解决方案。 方案背景 1、港口作业人员多&a…...

实时时钟和日历电路芯片MS85163/MS85163M

MS85163/MS85163M 是一款 CMOS 实时时钟 (RTC) 和 日历电路&#xff0c;针对低功耗进行了优化&#xff0c;内置了可编程的时钟输出、中断输出和低电压检测器。所有寄存器地址和数据都通过两线双向I 2 C 总线进行串行传输&#xff0c;最大总线传输速度为 400kbit/s 。采用SOP8…...

【Java从入门到精通】这也许就是Java火热的原因吧!

前言&#xff1a;Java是一种高级的、面向对象的、可跨平台的程序设计语言。Java根据技术类别可划分为以下几类&#xff1a;JavaSE&#xff08;Standard Edition&#xff0c;标准版&#xff09;&#xff1a;支持面向桌面、嵌入式和移动设备的应用程序开发&#xff1b;JavaEE&…...

zTasker—简洁易用强大的定时热键一体自动化工具,效率倍增器

软件名称 zTasker 应用平台 PC Windows7及以上 一句简介 市面上定时类软件很多&#xff0c;但无一例外功能都很单一&#xff0c;要完成不同的任务&#xff0c;需要不同的软件 市面上的热键软件&#xff0c;要么功能少&#xff0c;要么像是AutoHotKey这样对于一般用户太专业…...

惊艳时装界!AIGC风暴来袭,从设计到生产的全新体验

时尚是一个不断演进的领域&#xff0c;充满创新和独创性&#xff0c;但现在&#xff0c;创新迈入了一个崭新的境界——人工智能生成内容&#xff08;AIGC&#xff09;。这个革命性的技术&#xff0c;改变了时装设计的游戏规则。在过去的几年里&#xff0c;人工智能已经深刻地改…...

element -ui table表格内容无限滚动 使用插件vue-seamless-scroll

使用插件 一、安装组件依赖 npm install vue-seamless-scroll 二、引入组件 import vueSeamlessScroll from "vue-seamless-scroll"; components: { vueSeamlessScroll }, <div class"table-list "><vue-seamless-scroll :class-option"…...

如何在windows环境下编译T

一&#xff0c; 安装MYSYS2 1. 去https://www.msys2.org下载 msys2-x86_64-xxxxx.exe; 2. 按照msys2.org主页提示的步骤安装; 3.安装完默认起来的是 UCRT的&#xff0c; 可以根据环境的需要选择&#xff0c; 我选择的 MSYS2 MINGW64 4. 搭建编译环境&#xff0c; 安装对应的软…...

USB接口针式打印机

1 针式打印机原理 - 针式打印机16针是纵向排列&#xff0c;每次打印垂直的16bit&#xff0c;然后右移一bit&#xff0c;继续下列打印&#xff1b;字节的MSB表示最上面的点&#xff0c;字节LSB表示最下面的点 - 由于汉字字模的点阵是横向排列的&#xff0c;而提供给打印头的信息…...

外贸建站教程步骤有哪些?独立站怎么搭建?

推荐的外贸建站教程&#xff1f;制作国际贸易网站的流程&#xff1f; 对于那些希望将产品或服务推向全球市场的企业来说&#xff0c;建立一个专业、具有吸引力的网站是至关重要的。下面115SHOP将介绍外贸建站教程的关键步骤&#xff0c;帮助您更好地了解如何在国际市场上建立您…...

useGetState自定义hooks解决useState 异步回调获取不到最新值

setState 的两种传参方式 1、直接传入新值 setState(options); const [state, setState] useState(0); setState(state 1); 2、传入回调函数 setState(callBack); const [state, setState] useState(0); setState((prevState) > prevState 1); // prevState 是改变之…...

input子系统框架、外设驱动开发

一、input子系统基本框架 Linux内核为了两个目的&#xff1a; 简化纯输入类外设&#xff08;如&#xff1a;键盘、鼠标、游戏杆、轨迹球、触摸屏。。。等等&#xff09;的驱动开发统一输入类外设产生的数据格式&#xff08;struct input_event&#xff09;&#xff0c;更加方…...

Google Chrome 浏览器以全屏模式打开

目录 前言以全屏模式打开禁止弹出无法更新的提示窗禁止翻译网页Chrome设置禁止翻译网页可能1可能2可能3 网页添加指令禁止Chrome翻译网页 禁用脚本气泡浏览器解决办法html解决办法方法1&#xff1a;鼠标滑过超链接时&#xff0c;使状态栏不出现超链接方法2&#xff1a;方法3&am…...

安装torch113、cuda116并运行demo【Transformer】

文章目录 01. 导读02. 显卡驱动版本03. 创建环境、下载安装必要包04. 运行参考代码&#xff1a; 01. 导读 安装torch113、cuda116并运行demo【Transformer】 02. 显卡驱动版本 C:\Users\Administrator>nvidia-smi -l 10 Wed Sep 13 23:35:08 2023 ----------------------…...

基于scRNA-seq的GRN分析三阴性乳腺癌的肿瘤异质性

三阴性乳腺癌即TNBC是一种肿瘤异质性高的乳腺癌亚型。最近的研究表明&#xff0c;TNBC患者可能包含具有不同分子亚型的细胞。此外&#xff0c;基于scRNA-seq数据构建的GRN已经证明了对关键调控因子研究的重要性。作者使用scRNA-seq对TNBC患者的GRN进行了全面分析。从scRNA-seq数…...

Python:二进制文件实现等间隔取相同数据量并合并

举例&#xff1a;每3byte为一页&#xff0c;每3页为一wl。将所有wl的第一页/第二页/第三页分别合并为一个文件。 data b\x01\x02\x03\x04\x05\x06\x07\x08\x09\x01\x02\x03\x04\x05\x06\x07\x08\x09\x01\x02\x03\x04\x05\x06\x07\x08\x09\x01\x02\x03\x04\x05\x06\x07\x08\x0…...

python使用openvc库进行图像数据增强

以下是使用Python和OpenCV库实现图像数据增强的简单示例代码&#xff0c;其中包括常用的数据增强操作&#xff1a; import cv2 import numpy as np import os# 水平翻转 def horizontal_flip(image):return cv2.flip(image, 1)# 垂直翻转 def vertical_flip(image):return cv2…...

如何利用Api接口获取手机当前的网络位置信息

在移动互联网时代&#xff0c;手机定位已经成为了一个日常化的需求&#xff0c;无论是导航、社交还是打车等服务都需要获取手机的位置信息。而获取手机位置信息最基础的一步就是获取手机当前的网络位置信息&#xff0c;本文将介绍如何利用API接口获取手机当前的网络位置信息。 …...

vue-elementPlus自动按需导入和主题定制

elementPlus自动按需导入 装包 -> 配置 1. 装包&#xff08;主包和两个插件包&#xff09; $ npm install element-plus --save npm install -D unplugin-vue-components unplugin-auto-import 2. 配置 在vite.config.js文件中配置&#xff0c;配置完重启&#xff08;n…...

idea中dataBase模板生成

controller.java.vm ##定义初始变量 #set($tableName $tool.append($tableInfo.name, "Controller")) ##设置回调 $!callback.setFileName($tool.append($tableName, ".java")) $!callback.setSavePath($tool.append($tableInfo.savePath, "/contro…...

pc端测试手机浏览器运行情况,主要是测试硬件功能

测试h5震动摇晃等功能时不方便测试&#xff0c;需要连电脑显示调试数据 方法&#xff1a; 1.需要手机下载谷歌浏览器&#xff0c;pc端用edge或这谷歌浏览器 2.手机打开USB调试&#xff0c;打开要测试的网页 3.pc端地址栏输入edge://inspect/#devices&#xff08;这里用的edge浏…...

软件概要设计-架构真题(二十五)

软件概要设计包括软件设计的结构、确定系统功能模块及其相互关系&#xff0c;主要采用&#xff08;&#xff09;描述程序的结构。&#xff08;2018年&#xff09; 程序流程图、PAD图和伪代码模块结构图、数据流图和盒图模块结构图、层次图和HIPO图程序流程图、数据流图和层次图…...

CSDN发文表情包整理

文章目录 简介部分Emoji表情符号简表人物自然物品地点符号 各种Emoji表情链接 简介 CSDN支持Markdown语法及Emoji表情&#xff0c;使用各种Emoji表情可以使得自己的博文更加生动多彩。一般有两种在支持Markdown的语法环境中添加Emoji表情&#xff1a;1.直接将表情包复制到文档…...

springBoot对接Apache POI 实现excel下载和上传

搭建springboot项目 此处可以参考 搭建最简单的SpringBoot项目_Steven-Russell的博客-CSDN博客 配置Apache POI 依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.2</version> </…...

定积分的计算:牛顿-莱布尼茨公式

目录 牛顿-莱布尼茨公式 用C语言代码实现 利用换元积分法和分部积分法 利用奇偶性和周期性求积分 利用已有公式求积分 牛顿-莱布尼茨公式 牛顿-莱布尼茨公式&#xff08;Newton-Leibniz formula&#xff09;是微积分学中的基本定理之一&#xff0c;它反映了定积分与被积函…...

shell脚本之case 的用法

shell脚本之case case是Shell脚本中的一种控制流语句&#xff0c;它允许根据变量的值选择不同的执行路径。case语句的语法如下&#xff1a; case word in pattern [| pattern]...) command-list ;; pattern [| pattern]...) command-list ;; ... *) command-list ;; esa…...

第3章 helloworld 驱动实验(iTOP-RK3568开发板驱动开发指南 )

在学习C语言或者其他语言的时候&#xff0c;我们通常是打印一句“helloworld”来开启编程世界的大门。学习驱动程序编程亦可以如此&#xff0c;使用helloworld作为我们的第一个驱动程序。 接下来开始编写第一个驱动程序—helloworld。 3.1 驱动编写 本小节来编写一个最简单的…...

基于PyTorch使用LSTM实现新闻文本分类任务

本文参考 PyTorch深度学习项目实战100例 https://weibaohang.blog.csdn.net/article/details/127154284?spm1001.2014.3001.5501 文章目录 本文参考任务介绍做数据的导入 环境介绍导入必要的包介绍torchnet和keras做数据的导入给必要的参数命名加载文本数据数据前处理模型训…...

Flutter插件的制作和发布

Flutter制作插件有两种方式&#xff08;以下以android和ios为例&#xff09;&#xff1a; 目录 1.直接在主工程下的android和ios项目内写插件代码&#xff1a;2.创建独立Flutter Plugin项目&#xff0c;制作各端插件后&#xff0c;再引入项目&#xff1a;1. 创建Flutter Plugin…...

网站建设推荐公司/上海搜索引擎优化seo

Spring Cloud(微服务)学习篇(三) 1 nacos中使用openFeign(调用方式)实现短信发送 1.1 在shop-sms-api中创建com.zlz.shop.sms.api.service/vo/dto/util,目录结构如下所示 1.2 在pom.xml(shop-sms-api)中加入如下依赖 <dependencies><dependency><groupId>…...

东莞 营销网站制作/百度网页版浏览器

1.下载eclipse需要的运行环境&#xff0c;JDK/JRE. 在http://wiki.eclipse.org/Eclipse/Installation中&#xff0c;找到最新的eclipse版本需要的JAVA 版本&#xff0c;然后去相应网站下载JAVA。这里我用的是Eclipse 4.5(Mars)&#xff0c;它需要JAVA 7。 2.下载eclipse 在ecli…...

深圳网站建设公司 评论/视频号怎么推广流量

CCNA(4)有类路由 有类路由协议&#xff1a;RIPv1 、IGRP,现在已经很少看到有路由器运行这两个路由协议了。 有类路由协议在路由更新的时候不包含子网掩码信息&#xff0c;所以运行有类路由的路由器在进行路由更新时自行决定使用何种子网掩码。这种判断基于IP地址类型。 运行有类…...

wordpress 采集小说/网站关键词如何优化

使用MFC建立的SDI应用程序默认为白色背景,你可以按下列步骤修改为其他背景颜色。 CtrlW pops up the MFC classwizard property sheet. Select the Message Maps tab. From the drop-down list box under the Class Name static control, select the CxxxView option (xxx You…...

关于网站建设的请示范文/山东seo推广

Loadrunner一直被业内认为是最好用的性能测试工具&#xff0c;行业大哥大, 但是用过Loadrunner的朋友都知道&#xff0c;工具功能的确牛&#xff0c;但实际使用过程中总会有一些困扰新手的问题&#xff0c;无法录制脚本&#xff0c; 如遇到Loadrunner不支持的IE版本、对Chrome、…...

360网站推广怎么做/互联网推广公司排名

本文为《汇编语言程序设计》0904小节例程。点击链接…进课程主页。 jcxz指令 assume cs:codesg codesg segment start: mov ax,2000Hmov ds, axmov bx,0s: mov cx, [bx]jcxz okinc bxinc bx jmp short sok: mov dx, bxmov ax, 4c00Hint 21H codesg ends end start loop指令 …...