从汇编角度看c++20 协程

2023-05-16

从汇编角度看c++20 协程

背景:

在学习c++20 协程的时候,总对协程里边的局部成员存储,以及协程栈恢复有很多疑问,本次从过年arm64角度来分析下,具体情况,本例子也是从腾讯开源库libco 找到一些灵感,期望对初学者理解协程有所帮助

首先看下代码:

c++ 代码

C++
#include <experimental/coroutine>
#include <exception>
#include <iostream>
#include <thread>
#include <memory>
struct Generator {
 
  class ExhaustedException: std::exception { };
 
  struct promise_type {
    int value;
    bool is_ready = false;
      int result;
    std::experimental::suspend_never initial_suspend() { return {}; };
 
    std::experimental::suspend_always final_suspend() noexcept { return {}; }
 
    std::experimental::suspend_always await_transform(int value) {
      this->value = value;
      is_ready = true;
        std::cout<<"value="<<value<<std::endl;
      return {};
    }
 
    void unhandled_exception() {
 
    }
 
    Generator get_return_object() {
      return Generator{ std::experimental::coroutine_handle<promise_type>::from_promise(*this) };
    }
 
    //void return_void() { }
      void return_value(int res) {
          result = res;
         
      };
  };
 
  std::experimental::coroutine_handle<promise_type> handle;
 
  bool has_next() {
    if (handle.done()) {
      return false;
    }
      std::cout<<"has_next start"<<std::endl;
    if (!handle.promise().is_ready) {
      handle.resume();
    }
      std::cout<<"has_next end"<<std::endl;
    if (handle.done()) {
      return false;
    } else {
      return true;
    }
  }
 
  int next() {
    if (has_next()) {
      handle.promise().is_ready = false;
      return handle.promise().value;
    }
    throw ExhaustedException();
  }
  int getResult()
  {
      return handle.promise().result;
  }
  explicit Generator(std::experimental::coroutine_handle<promise_type> handle) noexcept
      : handle(handle) {}
 
  Generator(Generator &&generator) noexcept
      : handle(std::exchange(generator.handle, {})) {}
 
  Generator(Generator &) = delete;
  Generator &operator=(Generator &) = delete;
 
  ~Generator() {
    if (handle) handle.destroy();
  }
};
 
Generator sequence() {
  int i = 2;
    int b = 0x55;
    int c = 0x66;
    int d = 0x77;
  while (i < 5) {
    co_await i++;
  }
    co_return 5;
}
 Generator returns_generator() {
  auto g = sequence();
  if (g.has_next()) {
    std::cout << g.next() << std::endl;
  }
  return g;
}
int main() {
  auto generator = returns_generator();
  for (int i = 0; i < 5; ++i) {
    if (generator.has_next()) {
      std::cout << generator.next() << " result = "<< generator.getResult()<<std::endl;
    } else {
      break;
    }
  }
  return 0;
}

代码相对来说比较简单,一个序列生成器,协程比较经典的一个应用场景,大概我们来看下什么意思 sequence() 返回一个协程,这个协程刚开始initial_suspend() 返回的是never,不挂起协程,协程就会执行到sequence()方法里边, 然后执行到了 co_await i++; 代码走到了await_transform,返回了suspend_always 挂起,这时候代码会走到主程序中进行值的打印,接下来我们就分析下汇编代码

首先我们先看下i , a, b,c 存储在什么地方

汇编

C++
    0x100001670 <+484>:  mov    w8, #0x2
    0x100001674 <+488>:  str    w8, [x9, #0x1c]
    0x100001678 <+492>:  mov    w8, #0x55
->  0x10000167c <+496>:  str    w8, [x9, #0x20]
    0x100001680 <+500>:  mov    w8, #0x66
    0x100001684 <+504>:  str    w8, [x9, #0x24]
    0x100001688 <+508>:  mov    w8, #0x77
    0x10000168c <+512>:  str    w8, [x9, #0x28]
 

读取寄存器的值

Lldb:register read

C++
 
        x9 = 0x0000600000c081b0
     
        sp = 0x000000016fdff200
    

根据上边汇编x9的存放的地址是0x0000600000c081b0,它对应的内容如下,

C++
F4 2E 00 00 01 00 00 00 F0 32 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
02 00 00 00 55 00 00 00 66 00 00 00 77 00 00

可以看的的出来栈地址是0x000000016fdff200,而存放变量的地址是0x0000600000c081b0,两个差别很大,可以说明存放变量的在堆空间,在使用从堆空间拷贝到寄存器上,用与还原协程的栈空间

紧接着我们看下协程的挂起与恢复

挂起:

C++
  
   0x1000016bc <+560>:  bl     0x100001ac4               ; Generator::promise_type::await_transform at main.cpp:136
    0x1000016c0 <+564>:  b      0x1000016c4               ; <+568> at main.cpp
    0x1000016c4 <+568>:  ldr    x0, [sp, #0x60]
    0x1000016c8 <+572>:  bl     0x100001b20               ; std::experimental::coroutines_v1::suspend_always::await_ready at coroutine:311
    0x1000016cc <+576>:  tbnz   w0, #0x0, 0x10000175c     ; <+720> at main.cpp
->  0x1000016d0 <+580>:  b      0x1000016d4               ; <+584> at main.cpp
0x1000016c0 <+564>:  b      0x1000016c4               ; <+568> at main.cpp
    0x1000016c4 <+568>:  ldr    x0, [sp, #0x60]
    0x1000016c8 <+572>:  bl     0x100001b20               ; std::experimental::coroutines_v1::suspend_always::await_ready at coroutine:311
    0x1000016cc <+576>:  tbnz   w0, #0x0, 0x10000175c     ; <+720> at main.cpp
    0x1000016d0 <+580>:  b      0x1000016d4               ; <+584> at main.cpp
    0x1000016d4 <+584>:  ldr    x9, [sp, #0x78]
    0x1000016d8 <+588>:  mov    w8, #0x1
    0x1000016dc <+592>:  and    w8, w8, #0x3
    0x1000016e0 <+596>:  strb   w8, [x9, #0x2c]
    0x1000016e4 <+600>:  b      0x1000016e8               ; <+604> at main.cpp
    0x1000016e8 <+604>:  ldr    x0, [sp, #0x78]
    0x1000016ec <+608>:  bl     0x100001a84               ; std::experimental::coroutines_v1::coroutine_handle<Generator::promise_type>::from_address at coroutine:220
    0x1000016f0 <+612>:  mov    x8, x0
    0x1000016f4 <+616>:  ldr    x0, [sp, #0x60]
    0x1000016f8 <+620>:  stur   x8, [x29, #-0x70]
    0x1000016fc <+624>:  ldur   x8, [x29, #-0x70]
    0x100001700 <+628>:  stur   x8, [x29, #-0x68]
    0x100001704 <+632>:  ldur   x1, [x29, #-0x68]
    0x100001708 <+636>:  bl     0x100001b38               ; std::experimental::coroutines_v1::suspend_always::await_suspend at coroutine:313
    0x10000170c <+640>:  b      0x100001710               ; <+644> at main.cpp:207:5
    0x100001710 <+644>:  b      0x100001714               ; <+648> at main.cpp:207:5
    0x100001714 <+648>:  b      0x100001718               ; <+652> at main.cpp
    0x100001718 <+652>:  mov    w8, #0xff
    0x10000171c <+656>:  cbz    w8, 0x10000175c           ; <+720> at main.cpp
    0x100001720 <+660>:  b      0x100001724               ; <+664> at main.cpp
    0x100001724 <+664>:  mov    w8, #0xff
    0x100001728 <+668>:  subs   w8, w8, #0x1
    0x10000172c <+672>:  b.ne   0x100001944               ; <+1208> at main.cpp:201:11
    0x100001730 <+676>:  b      0x100001734               ; <+680> at main.cpp
    0x100001734 <+680>:  mov    w8, #0x2
    0x100001738 <+684>:  str    w8, [sp, #0x3c]
    0x10000173c <+688>:  b      0x100001740               ; <+692> at main.cpp
    0x100001740 <+692>:  ldr    w8, [sp, #0x3c]
    0x100001744 <+696>:  str    w8, [sp, #0x38]
    0x100001748 <+700>:  b      0x100001784               ; <+760> at main.cpp
    0x10000174c <+704>:  mov    x8, x1
    0x100001750 <+708>:  stur   x0, [x29, #-0x30]
    0x100001754 <+712>:  stur   w8, [x29, #-0x34]
    0x100001758 <+716>:  b      0x1000017f0               ; <+868> at main.cpp:210:1
    0x10000175c <+720>:  ldr    x0, [sp, #0x60]
    0x100001760 <+724>:  mov    w8, #0x0
    0x100001764 <+728>:  str    w8, [sp, #0x30]
    0x100001768 <+732>:  bl     0x100001b4c               ; std::experimental::coroutines_v1::suspend_always::await_resume at coroutine:315
    0x10000176c <+736>:  ldr    w8, [sp, #0x30]
    0x100001770 <+740>:  str    w8, [sp, #0x34]
    0x100001774 <+744>:  b      0x100001778               ; <+748> at main.cpp
    0x100001778 <+748>:  ldr    w8, [sp, #0x34]
    0x10000177c <+752>:  str    w8, [sp, #0x38]
    0x100001780 <+756>:  b      0x100001784               ; <+760> at main.cpp
    0x100001784 <+760>:  ldr    w8, [sp, #0x38]
    0x100001788 <+764>:  mov    x9, x8
    0x10000178c <+768>:  str    w9, [sp, #0x2c]
    0x100001790 <+772>:  cbz    w8, 0x1000017a4           ; <+792> at main.cpp:206:3
    0x100001794 <+776>:  b      0x100001798               ; <+780> at main.cpp
    0x100001798 <+780>:  ldr    w8, [sp, #0x2c]
    0x10000179c <+784>:  str    w8, [sp, #0x28]
    0x1000017a0 <+788>:  b      0x1000017d0               ; <+836> at main.cpp
    0x1000017a4 <+792>:  b      0x100001694               ; <+520> at main.cpp
    0x1000017a8 <+796>:  ldr    x0, [sp, #0x70]
    0x1000017ac <+800>:  mov    w1, #0x5
    0x1000017b0 <+804>:  bl     0x100001b5c               ; Generator::promise_type::return_value at main.cpp:152
    0x1000017b4 <+808>:  b      0x1000017b8               ; <+812> at main.cpp
    0x1000017b8 <+812>:  mov    w8, #0x3
    0x1000017bc <+816>:  str    w8, [sp, #0x24]
    0x1000017c0 <+820>:  b      0x1000017c4               ; <+824> at main.cpp
    0x1000017c4 <+824>:  ldr    w8, [sp, #0x24]
    0x1000017c8 <+828>:  str    w8, [sp, #0x28]
    0x1000017cc <+832>:  b      0x1000017d0               ; <+836> at main.cpp
    0x1000017d0 <+836>:  ldr    w8, [sp, #0x28]
    0x1000017d4 <+840>:  subs   w9, w8, #0x3
    0x1000017d8 <+844>:  str    w8, [sp, #0x20]
    0x1000017dc <+848>:  b.eq   0x100001818               ; <+908> at main.cpp
    0x1000017e0 <+852>:  b      0x1000017e4               ; <+856> at main.cpp
    0x1000017e4 <+856>:  ldr    w8, [sp, #0x20]
    0x1000017e8 <+860>:  str    w8, [sp, #0x40]
    0x1000017ec <+864>:  b      0x100001910               ; <+1156> at main.cpp
    0x1000017f0 <+868>:  b      0x1000017f4               ; <+872> at main.cpp:210:1
    0x1000017f4 <+872>:  ldur   x0, [x29, #-0x30]
    0x1000017f8 <+876>:  bl     0x100003c54               ; symbol stub for: __cxa_begin_catch
    0x1000017fc <+880>:  ldr    x0, [sp, #0x70]
    0x100001800 <+884>:  bl     0x100001b7c               ; Generator::promise_type::unhandled_exception at main.cpp:143
    0x100001804 <+888>:  b      0x100001808               ; <+892> at main.cpp:201:11
    0x100001808 <+892>:  bl     0x100003c60               ; symbol stub for: __cxa_end_catch
    0x10000180c <+896>:  b      0x100001810               ; <+900> at main.cpp:201:11
    0x100001810 <+900>:  b      0x100001814               ; <+904> at main.cpp:201:11
    0x100001814 <+904>:  b      0x100001818               ; <+908> at main.cpp
    0x100001818 <+908>:  ldr    x0, [sp, #0x70]
    0x10000181c <+912>:  bl     0x100001b98               ; Generator::promise_type::final_suspend at main.cpp:134
    0x100001820 <+916>:  ldr    x0, [sp, #0x68]
    0x100001824 <+920>:  bl     0x100001b20               ; std::experimental::coroutines_v1::suspend_always::await_ready at coroutine:311
    0x100001828 <+924>:  tbnz   w0, #0x0, 0x1000018b0     ; <+1060> at main.cpp
    0x10000182c <+928>:  b      0x100001830               ; <+932> at main.cpp
    0x100001830 <+932>:  ldr    x8, [sp, #0x78]
    0x100001834 <+936>:  str    xzr, [x8]
    0x100001838 <+940>:  b      0x10000183c               ; <+944> at main.cpp
    0x10000183c <+944>:  ldr    x0, [sp, #0x78]
    0x100001840 <+948>:  bl     0x100001a84               ; std::experimental::coroutines_v1::coroutine_handle<Generator::promise_type>::from_address at coroutine:220
    0x100001844 <+952>:  mov    x8, x0
    0x100001848 <+956>:  ldr    x0, [sp, #0x68]
    0x10000184c <+960>:  stur   x8, [x29, #-0x88]
    0x100001850 <+964>:  ldur   x8, [x29, #-0x88]
    0x100001854 <+968>:  stur   x8, [x29, #-0x80]
    0x100001858 <+972>:  ldur   x1, [x29, #-0x80]
    0x10000185c <+976>:  bl     0x100001b38               ; std::experimental::coroutines_v1::suspend_always::await_suspend at coroutine:313
    0x100001860 <+980>:  b      0x100001864               ; <+984> at main.cpp:201:11
    0x100001864 <+984>:  b      0x100001868               ; <+988> at main.cpp:201:11
    0x100001868 <+988>:  b      0x10000186c               ; <+992> at main.cpp
    0x10000186c <+992>:  mov    w8, #0xff
    0x100001870 <+996>:  cbz    w8, 0x1000018b0           ; <+1060> at main.cpp
    0x100001874 <+1000>: b      0x100001878               ; <+1004> at main.cpp
    0x100001878 <+1004>: mov    w8, #0xff
    0x10000187c <+1008>: subs   w8, w8, #0x1
    0x100001880 <+1012>: b.ne   0x100001944               ; <+1208> at main.cpp:201:11
    0x100001884 <+1016>: b      0x100001888               ; <+1020> at main.cpp
    0x100001888 <+1020>: mov    w8, #0x2
    0x10000188c <+1024>: str    w8, [sp, #0x1c]
    0x100001890 <+1028>: b      0x100001894               ; <+1032> at main.cpp
    0x100001894 <+1032>: ldr    w8, [sp, #0x1c]
    0x100001898 <+1036>: str    w8, [sp, #0x18]
    0x10000189c <+1040>: b      0x1000018d8               ; <+1100> at main.cpp
    0x1000018a0 <+1044>: mov    x8, x1
    0x1000018a4 <+1048>: stur   x0, [x29, #-0x30]
    0x1000018a8 <+1052>: stur   w8, [x29, #-0x34]
    0x1000018ac <+1056>: b      0x100001968               ; <+1244> at main.cpp:201:11
    0x1000018b0 <+1060>: ldr    x0, [sp, #0x68]
    0x1000018b4 <+1064>: mov    w8, #0x0
    0x1000018b8 <+1068>: str    w8, [sp, #0x10]
    0x1000018bc <+1072>: bl     0x100001b4c               ; std::experimental::coroutines_v1::suspend_always::await_resume at coroutine:315
    0x1000018c0 <+1076>: ldr    w8, [sp, #0x10]
    0x1000018c4 <+1080>: str    w8, [sp, #0x14]
    0x1000018c8 <+1084>: b      0x1000018cc               ; <+1088> at main.cpp
    0x1000018cc <+1088>: ldr    w8, [sp, #0x14]
    0x1000018d0 <+1092>: str    w8, [sp, #0x18]
    0x1000018d4 <+1096>: b      0x1000018d8               ; <+1100> at main.cpp
    0x1000018d8 <+1100>: ldr    w8, [sp, #0x18]
    0x1000018dc <+1104>: mov    x9, x8
    0x1000018e0 <+1108>: str    w9, [sp, #0xc]
    0x1000018e4 <+1112>: cbz    w8, 0x1000018f8           ; <+1132> at main.cpp
    0x1000018e8 <+1116>: b      0x1000018ec               ; <+1120> at main.cpp
    0x1000018ec <+1120>: ldr    w8, [sp, #0xc]
    0x1000018f0 <+1124>: str    w8, [sp, #0x40]
    0x1000018f4 <+1128>: b      0x100001910               ; <+1156> at main.cpp
    0x1000018f8 <+1132>: mov    w8, #0x0
    0x1000018fc <+1136>: str    w8, [sp, #0x8]
    0x100001900 <+1140>: b      0x100001904               ; <+1144> at main.cpp
    0x100001904 <+1144>: ldr    w8, [sp, #0x8]
    0x100001908 <+1148>: str    w8, [sp, #0x40]
    0x10000190c <+1152>: b      0x100001910               ; <+1156> at main.cpp
    0x100001910 <+1156>: ldr    x8, [sp, #0x78]
    0x100001914 <+1160>: ldr    w9, [sp, #0x40]
    0x100001918 <+1164>: str    w9, [sp, #0x4]
    0x10000191c <+1168>: cbz    x8, 0x100001930           ; <+1188> at main.cpp
    0x100001920 <+1172>: b      0x100001924               ; <+1176> at main.cpp
    0x100001924 <+1176>: ldr    x0, [sp, #0x78]
    0x100001928 <+1180>: bl     0x100003c30               ; symbol stub for: operator delete(void*)
    0x10000192c <+1184>: b      0x100001930               ; <+1188> at main.cpp
    0x100001930 <+1188>: ldr    w8, [sp, #0x4]
    0x100001934 <+1192>: cbz    w8, 0x100001940           ; <+1204> at main.cpp:201:11
    0x100001938 <+1196>: b      0x10000193c               ; <+1200> at main.cpp
    0x10000193c <+1200>: b      0x100001944               ; <+1208> at main.cpp:201:11
    0x100001940 <+1204>: b      0x100001944               ; <+1208> at main.cpp:201:11
    0x100001944 <+1208>: b      0x100001948               ; <+1212> at main.cpp:201:11
    0x100001948 <+1212>: b      0x10000194c               ; <+1216> at main.cpp
    0x10000194c <+1216>: mov    w8, #0x1
    0x100001950 <+1220>: and    w8, w8, #0x1
    0x100001954 <+1224>: and    w8, w8, #0x1
    0x100001958 <+1228>: sturb  w8, [x29, #-0x1a]
    0x10000195c <+1232>: ldurb  w8, [x29, #-0x19]
    0x100001960 <+1236>: tbnz   w8, #0x0, 0x100001998     ; <+1292> at main.cpp:210:1
    0x100001964 <+1240>: b      0x1000019b4               ; <+1320> at main.cpp:210:1
    0x100001968 <+1244>: b      0x10000196c               ; <+1248> at main.cpp:201:11
    0x10000196c <+1248>: b      0x100001970               ; <+1252> at main.cpp:201:11
    0x100001970 <+1252>: b      0x100001974               ; <+1256> at main.cpp
    0x100001974 <+1256>: ldr    x8, [sp, #0x78]
    0x100001978 <+1260>: cbz    x8, 0x10000198c           ; <+1280> at main.cpp:210:1
    0x10000197c <+1264>: b      0x100001980               ; <+1268> at main.cpp
    0x100001980 <+1268>: ldr    x0, [sp, #0x78]
    0x100001984 <+1272>: bl     0x100003c30               ; symbol stub for: operator delete(void*)
    0x100001988 <+1276>: b      0x10000198c               ; <+1280> at main.cpp:210:1
    0x10000198c <+1280>: ldurb  w8, [x29, #-0x19]
    0x100001990 <+1284>: tbnz   w8, #0x0, 0x1000019c4     ; <+1336> at main.cpp
    0x100001994 <+1288>: b      0x1000019d0               ; <+1348> at main.cpp:210:1
    0x100001998 <+1292>: ldurb  w8, [x29, #-0x1a]
    0x10000199c <+1296>: tbnz   w8, #0x0, 0x1000019b0     ; <+1316> at main.cpp:210:1
    0x1000019a0 <+1300>: b      0x1000019a4               ; <+1304> at main.cpp
    0x1000019a4 <+1304>: ldr    x0, [sp, #0x90]
    0x1000019a8 <+1308>: bl     0x100001ba8               ; Generator::~Generator at main.cpp:196
    0x1000019ac <+1312>: b      0x1000019b0               ; <+1316> at main.cpp:210:1
    0x1000019b0 <+1316>: b      0x1000019b4               ; <+1320> at main.cpp:210:1
    0x1000019b4 <+1320>: ldp    x29, x30, [sp, #0x130]
    0x1000019b8 <+1324>: ldp    x28, x27, [sp, #0x120]
    0x1000019bc <+1328>: add    sp, sp, #0x140
    0x1000019c0 <+1332>: ret   
    0x1000019c4 <+1336>: ldr    x0, [sp, #0x90]
    0x1000019c8 <+1340>: bl     0x100001ba8               ; Generator::~Generator at main.cpp:196
    0x1000019cc <+1344>: b      0x1000019d0               ; <+1348> at main.cpp:210:1
    0x1000019d0 <+1348>: b      0x1000019d4               ; <+1352> at main.cpp:201:11
    0x1000019d4 <+1352>: ldur   x0, [x29, #-0x30]
    0x1000019d8 <+1356>: bl     0x100003b58               ; symbol stub for: _Unwind_Resume
 

这个摘录上边代码一段汇编,看下协程挂起的过程中到底发生了什么事情 ,bl     0x100001b20  跳转到std::experimental::coroutines_v1::suspend_always::await_ready,判断ready 状态, 接着,tbnz   w0, #0x0, 0x10000175c  来看下tbnz 意思是判断#0x0位,跟0进行比较,如果非0 则跳转到0x10000175c,否则不调转,因为我们await_ready 是false所以这里tbnz判断失败,不跳转,则直接到了

 0x1000016d0    这里就是final_suspend 接着下来 就是要挂起了

绿色显示了汇编跳转的过程,可以看到最终调用ret,返回了调用的地方

恢复:

上边已经看了挂起的过程,接下来看下恢复协程怎么来的,首先看下resume相关汇编

C++
test`std::experimental::coroutines_v1::coroutine_handle<void>::resume:
    0x1000022ac <+0>:  sub    sp, sp, #0x20
    0x1000022b0 <+4>:  stp    x29, x30, [sp, #0x10]
    0x1000022b4 <+8>:  add    x29, sp, #0x10
    0x1000022b8 <+12>: str    x0, [sp, #0x8]
    0x1000022bc <+16>: ldr    x8, [sp, #0x8]
    0x1000022c0 <+20>: ldr    x0, [x8]
    0x1000022c4 <+24>: ldr    x8, [x0]
    0x1000022c8 <+28>: blr    x8
->  0x1000022cc <+32>: ldp    x29, x30, [sp, #0x10]
    0x1000022d0 <+36>: add    sp, sp, #0x20
    0x1000022d4 <+40>: ret   
 

然后我们读取下相关寄存器的值

C++
x0 = 0x0000600000c08000
x8 = 0x0000000100002ef4  test`sequence() at main.cpp:201

从上边绿色两行汇编可以看出来,x0存放的协程资源的堆地址,而x8则是存放协程代码的函数地址,接着blr x8 会跳转到协程,ret保存着 0x1000022cc这个地址,等协程挂起的时候会回到这个0x1000022cc地址上去,

总结:

从上边分析可以看出,c++协程实现还是比较复杂的,采用无栈协程,这个腾讯开源的libco的协程实现不太一样,协程资源存在栈资源, 切换与恢复协程通过跳转的功能实现的

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从汇编角度看c++20 协程 的相关文章

  • 一本通:1191流感传染

    一本通 xff1a 流感传染 题目描述 有一批易感人群住在网格状的宿舍区内 xff0c 宿舍区为n n的矩阵 xff0c 每个格点为一个房间 xff0c 房间里可能住人 xff0c 也可能空着 在第一天 xff0c 有些房间里的人得了流感
  • CentOS 7 使用iptables 开放端口

    转自 https www cnblogs com kyuang p 6307535 html CentOS 7 0默认使用的是firewall作为防火墙 xff0c 这里改为iptables防火墙 1 关闭firewall xff1a sy
  • 模拟幅度调制系统抗干扰性能仿真分析

    目录 1 引言2 系统模型和仿真1 DSB SC调制和解调1 调制过程2 解调过程3 信噪比分析4 matlab代码 2 AM调制和解调1 调制过程2 解调过程 xff1a 3 信噪比分析4 matlab代码 3 SSB调制和解调1 调制过
  • 解决重启后 /mnt/hgfs下共享文件夹挂载失效问题

    问题描述 xff1a 如题 xff0c 在 mn hgfs 下使用ls命令不显示共享文件夹 xff0c 在root权限下运行命令 xff1a vmhgfs fuse host mnt hgfs 后重新进入目录显示共享文件夹 xff0c 重启
  • Matlab安装硬件支持包补充@水月流荧

    您好 xff0c 请问我按照文章内容下载了支持包 xff0c 并且将archive拷贝到E matlab bin win64 xff08 本机位置 xff09 中 xff0c 后在win64中找到SupportSoftwareInstall
  • spring-boot-maven-plugin未指定版本导致的编译错误

    spring boot maven plugin未指定版本导致的编译错误 报错 springboot应用在使用maven编译时会报如下错误 xff1a Java Runtime class file version 61 0 this ve
  • ssh连接Centos7时vim无法使用粘贴板原因及解决方法

    原因 xff1a 1 命令行的vim没有 43 和 两个寄存器 xff08 原因之一 xff09 xff0c 退出vim后粘贴板内容不会保留到vim所在系统的粘贴板 2 在有 43 和 寄存器的前提下 xff0c 可以从vim复制到vim所
  • >Error:com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs for details

    gt Error com android tools aapt2 Aapt2Exception AAPT2 error check logs for details 这个错误会在3 0里面出现 解决办法是在gradle properties
  • 一键部署k8s脚本

    一键部署k8s脚本 目录 一键部署k8s脚本前情提示环境准备硬件系统要求角色说明 xff1a 其余准备工作 centos准备centos网络准备centos软件准备 部署k8s前配置下载部署脚本安装 Ansible 运行环境修改安装的配置文
  • Docker in docker 实现

    Docker in docker 文章目录 Docker in docker原理实现 xff08 centos7 xff09 常见问题参考 在docker容器内运行docker一般是不被倡导的 但有些场景和业务上 xff0c 需要在容器内使
  • 一篇文章入门spring

    Spring入门1 在之前我们对象的创建都是我们自己new出来的 xff0c 比如Student stu 61 new Student xff08 xff09 但是我们现在有了spring 我们将对象的创建的工作交给spring来处理 那么
  • JAVA数据结构之数组详细教程

    本文整理了java数据结构的数组操作 希望对刚入门数据结构的同志们有帮助 java数组非常简单 只要有JAVA语言基础就可以看这篇博文 大家不要害怕 非常简单 整理博客真的很花费时间 xff0c 如果对大家有帮助 xff0c 麻烦点赞评论
  • JAVA程序(MongoTemplate)操作mongodb数据库常用方法(超级详细)

    这里使用的是Spring 43 MongoTemplate来操作mongodb数据库 如果有不了解spring的同志先去了解一下spring为好 xff0c 这里给出实现的一些方法 主要有 xff1a 查询 增加 修改 删除 多字段增加 模
  • template操作mongodb数据库(更新方法大全)

    本文是使用JAVA程序操作MongoDB数据库 里面提供了各种更新数据的方法 xff0c 查询的各种方法会在后面进行更新 本文只是提供了数据库更新操作的一些方法 数据库数据和字段如下 xff1a 对于更新数据 xff0c 我将更新数据的方法
  • mongodb template 计算mongodb日期的解决方案

    mongodb由于特殊的日期格式 存在 8时区的问题 所以在使用java程序解决日期计算问题就会有点麻烦 其实也很简单 就是先将日期改变时区 然后转成带有格式String类型的日期 然后在java里面的将String转化成date类型即可
  • 一篇文章彻底理解二分搜索树(附代码实现)

    本文使用JAVA语言进行描述 其实本质上什么语言描述数据结构都是可以的 二叉树基础 二叉树的根节点 二叉树递归结构 xff1a 上面是一个满二叉树 但是实际中也有二叉树不是满的 二分搜索树 二分搜索树也不一定是满的 所以使用二分搜索树需要具
  • opengl 源码分析常见问题

    Opengl 一些问题解答 为什么opengl 不能跨线程 大家有没有想过这个问题 xff0c 网上给出的答案其实看得不太明白 xff0c 接下来我们看源码让你知道 C EGLContext Display createContext EG
  • mongodbtamplate使用程序创建mongdb索引的解决方案

    话不多说 xff0c 直接上代码 xff1a span class token keyword public span span class token keyword boolean span span class token funct
  • el表达式取不到值

    在jsp页面中有可能出现el表达式取不到值的问题 xff0c 但是反复检查代码 xff0c 跑断点都没有问题 xff0c 这是因为jsp忽略了el表达式 所以只要加上下面一行代码就可以了 span class token operator

随机推荐

  • Kaggle心脏病数据集为例学习机器学习的可解释性分析

    需要安装的工具包 pip install numpy pandas matplotlib seaborn wheel pandas profiling jupyter notebook span class token operator s
  • readlink /var/lib/docker/overlay2: invalid argument的解决方案

    发生这种情况是因为在运行Docker映像之间重新启动了docker xff0c 该映像已损坏 我重新启动系统 xff0c 然后运行以下命令 docker compose build no cache docker compose up 您还
  • python调用IP摄像头

    利用RTSP 43 opencv就可以实现网络摄像头的调用 rtsp是实时流传输协议 xff0c 是基于TCP IP协议体系中的一个应用层协议 xff0c 可以控制声音或者影像的多媒体串流协议 但是不同品牌的摄像头有不同的RTSP地址 下面
  • 22岁-时光如河,浮生为鱼

    时光如河 xff0c 浮生为 x1f41f 还没有学会告别 xff0c 四年就这样悄悄过去了 如往年今日一样 xff0c 依旧写些懒懒散散的文字致敬这一年的时光 x1f495 22岁生日快乐 x1f495 全文约4200字 xff0c 阅读
  • 电子书下载网站汇总

    网站名称地址简介语言推荐指数备注Book4Uhttp www book4you sk 外文下载网站斯洛伐克语 BookYardshttps www bookyards com en welcome主要面向教师的门户网站 xff0c 其中的书
  • Docker版 E5续订的E5调用API续订服务:Microsoft 365 E5 Renew X

    本文是基于作者SundayRX提出的E5 调用API续订服务 xff1a Microsoft 365 E5 Renew X的基础上提出的Docker版本的E5调用API续订服务 基础的账号注册等过程见SundayRX的博客 xff1a 账号
  • Docker版 Linux百度网盘备份工具

    一些必须要知道的事 xff1a 这个镜像的主要目的是为了将服务器或者群晖等linux场景中的资料备份到百度网盘中容器的基础镜像为ubuntu镜像 容器的备份周期为每天的凌晨两点 具体步骤如下 xff1a 下载镜像 docker pull h
  • 操作系统(五)中断和异常

    1 5 中断和异常 在上节内核态与用户态的转换过程中曾经提到过 xff0c 操作系统会响应中断信号强制夺回CPU使用权 xff0c 使用户态转换为内核态 中断 是操作系统夺回CPU使用权的唯一方式 xff0c 如果没有 中断 机制 xff0
  • Mediacodec 如何硬件解码到纹理的

    Mediacodec 如何硬件解码到纹理的 背景 xff1a 网上很多关于mediacodec xff0c surface xff0c surfacetexture的源码分析 xff0c 以及内部原理 xff0c 但是都局限于各自的内容 x
  • pyinstaller 递归深度设置(A RecursionError occurred)

    简介 xff1a pyinstaller常用于打包python文件 xff0c 当导入的包过多时常会出现一个递归深度超过限制的错误 这个可以通过设置最大递归深度来解决 1 pyinstaller报错信息 61 61 61 61 61 61
  • labelme标注格式转coco格式

    摘要 xff1a labelme是广泛使用的深度学习标注工具 xff0c 支持目标检测和实例分割等任务的标注 xff0c 但是一些框架如detectron2 xff0c solo等需要的是coco格式的 xff0c 这里提供一个示例把lab
  • opencv C++ 旋转任意角度图片

    摘要 xff1a opencv里面似乎没有直接的旋转图片的接口 xff0c 这里实现一个旋转任意角度的方法 xff0c 在旋转的时候调用opencv里面的仿射变换函数实现 有两种旋转模式 xff1a 一种按图片中心旋转 xff0c 尺寸与原
  • C++ opencv曲线拟合

    简介 xff1a 此问题是在做旋转模板匹配的时候 xff0c 选择最好的匹配结果时产生的 查找资料发现多项式拟合问题可以变成一个超定方程的求解问题 xff0c 而opencv中本身有一个cv solve 函数可以求解线性方程组 xff0c
  • C# 中的Bitmap 和(c++)opencv之间的传递

    C 中的Bitmap 和 xff08 c 43 43 xff09 opencv之间的传递 文章目录 C 中的Bitmap 和 xff08 c 43 43 xff09 opencv之间的传递1 C 传递bitmap给C 43 43 2 Pix
  • opencv kmeans (C++)

    kmeans 函数原型 span class token keyword double span cv span class token operator span span class token function kmeans span
  • C++ explict

    文章目录 C 43 43 中的explicit是一个关键字 xff0c 用于修饰构造函数 xff0c 它可以防止编译器进行隐式类型转换 xff0c 只允许显式地调用构造函数 它不能用于隐式转换和复制初始化 例如 xff0c 在下面的代码中
  • C++ friend

    在C 43 43 中 xff0c friend是一个关键字 xff0c 用于声明一个非成员函数或类可以访问另一个类的私有成员 例如 xff0c 我们有一个名为ClassA的类 xff1a span class token keyword c
  • C++ enum 和enum class

    文章目录 C 43 43 enum 和 enum class共同点区别 C 43 43 enum 和 enum class 在C 43 43 中 xff0c enum 是一种定义枚举类型的方法 一个枚举是一个整数值的命名集合 可以通过以下方
  • VS2019设置cl.exe环境变量

    版本 xff1a VS2019 设置 cl 环境变量 xff08 加入系统环境变量 xff08 PATH xff09 xff09 步骤 xff1a 1 找到cl exe的所在路径 xff0c 一般都在 xff1a C Program Fil
  • 从汇编角度看c++20 协程

    从汇编角度看c 43 43 20 协程 背景 xff1a 在学习c 43 43 20 协程的时候 xff0c 总对协程里边的局部成员存储 xff0c 以及协程栈恢复有很多疑问 xff0c 本次从过年arm64角度来分析下 xff0c 具体情