linux0.12-7-1
[272页]
第7章 初始化程序
1、main.c主要内核初始化工作。
2、如果能完全理解这里调用的所有程序,那么看完这张内容后应该对Linux内核有了大致的了解。
3、 有一定的C语言知识
4、 需要GNU gcc手册在身边作为参考,因为在内核代码很多地方使用gcc的扩展特性。
例如内联(inline)函数、内联(内嵌)汇编语句等。
7-1 main.c程序
7-1-1 功能描述
1、
(a)main.c程序首先利用前面setup.s程序取得的系统参数设置、系统的根文件设备
以及一些内存全局变量。这些内存变量指明了主内存的开始地址、系统所拥有的内存容量
和作为高速缓冲区内存的末端地址。
如果还定义了虚拟盘,则主内存将适当减少。
整个内存的映像示意图如图7-1所示
内核程序+高速缓冲+虚拟盘+主内存区。
(b)高速缓冲部分还要扣除被显示和ROM BIOS占用的部分。
高速缓冲区是用于磁盘等块设备临时存放数据的地方,
以1K(1024)字节为一个数据块单位。
©主内存区域的内存由内存管理模块mm通过分页机制进行管理分配,
以4K字节为一个内存页面单位。
(d)内核程序可以自由访问高速缓冲中的数据,但需要通过mm才能使用分配到内存页面。
2、main.c进行所有方面的硬件初始化工作
包括陷阱门、块设备、字符设备和tty,还包括人工设置第一个任务task 0。
待所有初始化工作完成后程序就设置中断允许标志以开启中断,
并切换到任务0中运行。
作者建议深入的看,遇到看不懂暂时先放一放。
3、整个内核完成初始化后,内核将执行权切换到用户模式(任务0),即CPU从0特权级
切换到了第3特权级。此时main.c的主程序就工作在任务0中。然后系统第一次调用进程创建
函数fork(),创建出一个用于运行init()的子进程(通常被称为init进程)。
4、 系统整个初始化过程如图7-2所示
看作者
(a)main.c程序首先确定如何分配使用系统物理内存
(b)调用内核各部分的初始化函数分别对内存管理、中断处理、块设备和字符设备、进程管理以及硬盘和软盘等硬件进行初始化处理。
©程序把自己"手工"移动到任务0(进程0)中运行,并使用fork()调用首次创建出进程1(init进程),
(d)init()函数将继续进行应用环境的初始化并执行shell登陆程序。
(e)而原进程0则会在系统空闲时被调度执行,因此进程0通常也被称为idle进程。
5、 init()函数可分为4个部分:安装根文件系统、显示系统信息、
运行系统初始资源配置文件rc中的命令、执行用户登陆shell程序。
(a)代码首先调用系统调用setup((void *) &drive_info);用来收集硬盘设备分区表信息并安装根文件系统。
在安装根文件系统之前,系统会先判断是否需要建立虚拟盘。若编译内核时设置了虚拟盘的大小,
并在前面内核初始化过程中已经开辟了一块内存用在虚拟盘,则内核就会首先尝试把根文件系统加载到内存的虚拟盘区中。
(b)打开一个终端设备tty0,并复制其文件描述符以产生标准输入stdin、标准输出stdout和错误输出stderr设备。
内核随后利用这些描述符在终端上显示一些系统信息,例如高速缓冲区中缓冲块总数、注内存区空闲内存总字节等。
©新建一个进程2,并在其中为建立用户交互使用环境而执行一些初始配置操作,即在用户可以使用shell命令行环境之前,
内核调用/bin/sh程序运行了配置文件etc/rc中设置的命令。rc文件的作业与DOS系统根目录中的AUTOEXEC.BAT文件类似。
这段代码首先通过关闭文件描述符0,并立刻打开文件/etc/rc,从而把标志输入stdin定向到/etc/rc文件上。
这样,所有的标准输入数据都将从该文件中读取。然后内核以非交互形式执行/bin/sh,从而实现执行/etc/rc文件中的命令。
当该文件中的命令执行完毕后,/bin/sh就会立刻退出。因此进程2也就随之结束。
(d)init()函数的最后一部分,等待进程2退出,创建新的进程,执行参数_exit(execve(“/bin/sh”,argv,envp));等待新进程退出,如此循环。
带有’-'标志会在/bin/sh执行时通知它这不是一次普通的运行,而是作为登陆shell运行/bin/sh的。
6、fork()是内联函数,因为创建进程1之前,要求进程0的用户堆栈干净,所以fork()不能以函数形式进行调用。
作者展开代码,且解释。
static inline int fork(void)
{
long __res;
asm volatile(“int $0x80”:“=a”(__res):“0”(__NR_fork));
if (__res>=0)
return (int)__res;
errno = -__res;
return -1;
}
进程init和进程0实际上同时使用着内核代码区内(小于1MB的物理内存)相同的代码和数据物理内存页面(640KB),
只是执行的代码不在一处。当进程init操作用户堆栈时,内核才会分配内存页给进程init。
当进程init或进程2执行过execve()调用后,进程2的代码和数据区位于系统的主内存。
7-1-2 代码注释
佩服赵老师保姆式注释,如果学不会只能怪您自己了。!!!(-:
/** linux/init/main.c** (C) 1991 Linus Torvalds*/
//是为了包括定义在unistd.h中的内嵌汇编代码等信息。
#define __LIBRARY__
//*.h头文件所在的默认目录是include/,则在代码中就不必明确指明其位置。如果不是UNIX的
//标准头文件,则需要指明所在的目录,并用双引号括住。unistd.h是标准符号常数与类型文件。
//其中定义了各种符号常数和类型,并声明了各种函数。如果还定义了符号__LIBRARY__,则还会
//包含系统调用号和内嵌汇编代码syscall0()等。
#include <unistd.h>
#include <time.h>//时间类型头文件。其中主要定义了tm结构和一些有关时间的函数原形。/*
我们需要下面这些内嵌语句-从内核空间创建进程将导致没有写时复制(COPY ON WRITE)!!!
知道执行一个execve调用。这对堆栈可能带来问题。处理方法是在fork()调用后不让main()
使用任何堆栈。因此就不能由函数调用-这意味着fork也要使用内嵌的代码,否则我们在从
fork()退出时就要使用堆栈了。实际上只要pause和fork需要使用内嵌方式,以保证从main()中不会弄乱堆栈,但是我们同时还
定义了其他一些函数。
*/
//fork()展开后
static inline _syscall0(int,fork)
//pause()系统调用;暂停进程的执行,直到收到一个信号。
static inline _syscall0(int,pause)
//setup(void *BIOS)系统调用,仅用于linux初始化(仅在这个程序中被调用)。
static inline _syscall1(int,setup,void *,BIOS)
//sync()系统调用:更新文件系统。
static inline _syscall0(int,sync)#include <linux/tty.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <asm/system.h>
#include <asm/io.h>#include <stddef.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>#include <linux/fs.h>#include <string.h>static char printbuf[1024];//静态字符串数组,用作内核显示信息的缓存。extern char *strcpy();
extern int vsprintf();//送格式化输出到一字符串中
extern void init(void);//函数原形,初始化
extern void blk_dev_init(void);//块设备初始化子程序
extern void chr_dev_init(void);//字符设备初始化
extern void hd_init(void);//硬盘初始化程序
extern void floppy_init(void);//软驱初始化程序
extern void mem_init(long start, long end);//内存管理初始化
extern long rd_init(long mem_start, int length);//虚拟盘初始化
extern long kernel_mktime(struct tm * tm);//计算系统开机启动时间(秒)。
//内核专用sprintf()函数。该函数用于产生格式化信息并输出到指定缓冲区str中。参数'*fmt'
//指定输出将采用的格式,参见标志C语言书籍。该子程序正好是vsprintf如何使用的一个简单
//例子。函数使用vsprintf()将格式化字符串放入str缓冲区,参见第179行上的printf()函数。
static int sprintf(char * str, const char *fmt, ...)
{va_list args;int i;va_start(args, fmt);i = vsprintf(str, fmt, args);va_end(args);return i;
}/** 以下这些数据是在内核引导期间由setup.s程序设置的。*///下面三行分别将指定的线性地址强行转换为给定数据类型的指针,并获取指针所指内容。由于内核代码//被映射到从物理地址零开始的地方,因此这些线性地址正好也是对应的物理地址。//这些指定地址处内存值的含义请参见表6-3(setup程序读取并保存的参数)。//drive_info结构请参见下面第125行。
#define EXT_MEM_K (*(unsigned short *)0x90002)//1MB以后的扩展内存大小KB
#define CON_ROWS ((*(unsigned short *)0x9000e) & 0xff)//选的的控制台屏幕行、列数。
#define CON_COLS (((*(unsigned short *)0x9000e) & 0xff00) >> 8)//
#define DRIVE_INFO (*(struct drive_info *)0x90080)//硬盘参数表32字节内容。
#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)//根文件系统所在设备号。
#define ORIG_SWAP_DEV (*(unsigned short *)0x901FA)//交换文件所在设备号。/*
是啊,是啊,下面这段程序很差劲,但我不知道如何正确地实现,而且好像
它还能运行。如果有关于实时时钟更多资料,那我很感兴趣。这些都是试探
出来的,另外还看了一些bios程序,呵!*/
//这段宏读取CMOS实时时钟信息。outb_p和inb_p是include/asm/io.h中定义的端口输入输出宏。
#define CMOS_READ(addr) ({ \
outb_p(0x80|addr,0x70); \ //0x70是写地址端口号。
inb_p(0x71); \ //0x71是读数据端口号。
})
//定义宏。将BCD码转换二进制数据值。
#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)//该函数取CMOS时钟信息作为开机时间,保存到全局变量startup_time(秒)中。参见后面
//CMOS内存列表说明。其中调用的函数kernel_mktime()用于计算从1970年1月1日0时起到
//开机当日经过的秒数,作为开机时间。
static void time_init(void)
{struct tm time;do {time.tm_sec = CMOS_READ(0);time.tm_min = CMOS_READ(2);time.tm_hour = CMOS_READ(4);time.tm_mday = CMOS_READ(7);time.tm_mon = CMOS_READ(8);time.tm_year = CMOS_READ(9);} while (time.tm_sec != CMOS_READ(0));BCD_TO_BIN(time.tm_sec);BCD_TO_BIN(time.tm_min);BCD_TO_BIN(time.tm_hour);BCD_TO_BIN(time.tm_mday);BCD_TO_BIN(time.tm_mon);BCD_TO_BIN(time.tm_year);time.tm_mon--;startup_time = kernel_mktime(&time);
}
//下面定义一些局部变量。
static long memory_end = 0; //机器具有的物理内存容量(字节数)。
static long buffer_memory_end = 0; //告诉缓冲区末端地址。
static long main_memory_start = 0; //主内存(将用于分页)开始的位置。
static char term[32]; //终端设置字符串(环境参数)。//读取并执行/etc/rc文件时所使用的命令行参数和环境参数。
static char * argv_rc[] = { "/bin/sh", NULL };
static char * envp_rc[] = { "HOME=/", NULL ,NULL };
//运行登陆shell时所使用的命令行参数和环境参数。
//"-"是传递给shell程序sh的一个标志。通过识别该标志,sh程序
//会作为登陆shell执行。其执行过程与在shell提示符下执行sh不一样。
static char * argv[] = { "-/bin/sh",NULL };
static char * envp[] = { "HOME=/usr/root", NULL, NULL };struct drive_info { char dummy[32]; } drive_info;//用于存放硬盘参数表信息。//内核初始化主程序。初始化结束后将以任务0(idle任务即空闲任务)的身份运行。
void main(void) /* 这里确实是void,没错。 */
{ /* 在startup程序(head.s)中就是这样假设的 */
/*
此时中断仍被禁止着,做完必要的设置后就将其开启。*/
//首先保存根文件系统设备号和交换文件设备号,并根据setup.s程序中获取的信息设置控制台终端
//屏幕行、列数环境变量TERM,并用其设置初始init进程中执行etc/rc文件和shell程序使用的
//环境变量,以及复制内存0x90080处的硬盘参数表。
//其中ROOT_DEV已在前面包含进的include/linux/fs.h文件第206行上被声明为extern int,
//而SWAP_DEV在include/linux/mm.h文件内也作了相同声明。这里mm.h文件并没有显示地列在
//本程序前面,因为前面包含进的include/linux/sched.h文件中已经包含有它。 ROOT_DEV = ORIG_ROOT_DEV;//声明在fs/super.c 定义在bootsetup.sSWAP_DEV = ORIG_SWAP_DEV;//声明在mm/swap.c 定义在bootsetup.ssprintf(term, "TERM=con%dx%d", CON_COLS, CON_ROWS);envp[1] = term; envp_rc[1] = term;drive_info = DRIVE_INFO;//复制内存0x90080处的硬盘参数表。
//接着根据机器物理内存容量设置高速缓冲区和主内存区的位置和范围。
//高速缓存末端地址->buffer_memory_end;机器内存容量->memory_end;
//主内存开始地址->main_memory_start;memory_end = (1<<20) + (EXT_MEM_K<<10);//内存大小=1MB+扩展内存(k)*1024字节。memory_end &= 0xfffff000;//忽略不到4KB(1页)的内存数。if (memory_end > 16*1024*1024)//如果内存量超过16MB,则按16MB计。memory_end = 16*1024*1024;if (memory_end > 12*1024*1024) //如果内存>12MB,则设置缓冲区末端=4MBbuffer_memory_end = 4*1024*1024;else if (memory_end > 6*1024*1024)//如果内存>6MB,则设置缓冲区末端=2MBbuffer_memory_end = 2*1024*1024;else//否则设置缓冲区末端=1MBbuffer_memory_end = 1*1024*1024;main_memory_start = buffer_memory_end;//主内存起始位置=缓冲区末端。//如果在Makefile文件中定义了内存虚拟盘符号RAMDISK,则初始化虚拟盘。此时主内存将减少。
//参见kernel/blk_drv_ramdisk.c。
#ifdef RAMDISKmain_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endif
//以下是内核进行所有方面的初始化工作。阅读时最好跟着调用的程序深入进去看,
//若实在看不下去了,就先放一放,继续看下一个初始化调用。--作者保姆式的注释和关系。mem_init(main_memory_start,memory_end);//主内存区初始化。trap_init();//陷阱门(硬件中断量)初始化。blk_dev_init();//块设备初始化。chr_dev_init();//字符设备初始化。tty_init();//tty初始化。time_init();//设置开机启动时间。sched_init();//调度程序初始化buffer_init(buffer_memory_end);//缓冲管理初始化,建内存链表等。hd_init();//硬盘初始化。floppy_init();//软驱动初始化sti();//所有初始化工作都完了,于是开启中断。
//下面过程通过在堆栈中设置的参数,利用中断返回指令启动任务0执行。 move_to_user_mode();//移到用户模式下执行。if (!fork()) { /* 在新建的子进程(任务1即init进程)中执行。 */init();}
//下面代码开始以任务0的身份运行。
/*
注意!!对于任何其他的任务,'pause()'将意味着我们必须等待收到一个信号才会返回就绪态,但
任务0(task0)是唯一例外情况,因为任务0在任何空闲时间里都会被激活,
因此对于任务0'pause()'仅意味着我们返回来查看是否有其他任务可以运行,如果没有的话我们就回到合理,
一直循环执行'pause()'*/for(;;)__asm__("int $0x80"::"a" (__NR_pause):"ax");
}//下面函数产生格式化信息并输出到标准输出设备stdout(1),这里是指屏幕上显示。参数'*fmt'
//指定输出将采用的格式,参见标准C语言书籍。该子程序正好是vsprintf如何使用的一个简单
//例子。该程序使用vsprintf()将格式化的字符串放入printbuf缓冲区,然后用write()将缓冲
//去的内容输出到标准设备(1--stdout)。vsprintf()函数的实现建kernel/vsprintf.c
static int printf(const char *fmt, ...)
{va_list args;int i;va_start(args, fmt);write(1,printbuf,i=vsprintf(printbuf, fmt, args));va_end(args);return i;
}
//在main()中已经进行了系统初始化,包括内存管理、各种硬件设备和驱动程序。init()函数
//运行在任务0第1次创建的子进程(任务1)中。它首先对第一个将要执行的程序(shell)
//的环境进行初始化,然后以登陆shell方式加载该程序并执行之。
void init(void)
{int pid,i;
//setup()是一个系统调用。用于读取硬盘参数包括分区表信息并加载虚拟盘(若存在的话)和
//安装根文件系统设备。该函数用25行上的宏定义,对应函数时sys_setup(),在块设备子目录
//kernel/blk_drv/hd.c,74行。setup((void *) &drive_info);
//下面以读写访问方式打开设备"/dev/tty0",它对应终端控制台。由于这是第一次打开文件
//操作,因此产生的文件句柄号(文件描述符)肯定是0.该句柄是UNIX类操作系统默认的控制
//台标志输入句柄stdin。这里再把它以读和写的方式分别打开是为了复制产生标准输出(写)
//句柄stdou和标志出差输出句柄stderr。函数前面的"(void)"前缀用于表示强制函数无需返回值。(void) open("/dev/tty1",O_RDWR,0);(void) dup(0);//复制句柄,产生句柄1号--stdout标准输出设备。(void) dup(0);//复制句柄,产生句柄2号--stderr标志出错输出设备。//下面打印缓冲区块数和总字节数,每块1024字节,以及主内存区空闲内存字节数。 printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,NR_BUFFERS*BLOCK_SIZE);printf("Free mem: %d bytes\n\r",memory_end-main_memory_start);//函数_exit()退出时的出错码 1-操作未允许;2--文件或目录不存在。if (!(pid=fork())) {//if语句里面是task2//关闭句柄0(stdin)并立刻打开/etc/rc文件的作业是把标志输入stdin重定向到/etc/rc文件。//这样shell程序/bin/sh就可以运行rc文件中的设置的命令。close(0);if (open("/etc/rc",O_RDONLY,0))_exit(1);//若打开文件失败,则退出。execve("/bin/sh",argv_rc,envp_rc);//替换成/bin/sh程序并执行。_exit(2);//若execve()执行失败则退出。}//下面还是父进程(1)执行语句。wait()等待子进程停止或终止,返回值应是子进程的进程号//(pid)。这三局的作用是父进程等待子进程的结束。&i是存放返回状态信息的位置。//如果wait()返回值不等于子进程号,则继续等待。if (pid>0)while (pid != wait(&i))/* nothing */;//如果执行到这里,说明刚创建的子进程的执行已停止或终止了。下面循环中首先再创建一个子进程,
//如果出错,则显示"初始化程序创建子进程失败"信息并继续执行。对于所创建的子进程将关闭所有
//以前还遗留的句柄(stdin,stdou,stderr),新创建一个会后并设置进程组号,
//然后重新打开/dev/tty0作为stdin,并复制成stdou和stderr。再次执行系统解释程序/bin/sh。但这
//次执行所选用的参数和环境数组另选了一套。然后父进程再次运行wait()等待。
//如果子进程又停止了执行,则在标准输出上显示出错信息"子进程pid停止了运行,返回码时i",
//然后继续重试下……,形成"大"死循环。while (1) {if ((pid=fork())<0) {printf("Fork failed in init\r\n");continue;}if (!pid) {//新的子进程。close(0);close(1);close(2);setsid();//创建新的会话期,见后面说明。(void) open("/dev/tty1",O_RDWR,0);(void) dup(0);(void) dup(0);_exit(execve("/bin/sh",argv,envp));}while (1)if (pid == wait(&i))break;printf("\n\rchild %d died with code %04x\n\r",pid,i);sync();//同步操作,刷新缓冲区。}_exit(0); /* 注意! 是_exit(),非exit() *///_exit()和exit()都用于正常终止一个函数。但_exit()直接是一个sys_exit系统调用,而//exit()则通常是普通函数库中的一个函数。它会先执行一些清除操作,例如调用执行各终止处理程序、//关闭所有标准IO等,然后调用sys_exit。
}
7-1-3 其他信息
1、 CMOS信息
(a)0x70是地址端口,0x71是数据端口。
(b)表7-1 CMOS 64字节信息简表
2、 调用fork()创建新进程
(a)fork是创建新进程,但需要用exec()簇函数取执行其他不同的程序。
(b)子进程pid=0,父进程pid=子进程的pid号。
©当程序执行完或有必要终止时就可以调用exit()来退出,
而父进程则可以使用wait()调用来查看或等待子进程的退出,并获取被终止进程的退出状态信息。
3、 关于会话期的概念
_不理解进程组和会话期。
相关文章:

linux0.12-7-1
[272页] 第7章 初始化程序 1、main.c主要内核初始化工作。 2、如果能完全理解这里调用的所有程序,那么看完这张内容后应该对Linux内核有了大致的了解。 3、 有一定的C语言知识 4、 需要GNU gcc手册在身边作为参考,因为在内核代码很多地方使用gcc的扩展…...

设置 文本框 自动填充背景颜色 为白色
关于autofill伪类的 兼容性: 在现代浏览器中,包括Chrome、Safari、Firefox等,都支持:autofill伪类,但在使用时必须加上浏览器前缀-webkit-,即:-webkit-autofill。 在旧版的浏览器中,可能不支持:autofill伪…...

Bitmap引起的OOM问题
作者:向阳逐梦 1.什么是OOM?为什么会引起OOM? 答:Out Of Memory(内存溢出),我们都知道Android系统会为每个APP分配一个独立的工作空间,或者说分配一个单独的Dalvik虚拟机,这样每个APP都可以独立…...

【JavaEE初阶】认识线程(Thread)
目录 🌾 前言 🌾 了解线程 🌈1.1 线程是什么? 🌈1.2 一些基本问题 🌾2、创建线程的方式 🌈 2.1 继承Thread类 🌈 2.2 实现Runnable接口并重写run()方法 🌈 注意…...

自动化运维工具一Ansible Roles实战
目录 一、Ansible Roles概述 1.1.roles官方的目录结构 1.2.Ansible Roles依赖关系 二、Ansible Roles案例实战 2.1.Ansible Roles NFS服务 2.2 Roles Memcached 2.3 Roles-rsync服务 一、Ansible Roles概述 之前介绍了 Playbook 的使用方法,对于批量任务的部…...

json 中有递归parentId节点转 c#实体类时如何处理
如果您有一个具有递归parentId节点的JSON数据,并且您需要将其转换为C#实体类,则可以使用以下方法: 创建一个类来表示JSON对象的节点,包括它的属性和子节点。 public class Node {public int Id { get; set; }public string Name …...

给大家介绍几个手机冷门但好用的小技巧
技巧一:拍照识别植物 手机的拍照识别植物功能是指在使用手机相机时,可以通过对植物进行拍照,并通过植物识别技术,获取植物的相关信息和资料。其主要优点如下: 方便实用:使用拍照识别植物功能,…...

2.3 定点乘法运算
学习目标: 如果我要学习定点乘法运算,我会按照以下步骤进行学习: 确定学习目标:明确学习定点乘法运算的目的和重点,以便有针对性地进行学习。 掌握基础知识:首先需要了解定点数和定点乘法的基础知识&…...

C++每日一练:打家劫室(详解动态规划法)
文章目录 前言一、题目二、分析三、代码总结 前言 这题目出得很有意思哈,打劫也是很有技术含量滴!不会点算法打劫这么粗暴的工作都干不好。 提示:以下是本篇文章正文内容,下面案例可供参考 一、题目 题目名称: 打家…...

Wireshark使用
Capture Filters 语法 <Protocol name><Direction><Hosts><Value><Logical operations><Expressions> e.g 1.tcp src port 443 只抓取来源端口是443的tcp数据 2.not arp 不获取arp数据 3.port 80 获取端口是80的数据,不指…...

这才是 SpringBoot 统一登录鉴权、异常处理、数据格式 的正确姿势
本篇将要学习 Spring Boot 统一功能处理模块,这也是 AOP 的实战环节 用户登录权限的校验实现接口 HandlerInterceptor WebMvcConfigurer 异常处理使用注解 RestControllerAdvice ExceptionHandler 数据格式返回使用注解 ControllerAdvice 并且实现接口 Response…...

Java面试题总结 | Java面试题总结6-MYSQL模块(持续更新)
Mysql 文章目录 Mysql关系型数据库和非关系型数据库的区别什么是ORM?-**mybatis**如何评估一个索引创建的是否合理?Count函数执行效果上:执行效率上:count(主键)和count(列名) 数据库的三大范式Mysql中char和varchar的区别数据库设计或者功能…...

Linux命令集(Linux文件管理命令--mv指令篇)
Linux命令集(Linux文件管理命令--mv指令篇) Linux文件管理命令集(mv指令篇)2. mv(move)1. 文件移动2. 递归移动目录3. 文件目录重命名4. 强制移动5. 备份覆盖的目标文件6. 试探性移动7. 显示移动进度8. 补集操作9. 修改文件的权限…...

不一样的 Git 之间 | GitLab vs GitHub vs Gitee vs GitCode
Git仓库对比:GitLab vs GitHub vs Gitee vs GitCode 在软件开发中,版本控制是必不可少的工具之一。Git作为目前最为流行的版本控制系统,也逐渐成为了开发者们的标配。但是,如何选择一个合适的Git仓库来存储您的代码呢?…...

海尔牵头IEEE P2786国际标准通过Sponsor投票并连任工作组主席
01 海尔牵头IEEE P2786国际标准 通过Sponsor投票 并连任工作组主席 海尔牵头制定的全球首个服装物联网国际标准IEEE P2786《Standard for General Requirements and Interoperability for Internet of Clothing》通过Sponsor投票,标志着该国际标准草案得到了行业…...

倾斜摄影超大场景的三维模型的顶层合并的纹理压缩与抽稀处理技术分析
倾斜摄影超大场景的三维模型的顶层合并的纹理压缩与抽稀处理技术分析 倾斜摄影超大场景的三维模型的顶层合并需要对纹理进行压缩和抽稀处理,以减小数据量和提高数据的传输和展示性能。以下是一种常用的纹理压缩和抽稀处理技术: 1、纹理图集 纹理瓦片化…...

linux命令之iostat详解
iostat 监视系统输入输出设备和CPU的使用情况 推荐Linux命令在线工具:linux在线查询工具 补充说明 iostat命令 被用于监视系统输入输出设备和CPU的使用情况。它的特点是汇报磁盘活动统计情况,同时也会汇报出CPU使用情况。同vmstat一样,ios…...

【C++】程序员必备知识:认识类与对象
【C】程序员必备知识:认识类与对象 ①.面向过程和面向对象②.类的引入③.类的定义Ⅰ.定义方式Ⅱ.命名规则建议: ④.类的访问限定符及封装Ⅰ.访问限定符Ⅱ.封装 ⑤.类的作用域⑥.类的实例化⑦.类的对象大小计算Ⅰ.如何计算?Ⅱ.类对象存储方式Ⅲ…...

python基础实战5-python基本结构
1 if语句 if语句是用来进行判断的,其使用格式如下 if 要判断的条件: 条件成立时,要做的事情 案例一: age 30 print("------if判断开始------") if age > 18:print("我成年了") print("------if判断…...

移动端异构运算技术 - GPU OpenCL 编程(基础篇)
一、前言 随着移动端芯片性能的不断提升,在移动端上实时进行计算机图形学、深度学习模型推理等计算密集型任务不再是一个奢望。在移动端设备上,GPU 凭借其优秀的浮点运算性能,以及良好的 API 兼容性,成为移动端异构计算中非常重要…...

QString类方法和变量简介(全)
QString类方法和变量简介 操作字符串(|append|insert|sprintf|QString::arg()|prepend|replace|trimmed|simplified)查询字符串(startsWith|endsWith|contains|localeAwareCompare|compare)字符串转换 标准C提供了两种字符串:一种是C语言风格的以"\0"字符…...

中移链控制台对接4A平台功能验证介绍
中移链控制台具备单独的注册登录页面,用户可通过页面注册或者用户管理功能模块进行添加用户,通过个人中心功能模块进行用户信息的修改和密码修改等操作,因业务要求,需要对中移链控制台的用户账号进行集中管理,统一由 4…...

必知的Facebook广告兴趣定位技巧,更准确地找到目标受众
在Facebook广告投放中,兴趣定位是非常重要的一环。兴趣定位不仅可以帮助我们找到我们想要的目标受众,还可以帮助我们避免一些常见的坑。今天,就让我们一起来看看必知的Facebook广告兴趣定位技巧,更准确地找到目标受众。 1.不要只关…...

【MySQL】慢查询+SQL语句优化 (内容源自ChatGPT)
慢查询SQL语句优化 1.什么是慢查询2.优化慢查询3.插入数据优化5.插入数据底层是什么6.页分裂7.页合并8.主键优化方式10.count 优化11.order by优化12.group by 优化13.limit优化14.update 优化15.innodb 三大特征 1.什么是慢查询 慢查询是指执行SQL查询语句所需要的时间较长&a…...

HashMap底层源码解析及红黑树分析
HashMap线程不安全,底层数组链表红黑树 面试重点是put方法,扩容 总结 put方法 HashMap的put方法,首先通过key去生成一个hash值,第一次进来是null,此时初始化大小为16,i (n - 1) & hash计算下标值&a…...

科技云报道:一路狂飙的ChatGPT,是时候被监管了
科技云报道原创。 即使你过去从不关注科技领域,但近期也会被一个由OpenAI(美国的一家人工智能公司)开发的人工智能聊天机器人“ChatGPT”刷屏。 与上届“全球网红”元宇宙不同,这位新晋的“全能网友”似乎来势更加凶猛。 互联网…...

第四十四章 管理镜像 - 传入日记传输率
文章目录 第四十四章 管理镜像 - 传入日记传输率传入日记传输率镜像数据库状态 第四十四章 管理镜像 - 传入日记传输率 传入日记传输率 在备份和异步成员的镜像成员状态列表下方,自上次刷新镜像监视器以来日志数据从主服务器到达的速率显示在该成员的传入日志传输…...

加密解密学习笔记
加密种类 对称加密,分组对称加密算法 加密算法 AES(Advanced Encryption Standard)高级加密标准 DES(Data Encryption Standard)数据加密标准 3DES/Triple DEA (Triple Data Encryption Algorithm) 三重数据加密算…...

Spring 属性填充源码分析(简单实用版)
属性填充 属性填充只有 3 种方式 根据名称填充 根据类型填充 思考什么时候会出现呢??? 多见于第三方框架与 Spring集成,举例:Mybatis 与 Spring集成,把 Mapper 接口注册为 BeanDefinition 时候就指定了自…...

【机器学习分支】重要性采样(Importance sampling)学习笔记
重要性采样(importance sampling)是一种用于估计概率密度函数期望值的常用蒙特卡罗积分方法。其基本思想是利用一个已知的概率密度函数来生成样本,从而近似计算另一个概率密度函数的期望值。 想从复杂概率分布中采样的一个主要原因是能够使用…...