ARM Linux 内核启动2 ——C语言阶段

news2024/10/5 3:28:53

一、内核启动的C语言阶段 1

1、这一块的学习思路

(1) 抓大放小,不深究。

(2) 感兴趣可以就某个话题去网上搜索资料学习。

(3) 重点局部深入分析。


2、具体学习方法

(1) 顺着代码执行路径抓全。这是我们的学习主线。

(2) 对照内核启动的打印信息进行分析。

在这里插入图片描述


3、几条学习线路

(1) 分析 uboot 给 kernel 传参的影响和实现。

(2) 硬件初始化与驱动加载。

(3) 内核启动后的结局与归宿。


二、内核启动的C语言阶段 2

1、杂碎

在这里插入图片描述

(1) smp。smp 就是对称多处理器(其实就是我们说的多核心 CPU)。

多核处理器也称片上多核处理器(Chip Multi-Processor,CMP)。

1.多核处理器的流行
多核出现前,商业化处理器都致力于单核处理器的发展,其性能已经发挥到极致,仅仅提高单核芯片的
速度会产生过多热量且无法带来相应性能改善,但CPU性能需求大于CPU发展速度。尽管增加流水线提
高频率,但缓存增加和漏电流控制不力造成功率大幅增加,性能反而不如之前低频率的CPU。功率增
加,散热问题也严重了,风冷已经不能解决问题了。

那么新技术必须出现-多核处理器。早在1996年就有第一款多核CPU原型Hydra。2001年IBM推出第
一个商用多核处理器POWER4,2005年Intal和AMD多核处理器大规模应用。

多核处理器越来越流行,无论在服务器、桌面、上网本、平板、手机还是医疗设备、国防、航天
等方面。

我们来了解一下基础知识。

2.多核处理器分类-同构、异构
从硬件的角度来看,多核设计分为两类。如果所有的核心或CPU具有相同的构架,那么定义为同构多核
(homogeneous);如果架构不同,那么称为异构(heterogeneous)多核。

从应用来看,同构多核处理器中大多数由通用处理器核构成,每个核可以独立运行,类似单核处理器
。而异构多核处理器往往同时继承了通用处理器、DSP、FPGA、媒体处理器、网络处理器等。每个内
核针对不同的需求设定的,从而提高应用的计算性能或实时性能。

目前的异构多处理器有:TI的达芬奇平台DM6000系列(ARM9+DSP)、Xilinx的Zynq7000系列(
双核Cortex-A9+FPGA)、Cell处理器(1个64位POWERPC+8个32位协处理器)等等。

同构多处理器就比较多了,Exynos4412,freescale i.mx6 dual和quad系列、TI的OMAP4460
等,Intel的Core Duo、Core2 Duo等。


从软件的角度来看,多核处理器的运行模式有三种:

SMP-对称多处理,symmetric multi-processing。

AMP-非对称多处理,asymmetric multi-processing

BMP-边界多处理(翻译不确定),bound multi-processing

参考链接:https://www.cnblogs.com/zamely/p/4334979.html


(2) lockdep。锁定依赖,是一个内核调试模块,处理内核自旋锁死锁问题相关的。

(3) cgroup。control group,内核提供的一种来处理进程组的技术。


2、打印内核版本信息

(1) 代码位于:kernel/init/main.c 中的 572 行。

在这里插入图片描述


(2) printk 函数是内核中用来从 console 打印信息的,类似于应用层编程中的 printf 。内核编程时,不能使用标准库函数,因此不能使用 printf,其实 printk 就是内核自己实现的一个 printf。


(3) printk 函数的用法和 printf 几乎一样,不同之处在于:可以在参数最前面用一个宏来定义消息输出的级别。为什么要有这种级别?

主要原因是 linux 内核太大了,代码量太多,里面的 printk 打印信息太多了。如果所有的 printk 都能打印出来,而不加任何限制,则最终内核启动后得到海量的输出信息。


(4) 为了解决打印信息过多,无效信息会淹没 有效信息的这个问题,linux 内核的解决方案是给每一个 printk 添加一个打印级别。级别定义 0-7(注意编程的时候要用相应的宏定义,不要直接用数字)分别代表 8 种输出的重要性级别,0 表示最重要,7 表示最不重要。我们在 printk 的时候自己根据自己的消息的重要性去设置打印级别。

在这里插入图片描述


(5) linux 的控制台监测消息的地方,也有一个消息过滤显示机制,控制台实际只会显示级别比我的控制台定义的级别高的消息。譬如说控制台的消息显示级别设置为 4,那么只有 printk 中消息级别为 0-3(也可能是 0-4)的才可以显示看见,其余的被过滤掉了。


(6) linux_banner 的内容解析。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


三、内核启动的C语言阶段 3

1、setup_arch 函数简介

在这里插入图片描述

(1) 从名字看,这个函数是 CPU 架构相关的一些创建过程。

(2) 实际上这个函数是用来确定我们当前内核的机器(arch、machine)的。

我们的 linux 内核会支持一种 CPU 的运行,“CPU+开发板” 就确定了一个硬件平台,然后我们当前配置的内核 就在这个平台上可以运行。之前说过的机器码,就是给这个硬件平台一个固定的编码,以表征这个平台。

(3) 当前内核支持的机器码以及硬件平台相关的一些定义都在这个函数中处理。

在这里插入图片描述

在这里插入图片描述


2、Machine 查找

(1) setup_processor 函数用来查找 CPU 信息,可以结合串口打印的信息来分析。

在这里插入图片描述

在这里插入图片描述


(2) setup_machine 函数的传参是机器码编号,machine_arch_type 符号在include/generated/mach-types.h 的 32039-32050 行定义了。经过分析后确定这个传参值就是 2456.

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


(3) 函数的作用是 通过传入的机器码编号,找到对应这个机器码的 machine_desc 描述符,并且返回这个描述符的指针。


(4) 其实真正干活的函数是 lookup_machine_type ,找这个函数发现在 head-common.S 中,真正干活的函数是 __lookup_machine_type

在这里插入图片描述


(5) __lookup_machine_type 函数的工作原理:内核在建立的时候,就把各种 CPU 架构的信息组织成一个一个的 machine_desc 结构体实例,然后都给一个段属性 .arch.info.init,链接的时候会保证这些描述符会被连接在一起。

__lookup_machine_type 就去那个那些描述符所在处依次挨个遍历各个描述符,比对看机器码哪个相同。

在这里插入图片描述


四、内核启动的C语言阶段 4

1、setup_arch 函数进行了基本的 cmdline 处理

(1) 这里说的 cmdline 就是指的 uboot 给 kernel 传参时,传递的命令行启动参数,也就是 uboot 的 bootargs

在这里插入图片描述


(2) 有几个相关的变量需要注意:
default_command_line:看名字是默认的命令行参数,实际是一个全局变量字符数组,这个字符数组可以用来存东西。

CONFIG_CMDLINE:在 .config 文件中定义的(可以在 make menuconfig 中去更改设置),这个表示内核的一个默认的命令行参数。

在这里插入图片描述


(3) 内核对 cmdline 的处理思路是:内核中自己维护了一个默认的 cmdline(就是 .config 中配置的这一个),然后 uboot 还可以通过 tag 给 kernel 再传递一个 cmdline 。

如果 uboot 给内核传 cmdline 成功,则内核会优先使用 uboot 传递的这一个;如果 uboot 没有给内核传 cmdline 或者传参失败,则内核会使用自己默认的这个 cmdline。以上说的这个处理思路就是在 setup_arch 函数中实现的。

在这里插入图片描述


2、实验验证内核的 cmdline 确定

(1) 验证思路:首先给内核配置时,配置一个基本的 cmdline,然后在 uboot 启动内核时给 uboot 设置一个 bootargs,然后启动内核看打印出来的 cmdline 和 uboot 传参时是否一样。

在这里插入图片描述


(2) 在 uboot 中去掉 bootargs,然后再次启动内核看打印出来的 cmdline 是否和内核中设置的默认的 cmdline 一样。

在这里插入图片描述

在这里插入图片描述


注意:uboot 给内核传递的 cmdline 非常重要,会影响内核的运行,所以要谨慎。有时候内核启动有问题,可以分析下是不是 uboot 的 bootargs 设置不对。

注意:这个传参在这里确定出来之后,还没完。后面还会对这个传参进行解析。解析之后 cmdline 中的每一个设置项都会对内核启动有影响。


五、内核启动的C语言阶段 5

1、setup_command_line

在这里插入图片描述

(1) 也是在处理和命令行参数 cmdline 有关的任务。


2、parse_early_param & parse_args

在这里插入图片描述

(1) 解析 cmdline 传参和其他传参。

(2) 这里的解析,意思是把 cmdline 的细节设置信息给解析出来。譬如 cmdline:console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3,则解析出的内容就是一个字符串数组,数组中依次存放了一个设置项目信息。
console=ttySAC2,115200 一个
root=/dev/mmcblk0p2 rw 一个
init=/linuxrc 一个
rootfstype=ext3 一个


(3) 这里只是进行了解析,并没有去处理。也就是说,只是把长字符串解析成了短字符串,最多和内核里控制这个相应功能的变量挂钩了,但是并没有去执行。执行的代码在各自模块初始化的代码部分。


3、杂碎

在这里插入图片描述

(1) trap_init 设置异常向量表
(2) mm_init 内存管理模块初始化
(3) sched_init 内核调度系统初始化
(4) early_irq_init & init_IRQ 中断初始化
(5) console_init 控制台初始化


总结:start_kernel 函数中调用了很多的 xx_init 函数,全都是内核工作需要的模块的初始化函数。这些初始化之后,内核就具有了一个基本的可以工作的条件了。

如果把内核比喻成一个复杂机器,那么 start_kernel 函数就是把这个机器的众多零部件组装在一起形成这个机器,让它具有可以工作的基本条件。


4、rest_init

在这里插入图片描述

(1) 这个函数之前内核的基本组装已经完成


(2) 剩下的一些工作就比较重要了,放在了一个单独的函数中,叫 rest_init


总结:start_kernel 函数做的主要工作:打印了一些信息、内核工作需要的模块的初始化被依次调用(譬如内存管理、调度系统、异常处理···)、我们需要重点了解的就是 setup_arch 中做的 2 件事情:机器码架构的查找,并且执行架构相关的硬件的初始化、uboot 给内核的传参 cmdline。


六、内核启动的C语言阶段 6

1、操作系统去哪了

(1) rest_init 中调用 kernel_thread 函数启动了 2 个内核线程,分别是:kernel_initkthreadd

在这里插入图片描述


(2) 调用 schedule 函数开启了内核的调度系统,从此 linux 系统开始转起来了。

在这里插入图片描述


(3) rest_init 最终调用 cpu_idle 函数,结束了整个内核的启动。也就是说 linux 内核最终结束于一个函数 cpu_idle 。这个函数里面肯定是死循环。

在这里插入图片描述


(4) 简单来说,linux 内核最终的状态是:有事干的时候,去执行有意义的工作(执行各个进程任务),实在没活干的时候,就去执行死循环(实际上死循环也可以看成是一个任务)。


(5) 之前已经启动了内核调度系统,调度系统会负责考评系统中所有的进程,这些进程里面只有有哪个需要被运行,调度系统就会终止 cpu_idle 死循环进程(空闲进程),转而去执行有意义的干活的进程。这样操作系统就转起来了。


2、什么是内核线程

(1) 进程和线程。简单来理解,一个运行的程序就是一个进程。所以进程就是任务、进程就是一个独立的程序。独立的意思就是这个程序和别的程序是分开的,这个程序可以被内核单独调用执行或者暂停。

(2) 在 linux 系统中,线程和进程非常相似,几乎可以看成是一样的。实际上我们当前讲课用到的进程和线程的概念就是一样的。

(3) 进程/线程就是一个独立的程序。应用层运行一个程序,就构成一个用户进程/线程,那么内核中运行一个函数(函数其实就是一个程序),就构成了一个内核进程/线程

(4) 所以我们 kernel_thead 函数运行一个函数,其实就是把这个函数变成了一个内核线程去运行起来,然后它可以被内核调度系统去调度。说白了就是去调度器注册了一下,以后人家调度的时候会考虑你。


3、进程0、进程1、进程2

在这里插入图片描述

(1) 截至目前为止,我们一共涉及到 3 个内核进程/线程。


(2) 操作系统是用一个数字来表示/记录一个进程/线程的,这个数字就被称为这个进程的进程号。这个号码是从 0 开始分配的。因此这里涉及到的三个进程,分别是 linux 系统的进程0、进程1、进程2.


(3) 在 linux 命令行下,使用 ps 命令可以查看当前 linux 系统中运行的进程情况。


(4) 我们在 ubuntu 下 ps -aux 可以看到当前系统运行的所有进程,可以看出进程号是从 1 开始的。为什么不从 0 开始,因为进程 0 不是一个用户进程,而属于内核进程。

在这里插入图片描述


(5) 三个进程

进程 0:进程 0 其实就是刚才讲过的 idle 进程,叫空闲进程,也就是死循环。
进程 1:kernel_init 函数就是进程 1,这个进程被称为 init 进程。
进程 2:kthreadd 函数就是进程 2,这个进程是 linux 内核的守护进程。这个进程是用来保证 linux 内核自己本身能正常工作的。

在这里插入图片描述

在这里插入图片描述


总结 1:本节课的重点在于,理解 linux 内核启动后达到的一个稳定状态。注意去对比内核启动后的稳定状态和 uboot 启动后的稳定状态的区别。

总结 2:本节课的第二个重点就是初步理解进程/线程的概念。

总结 3:你得明白每个进程有个进程号,进程号从 0 开始依次分配的。明白进程 0 是 idle 进程(idle 进程是干嘛的);进程 2 是 ktheadd 进程(基本明白干嘛的就行)

总结 4:分析到此,发现后续的事都在进程 1. 所以后面课程会重点从进程 1 出发,分析之后发生的事情。


七、init 进程详解 1

在这里插入图片描述

1、init 进程完成了从内核态向用户态的转变

(1) 一个进程 2 种状态。init 进程刚开始运行的时候是内核态,它属于一个内核线程;然后它自己运行了一个用户态下面的程序后,把自己强行转成了用户态。

因为 init 进程自身完成了从内核态到用户态的过度,因此后续的其他进程都可以工作在用户态下面了。


(2) 内核态下做了什么?重点就做了一件事情,就是挂载根文件系统,并试图找到用户态下的那个 init 程序。

init 进程要把自己转成用户态,就必须运行一个用户态的应用程序(这个应用程序名字一般也叫 init),要运行这个应用程序就必须得找到这个应用程序,要找到它就必须得挂载根文件系统,因为所有的应用程序都在文件系统中。

内核源代码中的所有函数都是内核态下面的,执行任何一个函数都不能脱离内核态。应用程序必须不属于内核源代码,这样才能保证应用程序自己是用户态。

也就是说,我们这里执行的这个 init 程序和内核不在一起,它是另外提供的。提供这个 init 程序的就是根文件系统。


(3) 用户态下做了什么?init 进程大部分有意义的工作,都是在用户态下进行的。init 进程对我们操作系统的意义在于:其他所有的用户进程都直接或者间接派生自 init 进程。


(4) 如何从内核态 跳跃到 用户态?还能回来不?

init 进程在内核态下面时,通过一个函数 kernel_execve 来执行一个用户空间编译链接的应用程序,就跳跃到用户态了

在这里插入图片描述

注意这个跳跃过程中,进程号是没有改变的,所以一直是进程 1。这个跳跃过程是单向的,也就是说一旦执行了 init 程序转到了用户态下,整个操作系统就算真正的运转起来了,以后只能在用户态下工作了,用户态下想要进入内核态只有走 API 这一条路了。


2、init 进程构建了用户交互界面

(1) init 进程是其他用户进程的老祖宗。linux 系统中一个进程的创建,是通过其父进程创建出来的。根据这个理论,只要有一个父进程,就能生出一堆子孙进程了。


(2) init 启动了 login 进程、命令行进程、shell进程。


(3) shell 进程启动了其他用户进程。命令行和 shell 一旦工作了,用户就可以在命令行下通过 ./xx 的方式来执行其他应用程序,每一个应用程序的运行 就是一个进程。


总结:本节的主要目的是让大家认识到 init 进程如何一步步发展成为我们平时看到的那种操作系统的样子。


八、init 进程详解 2

1、打开控制台

(1) linux 系统中,每个进程都有自己的一个文件描述符表,表中存储的是本进程打开的文件。

(2) linux 系统中有一个设计理念:一切皆是文件。所以设备也是以文件的方式来访问的。我们要访问一个设备,就要去打开这个设备对应的文件描述符。譬如 /dev/fb0 这个设备文件,就代表LCD显示器设备,/dev/buzzer 代表蜂鸣器设备,/dev/console 代表控制台设备。

(3) 这里我们打开了 /dev/console 文件,并且复制了 2 次文件描述符,一共得到了 3 个文件描述符。这三个文件描述符分别是 0、1、2。这三个文件描述符就是所谓的:标准输入、标准输出、标准错误。

在这里插入图片描述


(4) 进程 1 打开了三个标准输入、输出、错误文件,因此后续的进程 1 衍生出来的所有的进程,默认都具有这 3 个文件描述符。


2、挂载根文件系统

(1) prepare_namespace 函数中挂载根文件系统。

在这里插入图片描述


(2) 根文件系统在哪里?根文件系统的文件系统类型是什么? uboot 通过传参来告诉内核这些信息。

在这里插入图片描述

uboot 传参中的 root=/dev/mmcblk0p2 rw 这一句就是告诉内核,根文件系统在哪里。

uboot 传参中的 rootfstype=ext3 这一句就是告诉内核,rootfs 的类型。


(3) 如果内核挂载根文件系统成功,则会打印出:VFS: Mounted root (ext3 filesystem) on device 179:2.

如果挂载根文件系统失败,则会打印:No filesystem could mount root, tried: yaffs2

在这里插入图片描述


(4) 如果内核启动时,挂载 rootfs 失败,则后面肯定没法执行了,肯定会死。

内核中设置了启动失败休息 5s 自动重启的机制,因此这里会自动重启,所以有时候大家会看到反复重启的情况。

在这里插入图片描述


(5) 如果挂载 rootfs 失败,可能的原因有:
最常见的错误就是,uboot 的 bootargs 设置不对。
rootfs 烧录失败(fastboot 烧录不容易出错,以前是手工烧录很容易出错)
rootfs 本身制作失败的。(尤其是自己做的 rootfs,或者别人给的第一次用)


3、执行用户态下的进程1程序

(1) 上面一旦挂载 rootfs 成功,则进入 rootfs 中寻找应用程序的 init 程序,这个程序就是用户空间的进程 。找到后用 run_init_process 去执行它。

在这里插入图片描述


(2) 我们如果确定 init 程序是谁?方法是:

先从 uboot 传参 cmdline 中看有没有指定,如果有指定,先执行 cmdline 中指定的程序。

在这里插入图片描述

cmdline 中的 init=/linuxrc ,这个就是指定 rootfs 中哪个程序是 init 程序。这里的指定方式就表示我们 rootfs 的根目录下面有个名字叫 linuxrc 的程序,这个程序就是 init 程序。

如果 uboot 传参 cmdline 中没有 init=xx, 或者 cmdline 中指定的这个 xx 执行失败,还有备用方案。

第一备用:/sbin/init,
第二备用:/etc/init,
第三备用:/bin/init,
第四备用:/bin/sh。

如果以上都不成功,则认命了,死了。


九、cmdline 常用参数

1、格式简介

(1) 格式就是由很多个项目,用空格隔开依次排列,每个项目中都是项目名=项目值。

(2) 整个 cmdline 会被内核启动时解析,解析成一个一个的 项目名=项目值 的字符串。这些字符串又会被再次解析从而影响启动过程。

在这里插入图片描述


2、root=

(1) 这个是用来指定根文件系统在哪里的。

(2) 一般格式是 root=/dev/xxx(一般,如果是 nandflash 上,则 /dev/mtdblock2,如果是 inand/sd 的话,则 /dev/mmcblk0p2)。

(3) 如果是 nfs 的 rootfs,则 root=/dev/nfs


3、rootfstype=

(1) 根文件系统的文件系统类型,一般是 jffs2、yaffs2、ext3、ubi。


4、console=

(1) 控制台信息声明,譬如 console=/dev/ttySAC0,115200 表示控制台使用串口0,波特率是115200.

(2) 正常情况下,内核启动的时候会根据 console= 这个项目来初始化硬件,并且重定位 console 到具体的一个串口上,所以这里的传参会影响后续是否能从串口终端上接收到内核的信息。


5、mem=

(1) mem= 用来告诉内核当前系统的内存有多少。


6、init=

(1) init= 用来指定进程 1 的程序 pathname,一般都是 init=/linuxrc


7、常见 cmdline 介绍

(1)

console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3

第一种这种方式对应 rootfs 在 SD/iNand/Nand/Nor 等物理存储器上。这种 对应产品正式出货工作时的情况。


(2)

root=/dev/nfs nfsroot=192.168.1.141:/root/s3c2440/build_rootfs/aston_rootfs ip=192.168.1.10:192.168.1.141:192.168.1.1:255.255.255.0::eth0:off  init=/linuxrc console=ttySAC0,115200 

第二种这种方式对应 rootfs 在 nfs 上,这种 对应我们实验室开发产品做调试的时候。


十、内核中架构相关代码简介

1、内核代码基本分为 3 块

(1) arch。 本目录下全是 cpu 架构有关的代码。

(2) drivers。 本目录下全是硬件的驱动。

(3) 其他。 相同点是,这些代码都和硬件无关,因此系统移植和驱动开发的时候,这些代码几乎都是不用关注的

在这里插入图片描述


2、架构相关的常用目录名及含义

(1) mach。(mach 就是 machine architecture)。arch/arm 目录下的一个 mach-xx 目录就表示一类 machine 的定义,这类 machine 的共同点是:都用 xx 这个 cpu 来做主芯片。(譬如 mach-s5pv210 这个文件夹里面,都是 s5pv210 这个主芯片的开发板 machine);mach-xx 目录里面的一个 mach-yy.c 文件中定义了一个开发板(一个开发板对应一个机器码),这个是可以被扩展的。

在这里插入图片描述


(2) plat(plat 是 platform 的缩写,含义是平台)。plat 在这里可以理解为 SoC,也就是说这个 plat 目录下,都是 SoC 里面的一些硬件(内部外设)相关的一些代码。

在这里插入图片描述

在内核中,把 SoC 内部外设相关的硬件操作代码,就叫做平台设备驱动。

在这里插入图片描述


(3) include。这个 include 目录中的所有代码,都是架构相关的头文件。(linux 内核通用的头文件,在内核源码树根目录下的 include 目录里)。

在这里插入图片描述


在这里插入图片描述


3、补充

(1) 内核中的文件结构很庞大、很凌乱(不同版本的内核,可能一个文件存放的位置是不同的),会给我们初学者带来一定的困扰。

(2) 头文件目录 include 有好几个,譬如:

kernel/include		内核通用头文件
	kernel/arch/arm/include		架构相关的头文件
		kernel/arch/arm/include/asm
			kernel/arch/arm/include/asm/mach
	kernel/arch/arm/mach-s5pv210/include/mach
	kernel/arch/arm/plat-s5p/include/plat

(3) 内核中包含头文件时,有一些格式:

#include <linux/kernel.h>		kernel/include/linux/kernel.h
#include <asm/mach/arch.h>		kernel/arch/arm/include/asm/mach/arch.h
#include <asm/setup.h>			kernel/arch/arm/include/asm/setup.h
#include <plat/s5pv210.h>		kernel/arch/arm/plat-s5p/include/plat/s5pv210.h

(4) 有些同名的头文件是有包含关系的,有时候我们需要包含某个头文件时,可能并不是直接包含它,而是包含一个包含了它的头文件。


源自朱有鹏老师.

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

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

相关文章

Ansible批量部署采集器

千台服务器部署采集器的时候用到了 Ansible&#xff0c;简单记录一下。 安装 Ansible pip install ansible yum install ansible –y在 /etc/ansible/hosts 中添加被管理组 &#xff0c;比如图中[web] 是组的名字。 执行ansible命令测试&#xff0c;通过hosts中定义的web组执…

君子签「数据签」正式上线,推动企业电子合同实现高效安全签署

近年来&#xff0c;从政府机关到企事业单位&#xff0c;数字化转型步伐明显加快。 在数字化的驱动下&#xff0c;在各行业的服务过程中&#xff0c;君子签对产品部署有了更深的沉淀和经验&#xff0c;通过不断打磨、升级产品&#xff0c;「数据签」产品于近日正式上线&#xf…

Word这样用,提高效率不加班

Word这样用&#xff0c;提高效率不加班 今天给大家分享23条Word文档的应用小技巧。对于大家来说&#xff0c;掌握些技巧能够效率百倍&#xff0c;何乐不为&#xff1f; 这些技巧是本人通过整理一直在用并且使用频率较高的&#xff0c;也希望能帮到大家。有兴趣的小伙伴可以自己…

Python | 基于LendingClub数据的分类预测研究Part01——问题重述+特征选择+算法对比

欢迎交流学习~~ 专栏&#xff1a; 机器学习&深度学习 本文利用Python对数据集进行数据分析&#xff0c;并用多种机器学习算法进行分类预测。 具体文章和数据集可以见我所发布的资源&#xff1a;发布的资源 Python | 基于LendingClub数据的分类预测研究Part01——问题重述特…

多路I/O转接 Epoll

基本概述 epoll是Linux下多路复用IO接口select/poll的增强版本&#xff0c;它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率&#xff0c;因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被监听的文件描述符集合…

从输入url到页面展现(二)找服务器其实是在找IP地址

前言 前一节我们讲述了url的规则&#xff0c;url的几种类型&#xff0c;以及访问web服务器的时候&#xff0c;如果用户访问的url如果不包含index.html的话&#xff0c;大概会如何去寻找这个url对应的文件&#xff0c;如果感兴趣的同学可以回头去看上一篇&#xff1a;从输入url…

PyTorch中的可视化工具

目录 一、网络结构的可视化 1.1 通过HiddenLayer可视化网络 1.2 通过PyTorchViz可视化网络 二、训练过程可视化 2.1 通过tensorboardX可视化训练过程 2.2 HiddenLayer可视化训练过程 三、使用Visdom进行可视化 一、网络结构的可视化 我们训练神经网络时&#xff0c;除…

xshell是什么软件

xshell是什么软件? Xshell 是一个强大的远程管理软件&#xff0c;它支持SSH&#xff0c;TELNET 协议。Xshell可以在Windows下访问远端服务器、路由器、网络机顶盒等&#xff0c;类似的常用软件还有putty&#xff0c;以及Windows下的Telnet。 下面简单介绍一下xshell软件。 X…

康耐视Designer,通过VC5与三菱Q系列PLC-SLMP通讯说明

测试使用软件版本 Designer Version: 2.7 GX Works2 Version: 1.77F 测试使用硬件 Cognex Vision Controller VC5 CIC-5000R Mitsubishi PLC: Q06UDEHCPU PLC端设置(内置以太网口型号) 1.新建一个工程,选择对应的PLC系列和PLC类型: 2.PLC参数设置(以太网设置):…

LabVIEW-数值控件和布尔控件

简介 LabVIEW 以其强大、开放、图形化的虚拟仪器软件开发环境使得无论是否有过编程经验的工程师或科学家使用它时都可以快速、高效地与测量和控制硬件通信&#xff0c;并进行复杂的数据分析及处理。LabVIEW集成了满足GPIB、PXI、VXI、RS232、RS485、USB、DAQ等多种形式的设备互…

MATLAB算法实战应用案例精讲-【自动驾驶】激光雷达LiDAR(补充篇)

目录 前言 几个高频面试题目 自动驾驶中的传感器&#xff1a;LiDAR和 Radar的区别 LiDAR Radar 性能对比 激光雷达中是如何做到和GPS时间同步的&#xff1f; 一、三种方案PPSGPRMC、PTP、gPTP 二、同步过程 算法原理 发展历程 国内外厂商 算法思想 测距 三角测…

【博学谷学习记录】超强总结,用心分享丨人工智能 AI项目 前向概率计算笔记

目录前向概率模型基础参数公式推导代码实现前向概率 给定隐马尔可夫模型λ\lambdaλ&#xff0c;定义到时刻ttt部分观测序列为o1,o2,⋯,oto_1,o_2,\cdots,o_to1​,o2​,⋯,ot​且状态为sis_isi​的概率为前向概率&#xff0c;记作αt(i)P(o1,o2,⋯,ot,itsi∣λ)\alpha_t(i)P(o…

GDPU C语言 天码行空9

填空题 1. 指针排序 数组 输入 n5 30 85 12 77 6输出 6 12 30 77 85 &#x1f920; 代码 #include<stdio.h>#define N 10void sort(int *x,int n)// *x 是 数组 a 的地址 {int i,j,k,t;for(i0;i<n-1;i)//从前往后枚举 坑位{ki; for(ji1;j<n;j) if(x[k…

计算机网络 实验五

⭐计网实验专栏&#xff0c;欢迎订阅与关注&#xff01; ★观前提示&#xff1a;本篇内容为计算机网络实验。内容可能会不符合每个人实验的要求&#xff0c;因此以下内容建议仅做思路参考。 一、实验目的 理解DNS的域名解析机制&#xff0c;理解DHCP的工作机制熟悉WEB应用及超…

逍遥自在学C语言 | 位运算符>>的高级用法

前言 在上一篇文章中&#xff0c;我们介绍了<<运算符的高级用法&#xff0c;本篇文章&#xff0c;我们将介绍>> 运算符的一些高级用法。 一、人物简介 第一位闪亮登场&#xff0c;有请今后会一直教我们C语言的老师 —— 自在。 第二位上场的是和我们一起学习的小…

HCIP-6.8BGP的团体属性、BGP联盟

BGP的团体属性、BGP联盟1、Community:团体属性1.1、案例配置2、BGP联盟属性2.1、配置案例&#xff1a;2.2、四种类型的AS_PATH&#xff1a;对于大型网络或者路由条目较多&#xff0c;使用一种BGP特有的路由标记&#xff0c;用于简化路由策略的执行。对于减少路由条目&#xff0…

unity,制作一个环状滑动条

介绍 unity&#xff0c;制作一个环状滑动条 方法 1.导入png图片素材2.新建一个滑动条&#xff0c;两者图片都设置为图片3.调节slider的参数4.调节backgroud的参数5.fill area、fill的参数同上。 得到两个叠加的圆环。6.设置fill的背景颜色为红色7.设置fill填充方式&#xff0…

【C++】容器适配器之priority_queue 仿函数

一、priority_queue 的介绍和使用 1.priority_queue 的介绍 我们和学习之前的容器一样&#xff0c;可以使用cplusplus官网进行学习&#xff1a;priority_queue文档介绍 priority_queue(优先级队列)是一种容器适配器&#xff0c;它 和queue使用同一个头文件&#xff0c;其底层…

Elastic(ELK) Stack 架构师成长路径

Elastic Stack&#xff08;ELK Stack&#xff09;是一个开源的日志分析平台&#xff0c;由 Elasticsearch、Logstash 和 Kibana 三个组件组成&#xff0c;主要用于数据搜索、分析和可视化。要成为一名 ELK Stack 架构师&#xff0c;需要遵循一定的成长路径&#xff0c;以便逐步…

详解HiveSQL执行计划

一、前言 Hive SQL的执行计划描述SQL实际执行的整体轮廓&#xff0c;通过执行计划能了解SQL程序在转换成相应计算引擎的执行逻辑&#xff0c;掌握了执行逻辑也就能更好地把握程序出现的瓶颈点&#xff0c;从而能够实现更有针对性的优化。此外还能帮助开发者识别看似等价的SQL其…