哈工大操作系统实验三(整理自用)

news2024/11/18 12:46:25

一、实验内容

  • 基于模板 process.c 编写多进程的样本程序,实现如下功能: + 所有子进程都并行运行,每个子进程的实际运行时间一般不超过 30 秒; + 父进程向标准输出打印所有子进程的 id,并在所有子进程都退出后才退出;
  • 在 Linux0.11 上实现进程运行轨迹的跟踪。 + 基本任务是在内核中维护一个日志文件 /var/process.log,把从操作系统启动到系统关机过程中所有进程的运行轨迹都记录在这一 log 文件中。
  • 在修改过的 0.11 上运行样本程序,通过分析 log 文件,统计该程序建立的所有进程的等待时间、完成时间(周转时间)和运行时间,然后计算平均等待时间,平均完成时间和吞吐量。可以自己编写统计程序,也可以使用 python 脚本程序—— stat_log.py(在 /home/teacher/ 目录下) ——进行统计。
  • 修改 0.11 进程调度的时间片,然后再运行同样的样本程序,统计同样的时间数据,和原有的情况对比,体会不同时间片带来的差异。

/var/process.log 文件的格式必须为:

PID     X       Time

其中:

  • pid 是进程的 ID;
  • X 可以是 N、J、R、W 和 E 中的任意一个,分别表示进程新建(N)、进入就绪态(J)、进入运行态(R)、进入阻塞态(W) 和退出(E);
  • time 表示 X 发生的时间。这个时间不是物理时间,而是系统的滴答时间(tick);

三个字段之间用制表符分隔。例如:

12    N    1056
12    J    1057
4    W    1057
12    R    1057
13    N    1058
13    J    1059
14    N    1059
14    J    1060
15    N    1060
15    J    1061
12    W    1061
15    R    1061
15    J    1076
14    R    1076
14    E    1076
......

二、实验环境搭建(Ubuntu虚拟机)

ubuntu下 Linux 0.11 编译内核 - 实验环境搭建_~橘子~的博客-CSDN博客如果想学习Linux 0.11,实验环境是必不可少的。最好是能编译,能运行,能调试。本文用的是Mooc网上的哈工大李治军老师操作系统!李老师的讲课风格浅入深出。1.下载Linux 0.11用github下载https://github.com/Wangzhike/HIT-Linux-0.11 下载命令行下载 如果没有git,那么先安装gitsudo apt-get instal...https://blog.csdn.net/qq_39557240/article/details/85336730

三、先备知识

在init/目录下有一个main.c文件,系统在执行完boot/目录下的head.s程序后就会将执行权交给main.c,这个程序包含了内核初始化的所有工作。

  • 多进程如何启动?

 main中的fork()创建了操作系统中的0进程的第一个子进程,在这个子进程中,执行了init(),init中又执行了Shell(Windows桌面),Shell这个进程再启动其他进程,这样就形成了多进程图像。

这里简单介绍一下fork()函数,fork()系统调用用于创建子进程,它的返回值有两个,父进程返回创建子进程的pid,子进程返回0。这里if(!fork())就表示在子进程中调用init()函数。

  • 多进程如何组织?

多进程的组织依赖于一些重要的数据结构,比如PCB(进程控制块)、队列和状态。

进程控制块(PCB):是用来记录进程信息的一种数据结构,它在linux-0.11内核中的include/linux/sched.h中,结构如下:

状态:这里值进程的状态,进程的状态有五种:新建态、就绪态、运行态、阻塞态、终止态。

 注:上面的箭头表示状态的转换方向

队列:因为CPU在同一时刻只能运行一个进程。除了处于运行态的进程,其他的进程都排队等着执行。

        但是每个进程的状态又是不一样的,如果进程已经准备就绪,只是还没有被分配CPU,这个进程的PCB就处于就绪队列上,等着被CPU调度。如果进程正在等待一个事件发生,从而发生了阻塞,需要这个事件发生来唤醒,那么这个进程PCB处于阻塞队列上,等着被唤醒,唤醒后则进入就绪态。

因此每个队列上的元素都是进程的PCB,随着进程状态的改变,这些PCB也会随之出队入队。

这样操作系统就把多个进程有序的组织起来了。

  • 多进程如何交替?

进程交替需要三个步骤:队列操作+调度+切换

 队列操作:就是将进程PCB放入对应状态的等待队列中,随着进程状态转换出队入队的操作。

调度:当一个进程因为要等待某个事件的发生(比如读写磁盘的IO操作)或者时间片用完,擦偶作系统会使用剥夺当前进程的CPU使用权,使用调度算法(FIFO、短作业优先等策略)在就绪队列中选出下一个进程使用CPU。

切换:当进程切换时,需要保存运行时的一些状态和数据,而这些都保存在CPU的寄存器中,这时,被剥夺CPU的进程需要把CPU这些寄存器的值保存在自己的PCB中,而获得CPU使用权的进程,为了从上次运行的状态继续运行,则需要将自己PCB中的寄存器的值赋值给CPU。

  • 多进程如何影响?

 

 实验相关源码分析:

1.实验目的是要将进程的每一次状态变换和该状态持续的时间都记录在log文件中,因此要求操作系统启动后先打开/var/process.log,然后就可以在每个进程发生状态切换的时候写入记录,为了能尽早开始记录,应该在内核启动时就打开log文件,内核入口是init/main.c的main函数:

将下面红色区域的代码剪切到上面 move_to_user_mode();后面,然后其后跟着添加打开log文件的代码:

(void) open("/var/process.log",O_CREAT|O_TRUNC|O_WRONLY,0666);

 这样,文件描述符0,1,2,3就在进程0中创建了。根据fork()原理,进程0创建的任意子进程都会继承这些文件描述符,因此在init()中就不必再open()了。此后,init()的后续代码和/bin/sh都会重新初始化它们。所以只有进程0和进程1的文件描述符关联着log文件。

2.编写fprintk()函数,所有的状态转移都是在内核环境下发生的,而想要把每一次状态切换的记录写入log文件,内核不能使用write()函数,就像内核状态下只能调用printk()而不能调用printf()函数一样。因此这个fprintk()函数实现的功能就是在内核中写log文件。下面是代码:

#include "linux/sched.h"
#include "sys/stat.h"

static char logbuf[1024];
int fprintk(int fd, const char *fmt, ...)
{
    va_list args;
    int count;
    struct file * file;
    struct m_inode * inode;

    va_start(args, fmt);
    count=vsprintf(logbuf, fmt, args);
    va_end(args);
/* 如果输出到stdout或stderr,直接调用sys_write即可 */
    if (fd < 3)
    {
        __asm__("push %%fs\n\t"
            "push %%ds\n\t"
            "pop %%fs\n\t"
            "pushl %0\n\t"
        /* 注意对于Windows环境来说,是_logbuf,下同 */
            "pushl $logbuf\n\t"
            "pushl %1\n\t"
        /* 注意对于Windows环境来说,是_sys_write,下同 */
            "call sys_write\n\t"
            "addl $8,%%esp\n\t"
            "popl %0\n\t"
            "pop %%fs"
            ::"r" (count),"r" (fd):"ax","cx","dx");
    }
    else
/* 假定>=3的描述符都与文件关联。事实上,还存在很多其它情况,这里并没有考虑。*/
    {
    /* 从进程0的文件描述符表中得到文件句柄 */
        if (!(file=task[0]->filp[fd]))
            return 0;
        inode=file->f_inode;

        __asm__("push %%fs\n\t"
            "push %%ds\n\t"
            "pop %%fs\n\t"
            "pushl %0\n\t"
            "pushl $logbuf\n\t"
            "pushl %1\n\t"
            "pushl %2\n\t"
            "call file_write\n\t"
            "addl $12,%%esp\n\t"
            "popl %0\n\t"
            "pop %%fs"
            ::"r" (count),"r" (file),"r" (inode):"ax","cx","dx");
    }
    return count;
}

 3.在进程状态切换处,将进程的声明周期写入日志。涉及到的文件有fork.c、sched.c、exit.c

新建态:用户使用fork()函数,新建的进程处于新建态

就绪态:进程新建之后进入就绪态、进程阻塞事件发生进入就绪态、进程时间片用完进入就绪态等等

阻塞态:进程等待资源或事件会进入阻塞态

运行态:进程被调度占用CPU进入运行态

 四、实验步骤

 1.修改init.c文件。

将init.c文件中的init()的以下代码移动到main()函数中,并添加代码打开process.log文件。

2.在printf.c里面添加上面fprintk的代码:

3.将进程状态切换信息写入process.log文件:

修改fork文件:

 

修改sched.c文件:

 

 

 

 4.在linux-0.11目录下执行make all重新编译,若没报错则执行下一步。

5.编写process.c文件,这个文件可以放在oslab下,为了把这个文件拷贝到linux-0.11系统中,需要挂载系统硬盘,最后启动linux-0.11系统,在这个系统里编译运行process.c文件。

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/times.h>

#define HZ	100

void cpuio_bound(int last, int cpu_time, int io_time);

int main(int argc, char * argv[])
{
	pid_t n_proc[10]; /*10个子进程 PID*/
	int i;
	for(i=0;i<10;i++)
	{
		n_proc[i] = fork();
		/*子进程*/
		if(n_proc[i] == 0)
		{
			cpuio_bound(20,2*i,20-2*i); /*每个子进程都占用20s*/
			return 0; /*执行完cpuio_bound 以后,结束该子进程*/
		}
		/*fork 失败*/
		else if(n_proc[i] < 0 )
		{
			printf("Failed to fork child process %d!\n",i+1);
			return -1;
		}
		/*父进程继续fork*/
	}
	/*打印所有子进程PID*/
	for(i=0;i<10;i++)
		printf("Child PID: %d\n",n_proc[i]);
	/*等待所有子进程完成*/
	wait(&i);  /*Linux 0.11 上 gcc要求必须有一个参数, gcc3.4+则不需要*/ 
	return 0;
}

/*
 * 此函数按照参数占用CPU和I/O时间
 * last: 函数实际占用CPU和I/O的总时间,不含在就绪队列中的时间,>=0是必须的
 * cpu_time: 一次连续占用CPU的时间,>=0是必须的
 * io_time: 一次I/O消耗的时间,>=0是必须的
 * 如果last > cpu_time + io_time,则往复多次占用CPU和I/O
 * 所有时间的单位为秒
 */
void cpuio_bound(int last, int cpu_time, int io_time)
{
	struct tms start_time, current_time;
	clock_t utime, stime;
	int sleep_time;

	while (last > 0)
	{
		/* CPU Burst */
		times(&start_time);
		/* 其实只有t.tms_utime才是真正的CPU时间。但我们是在模拟一个
		 * 只在用户状态运行的CPU大户,就像“for(;;);”。所以把t.tms_stime
		 * 加上很合理。*/
		do
		{
			times(&current_time);
			utime = current_time.tms_utime - start_time.tms_utime;
			stime = current_time.tms_stime - start_time.tms_stime;
		} while ( ( (utime + stime) / HZ )  < cpu_time );
		last -= cpu_time;

		if (last <= 0 )
			break;

		/* IO Burst */
		/* 用sleep(1)模拟1秒钟的I/O操作 */
		sleep_time=0;
		while (sleep_time < io_time)
		{
			sleep(1);
			sleep_time++;
		}
		last -= sleep_time;
	}
}

挂载代码:sudo ./mount-hdc

将process文件用cp指令复制到hdc/usr/root目录下,然后在oslab目录执行./run,启动系统。

 执行process之后,会生成log文件(这个文件在hdc/var目录下,退出linux-0.11,hdc会自动卸载,再挂载一次就行了),但注意一定要刷新,然后将log文件拷贝到oslab目录下。

 6.编写python文件stat_log.py(这个文件放在oslab下),统计进程的运行信息。

#!/usr/share/python
import sys
import copy

P_NULL = 0
P_NEW = 1
P_READY = 2
P_RUNNING = 4
P_WAITING = 8
P_EXIT = 16

S_STATE = 0
S_TIME = 1

HZ = 100

graph_title = r"""
-----===< COOL GRAPHIC OF SCHEDULER >===-----

             [Symbol]   [Meaning]
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~
             number   PID or tick
              "-"     New or Exit 
              "#"       Running
              "|"        Ready
              ":"       Waiting
                    / Running with 
              "+" -|     Ready 
                    \and/or Waiting

-----===< !!!!!!!!!!!!!!!!!!!!!!!!! >===-----
"""

usage = """
Usage: 
%s /path/to/process.log [PID1] [PID2] ... [-x PID1 [PID2] ... ] [-m] [-g]

Example:
# Include process 6, 7, 8 and 9 in statistics only. (Unit: tick)
%s /path/to/process.log 6 7 8 9

# Exclude process 0 and 1 from statistics. (Unit: tick)
%s /path/to/process.log -x 0 1

# Include process 6 and 7 only and print a COOL "graphic"! (Unit: millisecond)
%s /path/to/process.log 6 7 -m -g

# Include all processes and print a COOL "graphic"! (Unit: tick)
%s /path/to/process.log -g
"""

class MyError(Exception):
    pass

class DuplicateNew(MyError):
    def __init__(self, pid):
	    args = "More than one 'N' for process %d." % pid
	    MyError.__init__(self, args)

class UnknownState(MyError):
    def __init__(self, state):
	    args = "Unknown state '%s' found." % state
	    MyError.__init__(self, args)

class BadTime(MyError):
    def __init__(self, time):
	    args = "The time '%d' is bad. It should >= previous line's time." % time
	    MyError.__init__(self, args)

class TaskHasExited(MyError):
    def __init__(self, state):
	    args = "The process has exited. Why it enter '%s' state again?" % state
	    MyError.__init__(self, args)

class BadFormat(MyError):
    def __init__(self):
	    args = "Bad log format"
	    MyError.__init__(self, args)

class RepeatState(MyError):
	def __init__(self, pid):
		args = "Previous state of process %d is identical with this line." % (pid)
		MyError.__init__(self, args)

class SameLine(MyError):
	def __init__(self):
		args = "It is a clone of previous line."
		MyError.__init__(self, args)

class NoNew(MyError):
	def __init__(self, pid, state):
		args = "The first state of process %d is '%s'. Why not 'N'?" % (pid, state)
		MyError.__init__(self, args)

class statistics:
	def __init__(self, pool, include, exclude):
		if include:
			self.pool = process_pool()
			for process in pool:
				if process.getpid() in include:
					self.pool.add(process)
		else:
			self.pool = copy.copy(pool)

		if exclude:
			for pid in exclude:
				if self.pool.get_process(pid):
					self.pool.remove(pid)
	
	def list_pid(self):
		l = []
		for process in self.pool:
			l.append(process.getpid())
		return l

	def average_turnaround(self):
		if len(self.pool) == 0:
			return 0
		sum = 0
		for process in self.pool:
			sum += process.turnaround_time()
		return float(sum) / len(self.pool)

	def average_waiting(self):
		if len(self.pool) == 0:
			return 0
		sum = 0
		for process in self.pool:
			sum += process.waiting_time()
		return float(sum) / len(self.pool)
	
	def begin_time(self):
		begin = 0xEFFFFF
		for p in self.pool:
			if p.begin_time() < begin:
				begin = p.begin_time()
		return begin

	def end_time(self):
		end = 0
		for p in self.pool:
			if p.end_time() > end:
				end = p.end_time()
		return end

	def throughput(self):
		return len(self.pool) * HZ / float(self.end_time() - self.begin_time())

	def print_graphic(self):
		begin = self.begin_time()
		end = self.end_time()

		print graph_title

		for i in range(begin, end+1):
			line = "%5d " % i
			for p in self.pool:
				state = p.get_state(i)
				if state & P_NEW:
					line += "-"
				elif state == P_READY or state == P_READY | P_WAITING:
					line += "|"
				elif state == P_RUNNING:
					line += "#"
				elif state == P_WAITING:
					line += ":"
				elif state & P_EXIT:
					line += "-"
				elif state == P_NULL:
					line += " "
				elif state & P_RUNNING:
					line += "+"
				else:
					assert False
				if p.get_state(i-1) != state and state != P_NULL:
					line += "%-3d" % p.getpid()
				else:
					line += "   "
			print line

class process_pool:
	def __init__(self):
		self.list = []
	
	def get_process(self, pid):
		for process in self.list:
			if process.getpid() == pid:
				return process
		return None

	def remove(self, pid):
		for process in self.list:
			if process.getpid() == pid:
				self.list.remove(process)

	def new(self, pid, time):
		p = self.get_process(pid)
		if p:
			if pid != 0:
				raise DuplicateNew(pid)
			else:
				p.states=[(P_NEW, time)]
		else:
			p = process(pid, time)
			self.list.append(p)
		return p

	def add(self, p):
		self.list.append(p)

	def __len__(self):
		return len(self.list)
	
	def __iter__(self):
		return iter(self.list)

class process:
	def __init__(self, pid, time):
		self.pid = pid
		self.states = [(P_NEW, time)]
	
	def getpid(self):
		return self.pid

	def change_state(self, state, time):
		last_state, last_time = self.states[-1]
		if state == P_NEW:
			raise DuplicateNew(pid)
		if time < last_time:
			raise BadTime(time)
		if last_state == P_EXIT:
			raise TaskHasExited(state)
		if last_state == state and self.pid != 0: # task 0 can have duplicate state
			raise RepeatState(self.pid)

		self.states.append((state, time))

	def get_state(self, time):
		rval = P_NULL
		combo = P_NULL
		if self.begin_time() <= time <= self.end_time():
			for state, s_time in self.states:
				if s_time < time:
					rval = state
				elif s_time == time:
					combo |= state
				else:
					break
			if combo:
				rval = combo
		return rval

	def turnaround_time(self):
		return self.states[-1][S_TIME] - self.states[0][S_TIME]

	def waiting_time(self):
		return self.state_last_time(P_READY)

	def cpu_time(self):
		return self.state_last_time(P_RUNNING)

	def io_time(self):
		return self.state_last_time(P_WAITING)

	def state_last_time(self, state):
		time = 0
		state_begin = 0
		for s,t in self.states:
			if s == state:
				state_begin = t
			elif state_begin != 0:
				assert state_begin <= t
				time += t - state_begin
				state_begin = 0
		return time


	def begin_time(self):
		return self.states[0][S_TIME]

	def end_time(self):
		return self.states[-1][S_TIME]
		
# Enter point
if len(sys.argv) < 2:
	print usage.replace("%s", sys.argv[0])
	sys.exit(0)

# parse arguments
include = []
exclude = []
unit_ms = False
graphic = False
ex_mark = False

try:
	for arg in sys.argv[2:]:
		if arg == '-m':
			unit_ms = True
			continue
		if arg == '-g':
			graphic = True
			continue
		if not ex_mark:
			if arg == '-x':
				ex_mark = True
			else:
				include.append(int(arg))
		else:
			exclude.append(int(arg))
except ValueError:
	print "Bad argument '%s'" % arg
	sys.exit(-1)

# parse log file and construct processes
processes = process_pool()

f = open(sys.argv[1], "r")

# Patch process 0's New & Run state
processes.new(0, 40).change_state(P_RUNNING, 40)

try:
	prev_time = 0
	prev_line = ""
	for lineno, line in enumerate(f):

		if line == prev_line:
			raise SameLine
		prev_line = line

		fields = line.split("\t")
		if len(fields) != 3:
			raise BadFormat

		pid = int(fields[0])
		s = fields[1].upper()

		time = int(fields[2])
		if time < prev_time:
			raise BadTime(time)
		prev_time = time

		p = processes.get_process(pid)

		state = P_NULL
		if s == 'N':
			processes.new(pid, time)
		elif s == 'J':
			state = P_READY
		elif s == 'R':
			state = P_RUNNING
		elif s == 'W':
			state = P_WAITING
		elif s == 'E':
			state = P_EXIT
		else:
			raise UnknownState(s)
		if state != P_NULL:
			if not p:
				raise NoNew(pid, s)
			p.change_state(state, time)
except MyError, err:
	print "Error at line %d: %s" % (lineno+1, err)
	sys.exit(0)

# Stats
stats = statistics(processes, include, exclude)
att = stats.average_turnaround()
awt = stats.average_waiting()
if unit_ms:
	unit = "ms"
	att *= 1000/HZ
	awt *= 1000/HZ
else:
	unit = "tick"
print "(Unit: %s)" % unit
print "Process   Turnaround   Waiting   CPU Burst   I/O Burst"
for pid in stats.list_pid():
	p = processes.get_process(pid)
	tt = p.turnaround_time()
	wt = p.waiting_time()
	cpu = p.cpu_time()
	io = p.io_time()

	if unit_ms:
		print "%7d   %10d   %7d   %9d   %9d" % (pid, tt*1000/HZ, wt*1000/HZ, cpu*1000/HZ, io*1000/HZ)
	else:
		print "%7d   %10d   %7d   %9d   %9d" % (pid, tt, wt, cpu, io)
print "Average:  %10.2f   %7.2f" % (att, awt)
print "Throughout: %.2f/s" % (stats.throughput())

if graphic:
	stats.print_graphic()

运行这个文件:python2 stat_log.py process.log,得到实验结果。

 参考博客:

(112条消息) 操作系统实验四 进程运行轨迹的跟踪与统计(哈工大李治军)_Casten-Wang的博客-CSDN博客https://blog.csdn.net/leoabcd12/article/details/120107478

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

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

相关文章

SpringBoot项目做成Docker 镜像

1.使用Xshell5使用 put指令上传到Centos put D:\git\repository_idea\emsms\target\emsms-0.0.1-SNAPSHOT.jar 2.编辑文件 vim spring ROM java:8 ADD emsms-0.0.1-SNAPSHOT.jar dd.jar CMD java -jar dd.jar 示例&#xff1a; 3.退出并保存编辑 4.将文件打包成镜像 dock…

Flink中FileSink的使用

在Flink中提供了StreamingFileSink用以将数据流输出到文件系统. 这里结合代码介绍如何使用FileSink. 首先FileSink有两种模式forRowFormat和forBulkFormat public static <IN> DefaultRowFormatBuilder<IN> forRowFormat(final Path basePath, final Encoder<IN…

Labview视觉一键尺寸测量仪,多产品,多尺寸,快速编辑, 测量,导出结果

这是一个关于LabVIEW视觉一键尺寸测量仪的描述&#xff0c;它具有以下特点&#xff1a;支持多种产品和尺寸的测量&#xff0c;可以快速进行编辑、测量和导出结果。 这个领域涉及到的知识点和领域范围包括&#xff1a;LabVIEW、视觉测量、尺寸测量、编辑功能和结果导出。 LabV…

HCIP-7.4交换机STP生成树协议原理

HCIP-7.4交换机STP生成树协议原理 1、什么是交换机生成树&#xff1f;2、STP生成树2.1、标准生成树基本计算过程(802.1D)2.2、STP的基本概念2.3、 BPDU格式及字段说明2.4、 STP的选举原则2.4.1 配置案例说明2.4.2 华为设备的COST值 2.5、端口状态描述2.6、cost值修改2.6.1、非根…

【UCOS-III】自我学习笔记→第20讲→时间管理

文章目录 前言实验步骤1.复制任务创建和删除工程文件并删除task3任务&#xff0c;修改任务1和任务2的优先级为22.修改任务1和任务2的内容3.查看示波器现象 测试代码工程文件总结 前言 无&#xff0c;仅作记录&#xff0c;不具有参考价值&#xff0c;所用开发板为STM32F411RET6…

计算机视觉:分割一切AI大模型segment-anything

1 segment-anything介绍 Segment Anything Model (SAM)来源于Facebook公司Meta AI实验室。据Mata实验室介绍&#xff0c;SAM 已经学会了关于物体的一般概念&#xff0c;并且它可以为任何图像或视频中的任何物体生成 mask&#xff0c;甚至包括在训练过程中没有遇到过的物体和图…

刷题日记《链表01》

题目概述&#xff08;力扣&#xff09; 给定循环单调非递减列表中的一个点&#xff0c;写一个函数向这个列表中插入一个新元素 insertVal &#xff0c;使这个列表仍然是循环升序的。 给定的可以是这个列表中任意一个顶点的指针&#xff0c;并不一定是这个列表中最小元素的指针。…

SpringCloud-13_Alibaba OSS

对象存储 OSS 就是所谓的“图床”吗&#xff1f;&#xff08;致敬yupi /捂脸&#xff09; 一图说明&#xff1a; 阿里云对象存储oos 阿里云对象存储 OSS&#xff08;Object Storage Service&#xff09;是一款海量、安全、低成本、高可靠的云存储服务&#xff0c;提供最高可达 …

汉王人脸考勤管理系统 万能密码登录 漏洞复现

fofa&#xff1a;title“汉王人脸考勤管理系统” 漏洞复现 登录页面&#xff1a; 使用万能密码登录 用户名&#xff1a;or’ or 11– 密码&#xff1a;or

Minio的使用

今天学习的时候用到了阿里云的OSS&#xff0c;由于在公司项目上用到了Minio作为云端文件服务器&#xff0c;因此学习了以下Minio&#xff0c;打算替换掉阿里云的OSS 1.Minio的安装 在这里提供了Docker-compose的模式作为Minio的下载(其中9002作为API请求接口端&#xff0c;90…

Matlab【旅行商问题】—— 基于模拟退火算法的无人机药品配送路线最优化

文章目录 问题描述模拟退火算法Metropolis准则算法流程图&#xff1a; Demo1&#xff1a;只考虑累计距离&#xff0c;通过模拟退火算法求解最短路径matlab代码&#xff1a;最优解之一&#xff1a;适应度进行曲线&#xff1a; Demo1&#xff1a;考虑每个站点的病人数量&#xff…

Visual modflow Flex地下水数值模拟及参数优化、抽水实验设计与处理、复杂的饱和/非饱和地下水流分析

专题一 地下水数值软件的操作流程、建模步骤和所需资料处理及相关注意事项 [1] Visual MODFLOW Flex特征 [2] Visual MODFLOW Flex软件界面及模块 [3] 地下水数值模拟的建模步骤及数据需求 专题二 模型建模操作方法 技巧、真实案例演练、特殊问题处理 [1] 直接模型建模的操…

深入浅出设计模式 - 装饰者模式

博主介绍&#xff1a; ✌博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家✌ Java知识图谱点击链接&#xff1a;体系化学习Java&#xff08;Java面试专题&#xff09; &#x1f495;&#x1f495; 感兴趣的同学可以收…

Python桌面应用开发之PyQt

文章目录 引言&#xff1a;桌面应用开发三大框架介绍一、PyQT介绍二、安装三、使用教程(1)基础窗口(2)分区布局窗口(类似于html中div的使用)(3)栅格布局窗口(类似于html中的table)(4)表单布局窗口(类似于html中的form)(5)事件函数与事件过滤器(6)信号和槽四、实战示例(1)状态栏…

跨链协议悄然升级

当前加密产业公链百家争鸣&#xff0c;群雄割据&#xff0c;每条公链都拥有自身的忠实用户。 然而&#xff0c;公链与公链之间仿佛一座座孤岛&#xff0c;无法进行无缝的交流和联系&#xff0c;仅能通过跨链桥经由在两条不同的链上运用不同处理机制来协助转移用户的资产。但&a…

【深度学习 | CNN】“深入解析卷积神经网络与反卷积:从原理到应用的全面指南” (从一维、二维、三维讲解)

🤵‍♂️ 个人主页: @AI_magician 📡主页地址: 作者简介:CSDN内容合伙人,全栈领域优质创作者。 👨‍💻景愿:旨在于能和更多的热爱计算机的伙伴一起成长!!🐱‍🏍 🙋‍♂️声明:本人目前大学就读于大二,研究兴趣方向人工智能&硬件(虽然硬件还没开始玩…

MySQL数据库的MHA高可用集群部署及故障切换(图文详解!绝对详细!!)

目录 一、MHA概述 1、MHA简介 2、MHA 的组成 &#xff08;1&#xff09;MHA Node&#xff08;数据节点&#xff09; &#xff08;2&#xff09;MHA Manager&#xff08;管理节点&#xff09; 3、MHA 的特点 二、 搭建MySQLMHA 1、实验思路 2、修改mysql节点的主机名 3&…

JS 事件委托

JavaScript事件委托&#xff08;Event delegation&#xff09;又叫事件代理&#xff0c;是一种在父元素上监听事件&#xff0c;然后通过事件冒泡机制来处理子元素的事件的技术。通过事件委托&#xff0c;可以避免为每个子元素都绑定事件处理程序&#xff0c;提高性能并简化代码…

SC7504运算放大器(OPA)可pin对pin兼容OPA4350

SC750x 系列轨至轨 CMOS 运算放大器针对低电压单电源运行进行了优化。轨至轨输入和输出、低噪声(5nV/√Hz) 和高速运行(38MHz, 22V/μs) 使得运算放大器非常适合驱动模数 (A/D) 转换器。可pin对pin兼容OPA4350。而且也适用于手机功率放大器 (PA) 控制环路和视频处理&#xff08…

【嵌入式Qt开发入门】初识Qt——Linux下安装Qt

Qt 是什么&#xff1f; Qt 是一个跨平台的 C开发库。主要用来开发图形用户界面&#xff08;Graphical User Interface&#xff0c;简 称 GUI&#xff09;程序。 Qt 虽然经常被当做一个 GUI 库&#xff0c;用来开发图形界面应用程序&#xff0c;但这并不是 Qt 的全部&#xff1b…