MIT6.824 Spring2021 Lab 1: MapReduce

文章目录

    • 0x00 准备
    • 0x01 MapReduce简介
    • 0x02 RPC
    • 0x03 调试
    • 0x04 代码
      • coordinator.go
      • rpc.go
      • worker.go

0x00 准备

  • 阅读MapReduce论文
  • 配置GO环境

因为之前没用过GO,所以 先在网上学了一下语法A Tour of Go

感觉Go的接口和方法的语法和C++挺不一样, 并发编程也挺有意思

0x01 MapReduce简介

需要实现master和coordinator。

MapReduce分为两个阶段:Map和Reduce阶段。

Map阶段函数提供Key,比如pg-being_ernest.txt是key,然后Worker通过这个Key获取Value。比如pg-being_ernest.txt的具体内容。然后将Key和Value(在例子中是文章的内容),传递给map function。获取结果,并将结果分成R个Reduce内容。

举个例子。假设我们要对pg-being_ernest.txtpg-dorian_gray.txt统计词频。那么就要有两个Map Task(不一定有两个Worker,比如有3个Worker,那么就是2个Worker干活一个围观;如果只有一个Worker,那么该Worker会被前后分配两次Map操作)。假设有3个Reduce操作,那么Map的中间操作就会按照key被分为3个文件。

pg-being_ernest.txt对应Map0 , Map0操作的kv被分进mr-0-0,mr-0-1,mr-0-2

pg-dorian_gray.txt对应Map0 , Map0操作的kv被分进mr-1-0,mr-1-1,mr-1-2

当所有的Mapf已经生成结果,Worker就会被指派Reduce操作。比如被指派的Reduce操作编号为2,那么Reduce就会读取mr-0-2,mr-1-2。并且聚合相同的Key,传递给Reduce函数。

比如,pg-being_ernest.txt中的map操作有kv,a 1 b 1 b 1输出到mr-0-2pg-dorian_gray.txt中的map操作有kv,c 1 b 1 c 1输出到mr-0-2

然后Task编号为2的Reduce任务会读取所有对应的中间文件。得到key。a 1 b 1 b 1 c 1 b 1 c 1。然后再对要处理的key进行排序,得到 a 1 b 1 b 1 b 1 c 1 c 1。再按照相同的key调用reduce函数。

上面例子的调用为

reducef(key:"a",value:list[1]),得到"1"

reducef(key:"b",value:list[1,1,1]),得到"3"

reducef(key:"c",value:list[1,1]),得到"2"

最后将kvs:[{“a”,“1”},{“b”,“3”},{“c”,“2”}]写入该reduce生成的文件mr-out-2

0x02 RPC

使用GO的RPC库,可以简单地实现Server

学习时参考了Go 每日一库之 rpc - 知乎 (zhihu.com)

在MapReduce操作流程中就是:

  1. 首先启动多个Worker(以下简称C)和一个Coordinator(以下简称S)
  2. C每隔一段时间(比如1s)会向S发送一个任务请求
    1. S首先检查Map任务还有没有分配完(注意不是运行完)。如果没有,分配一个Map任务给C
    2. 如果Map任务分配完了,并且还没有工作完,S让C等待
    3. 如果Map工作完了。Reduce还没分配完了,S给C分配一个空闲的Reduce任务
    4. 如果Reduce都工作完了,所有任务也都结束了。
  3. 如果C完成了任务,会向S发送一个请求。S知道了某个任务完成,就会进行相应的操作标记。

一些注意的点:

每个任务是有时间上限的(10s)。每分配一个任务就会启动一个GO程,然后等待相应的时间,检查是否完成了工作。如果没完成,将该任务编号重新加入管道。

如何判断一个任务是否完成呢?

比如第一个Worker申请到了任务1,过了10s钟还没有完成,S又将任务1加入待完成管道。此时第2个worker申请到了任务1,又过了4s,第一个Worker发送一个MapDone的请求给S。S如何判断是否完成了该任务。

我的处理是维护任务是由哪个Worker运行的状态。其中Worker由RPC的时间戳标记。比如worker1在第一次请求时时间戳为13213123,Server维护maptask[1]是由13213123正在运行,当第一次超时,maptask[1]变成了worker2请求时的时间戳``13219889。在第14s,收到MapDone的请求,检查其时间戳为13213123`和当前正在运行的时间戳不同,所以丢弃掉该结果。

还有就是并发处理,这个使用锁就行了。

0x03 调试

  1. 命令行的参数:(因为不用shell的话不能用通配符pg*.txt代替,只能输入所有文件名)
pg-being_ernest.txt
pg-dorian_gray.txt
pg-frankenstein.txt
pg-grimm.txt
pg-huckleberry_finn.txt
pg-metamorphosis.txt
pg-sherlock_holmes.txt
pg-tom_sawyer.txt
  1. 在调试时出现报错

cannot load plugin ./wc.so err: plugin.Open("./wc"): plugin was built with a different version of package internal/abi

是因为build wc.so时的参数和运行mr参数不一致导致的。

  1. 使用./test-mr-many.sh 3重复测试3次。通过测试

image-20230903010226505

感觉Lab1做下来还是挺通透。像是引入GO和相关概念。通过lab,学习到了GO调试。

0x04 代码

coordinator.go

package mrimport ("log""sync""time"
)
import "net"
import "os"
import "net/rpc"
import "net/http"type status int // 用于指示worker的状态const (notStart status = iotarunningtaskDone
)
const workMaxTime = 12 * time.Secondtype Coordinator struct {// Your definitions here.nReduce          int // Reduce数量mMap             int // Map数量taskDone         boolreduceTaskStatus []statusmapTaskStatus    []status// runningMap 是当前正在running的rpcId// 想一下这种情况:第一个worker没有在10秒内返回结果,于是master开始把同样的任务返回给了第二个worker,此时又过了几秒,比如两秒钟// 那么master如何判断是第二个worker完成了任务,还是第一个worker呢?runningMap    []RpcIdTrunningReduce []RpcIdTmapTasks      chan TaskIdT // 待开始的mapreduceTasks   chan TaskIdT // 待开始的reducefiles         []string     // 要进行task的文件mapCnt        int          // 已完成的map数量reduceCnt     int          // 已完成的reduce数量latch         *sync.Cond
}// Your code here -- RPC handlers for the worker to call.// Example
// an example RPC handler.
//
// the RPC argument and reply types are defined in rpc.go.
func (c *Coordinator) Example(args *ExampleArgs, reply *ExampleReply) error {reply.Y = args.X + 1return nil
}// Appoint 用于worker请求一个任务
func (c *Coordinator) Appoint(request *ReqArgs, reply *ResArgs) error {reply.ResId = request.ReqIdreply.MapNumM = c.mMapreply.ReduceNumN = c.nReducec.latch.L.Lock()done := c.taskDonec.latch.L.Unlock()if done {reply.ResOp = WorkDonereturn nil}switch request.ReqOp {case WorkReq:{// 请求一个任务c.latch.L.Lock()if len(c.mapTasks) > 0 {// 如果map任务还没有完全分配 分配一个map workertaskId := <-c.mapTasksreply.ResTaskId = taskIdreply.ResContent = c.files[taskId]reply.ResOp = WorkMapc.runningMap[taskId] = reply.ResIdc.mapTaskStatus[taskId] = runningc.latch.L.Unlock()go c.checkDone(WorkMap, reply.ResTaskId)log.Printf("Assign map \t%d to \t%d\n", reply.ResTaskId, reply.ResId)return nil}if c.mapCnt < c.mMap {// 如果map任务已经全部分配完了,但是还没有运行完成,还不能开始reduce// worker需要暂时等待一下reply.ResOp = WorkNothingc.latch.L.Unlock()log.Println("Map All assigned but not done")return nil}if len(c.reduceTasks) > 0 {// 已经确定完成了所有map,还没有分配完reducetaskId := <-c.reduceTasksreply.ResTaskId = taskIdreply.ResOp = WorkReducec.runningReduce[taskId] = reply.ResIdc.reduceTaskStatus[taskId] = runningc.latch.L.Unlock()go c.checkDone(WorkReduce, reply.ResTaskId)log.Printf("Assign reduce \t%d to \t%d\n", reply.ResTaskId, reply.ResId)return nil}// 如果分配完了所有的reduce,但是还没有done.worker需要等待reply.ResOp = WorkNothinglog.Println("Reduce All assigned but not done")c.latch.L.Unlock()return nil}case WorkMapDone:{c.latch.L.Lock()defer c.latch.L.Unlock()if c.runningMap[request.ReqTaskId] != request.ReqId || c.mapTaskStatus[request.ReqTaskId] != running {// 说明该map已经被abortreply.ResOp = WorkTerminatereturn nil}log.Printf("Work Map \t%d done by \t%d\n", request.ReqTaskId, request.ReqId)c.mapTaskStatus[request.ReqTaskId] = taskDonec.mapCnt++}case WorkReduceDone:{c.latch.L.Lock()defer c.latch.L.Unlock()if c.runningReduce[request.ReqTaskId] != request.ReqId || c.reduceTaskStatus[request.ReqTaskId] != running {// 说明该map已经被abortreply.ResOp = WorkTerminatereturn nil}c.reduceTaskStatus[request.ReqTaskId] = taskDonec.reduceCnt++log.Printf("Work Reduce \t%d done by \t%d\n", request.ReqTaskId, request.ReqId)if c.reduceCnt == c.nReduce {c.taskDone = truereply.ResOp = WorkDone}}default:return nil}return nil
}// start a thread that listens for RPCs from worker.go
func (c *Coordinator) server() {log.Println("Launching Server")e := rpc.Register(c)if e != nil {log.Fatal("register error:", e)}rpc.HandleHTTP()//l, e := net.Listen("tcp", ":1234")sockname := coordinatorSock()_ = os.Remove(sockname)l, e := net.Listen("unix", sockname)go func(l net.Listener) {for {time.Sleep(5 * time.Second)if c.Done() {err := l.Close()if err != nil {log.Fatal("close error:", err)}}}}(l)if e != nil {log.Fatal("listen error:", e)}go func() {err := http.Serve(l, nil)if err != nil {log.Fatal("server error:", err)}}()
}// Done main/mrcoordinator.go calls Done() periodically to find out
// if the entire job has finished.
func (c *Coordinator) Done() bool {c.latch.L.Lock()defer c.latch.L.Unlock()// Your code here.return c.taskDone
}// checkDone 检查任务是否完成
func (c *Coordinator) checkDone(workType WorkType, t TaskIdT) {time.Sleep(workMaxTime)c.latch.L.Lock()defer c.latch.L.Unlock()switch workType {case WorkMap:{if c.mapTaskStatus[t] != taskDone {c.mapTaskStatus[t] = notStartc.mapTasks <- t}}case WorkReduce:{if c.reduceTaskStatus[t] != taskDone {// 如果没有完成任务c.reduceTaskStatus[t] = notStartc.reduceTasks <- t}}default:log.Panicf("Try Check Invalid WorkType %v\n", workType)}}// MakeCoordinator create a Coordinator.
// main/mrcoordinator.go calls this function.
// nReduce is the number of reduce tasks to use.
func MakeCoordinator(files []string, nReduce int) *Coordinator {log.Println("Launching Master Factory")c := Coordinator{}c.nReduce = nReducec.mMap = len(files) // 每个file对应一个mapc.taskDone = falsec.files = filesc.mapTasks = make(chan TaskIdT, c.mMap)c.mapTaskStatus = make([]status, c.mMap)c.runningMap = make([]RpcIdT, c.mMap)c.reduceTaskStatus = make([]status, nReduce)c.reduceTasks = make(chan TaskIdT, nReduce)c.runningReduce = make([]RpcIdT, nReduce)c.latch = sync.NewCond(&sync.Mutex{})for i := 0; i < c.mMap; i++ {c.mapTasks <- TaskIdT(i)c.runningMap[i] = -1c.mapTaskStatus[i] = notStart}for i := 0; i < c.nReduce; i++ {c.reduceTasks <- TaskIdT(i)c.runningReduce[i] = -1c.reduceTaskStatus[i] = notStart}c.server()return &c
}

rpc.go

package mr//
// RPC definitions.
//
// remember to capitalize all names.
//import "os"
import "strconv"//
// example to show how to declare the arguments
// and reply for an RPC.
//type ExampleArgs struct {X int
}type ExampleReply struct {Y int
}
type RpcIdT int64 // RpcIdT 是通过时间戳生成的, 指示一个唯一的RpcId
type ReqArgs struct {ReqId     RpcIdTReqOp     WorkTypeReqTaskId TaskIdT
}// ResArgs 是RPC的返回
// Response
type ResArgs struct {ResId      RpcIdTResOp      WorkTypeResTaskId  TaskIdT // 分配的任务编号ResContent stringReduceNumN int // 有n个reduceMapNumM    int // 有M个map任务
}
type WorkType int// TaskIdT 是对任务的编号
type TaskIdT int// 枚举工作类型
const (WorkNothing    WorkType = iotaWorkReq                 // worker申请工作WorkMap                 // 分配worker进行map操作WorkReduce              // 分配worker进行reduce操作WorkDone                // [[unused]]master所有的工作完成WorkTerminate           // 工作中止WorkMapDone             // Worker完成了map操作WorkReduceDone          // Worker完成了reduce操作
)// Rpc exports struct we need
type Rpc struct {Req ReqArgsRes ResArgs
}// Cook up a unique-ish UNIX-domain socket name
// in /var/tmp, for the coordinator.
// Can't use the current directory since
// Athena AFS doesn't support UNIX-domain sockets.
func coordinatorSock() string {s := "/var/tmp/824-mr-"s += strconv.Itoa(os.Getuid())return s
}

worker.go

package mrimport ("encoding/json""fmt""io""os""sort""strconv""time"
)
import "log"
import "net/rpc"
import "hash/fnv"const sleepTime = 500 * time.Millisecond// KeyValue
// Map functions return a slice of KeyValue
type KeyValue struct {Key   stringValue string
}
type ByKey []KeyValue// Len 通过HashKey进行排序
func (a ByKey) Len() int           { return len(a) }
func (a ByKey) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByKey) Less(i, j int) bool { return ihash(a[i].Key) < ihash(a[j].Key) }// use ihash(key) % NReduce to choose the reduce
// task number for each KeyValue emitted by Map.
func ihash(key string) int {h := fnv.New32a()_, err := h.Write([]byte(key))if err != nil {return 0}return int(h.Sum32() & 0x7fffffff)
}// Worker
// main/mrworker.go calls this function.
func Worker(mapf func(string, string) []KeyValue,reducef func(string, []string) string) {// Your worker implementation here.for {timeStamp := time.Now().Unix()rpcId := RpcIdT(timeStamp)req := ReqArgs{}req.ReqId = rpcIdreq.ReqOp = WorkReq // 请求一个工作res := ResArgs{}ok := call("Coordinator.Appoint", &req, &res)if !ok {// 如果Call发生错误log.Println("Maybe Coordinator Server has been closed")return}switch res.ResOp {case WorkDone:// 所有工作已经完成returncase WorkMap:doMap(rpcId, &res, mapf)case WorkReduce:doReduce(rpcId, &res, reducef)case WorkNothing:// 等待time.Sleep(sleepTime)default:break}time.Sleep(sleepTime)}
}
func doMap(rpcId RpcIdT, response *ResArgs, mapf func(string, string) []KeyValue) {// filename 是response中的文件名filename := response.ResContentfile, err := os.Open(filename)if err != nil {log.Fatalf("cannot open %v", filename)}defer func(file *os.File) {_ = file.Close()}(file)// content读取该文件中的所有内容content, err := io.ReadAll(file)if err != nil {log.Fatalf("cannot read %v", filename)}kvs := mapf(filename, string(content))// 需要将kv输出到n路 中间文件中ofiles := make([]*os.File, response.ReduceNumN)encoders := make([]*json.Encoder, response.ReduceNumN)for i := 0; i < response.ReduceNumN; i++ {// 这里输出的名字是mr-ResTaskId-reduceN// 其中,ResTaskId是0~m的数字oname := "mr-" + strconv.Itoa(int(response.ResTaskId)) + "-" + strconv.Itoa(i)ofiles[i], err = os.Create(oname)if err != nil {log.Fatal("Can't Create Intermediate File: ", oname)}defer func(file *os.File, oname string) {err := file.Close()if err != nil {log.Fatal("Can't Close Intermediate File", oname)}}(ofiles[i], oname)encoders[i] = json.NewEncoder(ofiles[i])}for _, kv := range kvs {ri := ihash(kv.Key) % response.ReduceNumNerr := encoders[ri].Encode(kv)if err != nil {log.Fatal("Encode Error: ", err)return}}req := ReqArgs{ReqId:     rpcId,ReqOp:     WorkMapDone,ReqTaskId: response.ResTaskId,}res := ResArgs{}call("Coordinator.Appoint", &req, &res)
}func doReduce(rpcId RpcIdT, response *ResArgs, reducef func(string, []string) string) {rid := response.ResTaskId // 当前reduce的编号var kva []KeyValuefor i := 0; i < response.MapNumM; i++ {// 读取所有该rid的中间值func(mapId int) {// 读取m-rid的中间值inputName := "mr-" + strconv.Itoa(i) + "-" + strconv.Itoa(int(rid))// 在当前对应r的输出中,获取所有keyifile, err := os.Open(inputName)if err != nil {log.Fatal("Can't open file: ", inputName)}defer func(file *os.File) {err := file.Close()if err != nil {log.Fatal("Can't close file: ", inputName)}}(ifile)dec := json.NewDecoder(ifile)for {var kv KeyValueif err := dec.Decode(&kv); err != nil {break}kva = append(kva, kv) //}}(i)}// 通过hashKey排序sort.Sort(ByKey(kva))intermediate := kva[:]oname := "mr-out-" + strconv.Itoa(int(rid))ofile, err := os.Create(oname)if err != nil {log.Fatal("Can't create file: ", oname)}defer func(ofile *os.File) {err := ofile.Close()if err != nil {log.Fatal("Can't close file: ", oname)}}(ofile)// log.Println("Total kv len: ", len(intermediate))// cnt := 0i := 0for i < len(intermediate) {j := i + 1for j < len(intermediate) && intermediate[j].Key == intermediate[i].Key {j++}var values []stringfor k := i; k < j; k++ {values = append(values, intermediate[k].Value)}// cnt++output := reducef(intermediate[i].Key, values)// this is the correct format for each line of Reduce output._, fprintf := fmt.Fprintf(ofile, "%v %v\n", intermediate[i].Key, output)if fprintf != nil {return}i = j}// log.Println("Unique key count: ", cnt)req := ReqArgs{ReqId:     rpcId,ReqOp:     WorkReduceDone,ReqTaskId: response.ResTaskId,}res := ResArgs{}call("Coordinator.Appoint", &req, &res)
}// CallExample
// example function to show how to make an RPC call to the coordinator.
//
// the RPC argument and reply types are defined in rpc.go.
func CallExample() {// declare an argument structure.args := ExampleArgs{}// fill in the argument(s).args.X = 99// declare a reply structure.reply := ExampleReply{}// send the RPC request, wait for the reply.call("Coordinator.Example", &args, &reply)// reply.Y should be 100.fmt.Printf("reply.Y %v\n", reply.Y)
}// send an RPC request to the coordinator, wait for the response.
// usually returns true.
// returns false if something goes wrong.
func call(rpcName string, args interface{}, reply interface{}) bool {// c, err := rpc.DialHTTP("tcp", "127.0.0.1"+":1234")sockname := coordinatorSock()c, err := rpc.DialHTTP("unix", sockname)if err != nil {log.Fatal("dialing:", err)}defer func(c *rpc.Client) {err := c.Close()if err != nil {log.Fatal("Close Client Error When RPC Calling", err)}}(c)err = c.Call(rpcName, args, reply)if err == nil {return true}fmt.Println(err)return false
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OCR多语言识别模型构建资料收集

OCR多语言识别模型构建 构建多语言识别模型方案 合合&#xff0c;百度&#xff0c;腾讯&#xff0c;阿里这四家的不错 调研多家&#xff0c;发现有两种方案&#xff0c;但是大多数厂商都是将多语言放在一个字典里&#xff0c;构建1w~2W的字典&#xff0c;训练一个可识别多种语…

解决报错之org.aspectj.lang不存在

一、IDEA在使用时&#xff0c;可能会遇到maven依赖包明明存在&#xff0c;但是build或者启动时&#xff0c;报找不存在。 解决办法&#xff1a;第一时间检查Setting->Maven-Runner红圈中的√有没有选上。 二、有时候&#xff0c;明明依赖包存在&#xff0c;但是Maven页签中…

监听Helm release资源

监听Helm release资源 基于helm做部署管理工具时&#xff0c;可能想要管理用户已有环境&#xff0c;这时需要将已有环境中的release信息上报到业务系统中。当用户在环境中部署新的release时&#xff0c;也需要实时监听并上报回来。下面将讲解如何去监听release资源 helm rele…

湖南省副省长秦国文一行调研考察亚信科技

9月5日&#xff0c;湖南省人民政府党组成员、副省长秦国文一行到亚信科技调研考察&#xff0c;亚信科技高级副总裁陈武主持接待。 图&#xff1a;双方合影 在亚信科技创新展示中心&#xff0c;秦国文了解了亚信科技在5G、算力网络、人工智能、大数据等前沿领域的创新探索&…

Git 基本原理和常用操作

Git Git 是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。由 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开源的版本控制软件。 Git 常用操作 git 提交流程&#xff1a;工作区 -> git add 到暂存区 -> gi…

Pytorch中如何加载数据、Tensorboard、Transforms的使用

一、Pytorch中如何加载数据 在Pytorch中涉及到如何读取数据&#xff0c;主要是两个类一个类是Dataset、Dataloader Dataset 提供一种方式获取数据&#xff0c;及其对应的label。主要包含以下两个功能&#xff1a; 如何获取每一个数据以及label 告诉我们总共有多少的数据 Datal…

C++多态【下】

文章目录 1.多态实现的底层1.1初识多态原理1.2深入理解虚函数表1.单继承虚函数表2.探究虚函数表存储数据3.知识点金4.多继承虚函数表 2.题目讲解 1.多态实现的底层 1.1初识多态原理 class Dad { public:virtual void Cook() { cout << "佛跳墙" << en…

Can‘t connect to local MySQL server through socket ‘/tmp/mysql.sock‘

最近在用django框架开发后端时&#xff0c;在运行 $python manage.py makemigrations 命令时&#xff0c;报了以上错误&#xff0c;错误显示连接mysql数据库失败&#xff0c;查看了mysql数据库初始化配置文件my.cnf&#xff0c;我的mysql.sock文件存放路径配置在了/usr/local…

【微服务部署】08-监控与告警

文章目录 1. PrometheusOperator1.1 优势1.2 配置脚本1.3 部署脚本 2. Granfana实现监控看板2.1 Granfana核心特性2.2 部署文件 3. prometheus-net收集自定义指标3.1 组件包3.2 使用场景 目前Kubernetes中最流行的监控解决方案是使用Prometheus和AlertManager 1. PrometheusOpe…

jar包或exe程序设置为windows服务

最近在使用java和python制作客户端时突发奇想&#xff0c;是否能够通过一种方法来讲jar包和exe程序打包成windows服务呢&#xff1f;简单了解了一下是可以的。 首先要用到的是winSW&#xff0c;制作windows服务的过程非常简单&#xff0c;仅需几步制作完成&#xff0c;也不需要…

ESP32-C3的存储器类型

本文主要参考ESP-IDF编程指南&#xff0c;一点小记录。 ESP32-C3的存储器有&#xff1a; ESP-IDF 区分了指令总线&#xff08;IRAM、IROM、RTC FAST memory&#xff09;和数据总线 (DRAM、DROM)。 内部SRAM的一部分是指令RAM(IRAM)。那为什么要把指令放在RAM中&#xff0c;就是…

Zookeeper简述

数新网络-让每个人享受数据的价值 官网现已全新升级—欢迎访问&#xff01; 前 言 ZooKeeper是一个开源的、高可用的、分布式的协调服务&#xff0c;由Apache软件基金会维护。它旨在帮助管理和协调分布式系统和应用程序&#xff0c;提供了一个可靠的平台&#xff0c;用于处理…

QT第二天

1.优化登陆界面&#xff0c;当点击登录按钮后&#xff0c;在该按钮对应的槽函数中&#xff0c;判断账户和密码框内的数据是否为admin和123456&#xff0c;如果账户密码匹配成功&#xff0c;则提示登陆成功并关闭登录界面&#xff0c;如果账户密码匹配失败&#xff0c;则提示登录…

嵌入式开发-11 Linux下GDB调试工具

目录 1 GDB简介 2 GDB基本命令 3 GDB调试程序 1 GDB简介 GDB是GNU开源组织发布的一个强大的Linux下的程序调试工具。 一般来说&#xff0c;GDB主要帮助你完成下面四个方面的功能&#xff1a; 1、启动你的程序&#xff0c;可以按照你的自定义的要求随心所欲的运行程序&#…

介绍PHP

PHP是一种流行的服务器端编程语言&#xff0c;用于开发Web应用程序。它是一种开源的编程语言&#xff0c;具有易学易用的语法和强大的功能。PHP支持在服务器上运行的动态网页和Web应用程序的快速开发。 PHP可以与HTML标记语言结合使用&#xff0c;从而能够生成动态的Web页面&a…

Python Opencv实践 - Harris角点检测

参考资料&#xff1a;https://blog.csdn.net/wsp_1138886114/article/details/90415190 import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/chinease_tower.jpg", cv.IMREAD_COLOR) plt.imshow(img[:,:,::-1])#…

CSS笔记(黑马程序员pink老师前端)浮动,清除浮动

浮动可以改变标签的默认排列方式。浮动元素常与标准流的父元素搭配使用. 网页布局第一准则:多个块级元素纵向排列找标准流&#xff0c;多个块级元素横向排列找浮动。 float属性用于创建浮动框&#xff0c;将其移动到一边&#xff0c;直到左边缘或右边缘触及包含块或另一个浮动框…

xss前十二关靶场练习

目录 一、xss原理和分类 1.原理 2.分类&#xff1a;xss分为存储型和反射型以及dom型 &#xff08;1&#xff09;反射性 &#xff08;2&#xff09;存储型 &#xff08;3&#xff09;dom型 二、靶场关卡练习​编辑 1.第一关 2.第二关 3.第三关 4.第四关 5.第五关 6…

Redis——认识Redis

简单介绍 Redis诞生于2009年&#xff0c;全称是Remote Dictionary Server&#xff0c;远程词典服务器&#xff0c;是一个基于内存的键值型NoSQL数据库。 特征 键值&#xff08;Key-value&#xff09;型&#xff0c;value支持多种不同数据结构&#xff0c;功能丰富单线程&…

对象模型和this指针(个人学习笔记黑马学习)

1、成员变量和成员函数 #include <iostream> using namespace std; #include <string>//成员变量和成员函数分开存储class Person {int m_A;//非静态成员变量 属于类的对象上的static int m_B;//静态成员变量 不属于类的对象上void func() {} //非静态成员函数 不…