群组沟通的礼仪和技巧

网络几乎已经成为人们生活不可获取的部分。各行各业使用IM软件几乎都有一致的共同点。每个行业的背后沟通交流出发点是相同的:暴露问题和解决问题。

先看几个场景:

1, 客户将宠物放到宠物学校学习,宠物学校的教练会给宠物建一个微信群,实时沟通宠物的学习情况。

2,客户将房屋装修外包给装修公司,装修公司建群将装修进度和问题在群里反馈和沟通。

3,企业内部员工就某个问题或群体组建即时消息群。讨论工作问题。

在这几个群的背后,表面上最核心的动作就是:消息的组播。沟通变成了组播,组播结果驱动了事情的进展和方向。

实际是,消息的组播让工作有一个更好的交付界面。

在组播中,有以下礼仪需要注意:

  • 要及时赞美和鼓励。
  • 不要幻想对方都站在自己的立场想问题,要主谓宾描述完整。以免引起不恰当的误会。
  • 要多用 “请” , “多谢”,“辛苦你” 。因为对方看不到你的眼睛,这些词语让人处于合适的社交氛围中。

实践上述礼仪时,针对网络谩骂行为,造谣行为不在此礼仪的应用范围。应该特殊处理。不能姑息养奸,要及时给出“合适”的制止。

C++对象模型小探索

class base {
public:
    base():a(10),b(11){}
    virtual void fa(){ std::cout << "base a function...\n";}
    virtual void fb(){ std::cout << "base b function...\n";}
    
public:
    int a;
    int b;
    std::string c{"angel"};
};

class derived: public base {
public:
    void fa() override { std::cout << "derived a function ...\n";}
    void fb() override { std::cout << "derived b function...\n"; }
    void fc() { std::cout << "derived c function...\n";}
public:
    int c;
};



int main(int argc, char** argv)
{
    std::cout << "class base size : " << sizeof(base) << std::endl;
    std::cout << "class derived size: " << sizeof(derived) << std::endl;
    
    derived* pd = new derived();
    base* pb = new derived();
 
    using func= void(*)();
    
    long int vptr1 = reinterpret_cast<long int>(pb);
    long int vptr2 = reinterpret_cast<long int>(pd);
    
    long int* p = (long int*)vptr1;
    long int* p1 = (long int*)vptr2;
    std::cout << "p  " <<  p << std::endl;
    std::cout << "p1 " <<  p1 << std::endl;
    
    long int* vtaddr = (long int*)(*p);
    long int* vtaddr2 = (long int*)(*p1);
    std::cout << "vtaddr  " << vtaddr << std::endl;
    std::cout << "vtaddr2 " << vtaddr2 << std::endl;
    
    
    func* ptr = (func*)(vtaddr);
    (*ptr)();
    func* ptrb = (func*)(vtaddr+1);
    (*ptrb)();
    
    
    func* bptr = (func*)(vtaddr2);
    (*bptr)();
    func* bptrb = (func*)(vtaddr2+1);
    (*bptrb)();
    
    std::cout << *(int*)(long int*)(vptr1+8) << std::endl;
    std::cout << *(int*)(long int*)(vptr1+12) << std::endl;
    std::cout << std::string((char*)(long int*)(vptr1+16))<< std::endl;
    
    
    pd->fa();
    pb->fa();
    
    

    return 0;
}


以上代码强制调用vtable指向的函数,从安全性角度讲:可以替换虚表的任何函数。

结果是:

 class base size : 40
 class derived size: 48
 p  0x102a84240
 p1 0x102a84210
 vtaddr  0x10000b090
 vtaddr2 0x10000b090
 derived a function ...value is ...34
 derived b function...
 derived a function ...value is ...-1746446496
 derived b function...
 10
 11
 
 angel
 derived a function ...value is ...10
 derived a function ...value is ...10

强行使用裸指针调用,得到的成员值明显不对。推测是没有代入隐含的this指针。

下次谈谈如何基于本案列做C++的逆向工程。

我对C/C++语言运算规则另类小解

先看看无聊的C运算符优先级和结合性。看到表1的人可以分为三类:

  • C语言编译器的作者
  • 计算机入门者
  • 各种计算机基础能力考试应对者

比如要你计算: -Num++, +Num++, f( a(), b(), c()) ,int a= c,d,e; 之类的运算顺序。

大多数工程师为了避免记忆这繁琐的规则,使用()括号大法来解决大部分运算符优先级/结合性的问题。而且在大多数情况下,商业团队会通过统一的编程规范来避免程序语言的”坑”。

表1:运算符优先级和结合性

优先级 运算符 描述 结合性
1 ++ -- 后缀自增与自减 从左到右
() 函数调用
[] 数组下标
. 结构体与联合体成员访问
-> 结构体与联合体成员通过指针访问
(type){list} 复合字面量(C99)
2 ++ -- 前缀自增与自减[注 1] 从右到左
+ - 一元加与减
! ~ 逻辑非与逐位非
(type) 转型
* 间接(解引用)
& 取址
sizeof 取大小[注 2]
_Alignof 对齐要求(C11)
3 * / % 乘法、除法及余数 从左到右
4 + - 加法及减法
5 << >> 逐位左移及右移
6 < <= 分别为 < 与 ≤ 的关系运算符
> >= 分别为 > 与 ≥ 的关系运算符
7 == != 分别为 = 与 ≠ 关系
8 & 逐位与
9 ^ 逐位异或(排除或)
10 | 逐位或(包含或)
11 && 逻辑与
12 || 逻辑或
13 ?: 三元条件[注 3] 从右到左
14[注 4] = 简单赋值
+= -= 以和及差赋值
*= /= %= 以积、商及余数赋值
<<= >>= 以逐位左移及右移赋值
&= ^= |= 以逐位与、异或及或赋值
15 , 逗号 从左到右

去记忆这种表格明显是反直觉的,我们还是得理解编译器帮我们解释符号的意图。

C的符号可以归结为几类:

  1. 算术运算符。最普通的+ – * / % ~ & | ^ << >>
  2. 逻辑运算符。! && ||
  3. 赋值运算符。= &= <<=
  4. 比较运算符。< == >= <= > !=
  5. 成员访问。 [] () * -> .
  6. 其他 … , (type强转), sizeof, _Alignof

先是结合性,原则很简单,也很人类。

就是按照普通人的阅读顺序去记忆: 比如[], 它肯定是从左到右结合。因为 一个表达式 a[1], 先要知道a,才能知道相对寻址的 a[1]。 再比如算术运算a + b, 肯定是先move a到寄存器,在把a和b想加。因此也是从左向右结合。

那么,哪些是从右往左结合呢?

我们写代码 sizeof( SuperStruct ), 肯定是先想到SuperStruct的大小和地址在哪,然后再去取大小。这就是很直接的计算机思维

最有代表性的是: 前++,前– VS 后++, 后–

用他们来测试下这种规律的正确性:

a++ , 从左到右。因为先得知道a,才能++。

–a , 从右往左结合。 因为先得知道a,才能–。

看,这是不用记忆的。非常直接的计算机符号逻辑

优先级的记忆

优先级最高的: 除了后加/后减的优先级超高。 成员访问优先级高完全符合直觉。成员访问优先级高,可以理解为得先有数据才能做各种运算

优先级次高的: 几乎全部单目运算符优先级是次高的。单目运算符只需要一个操作数,和成员访问有相同的属性。

优先级第三/四高: 接下来是双目运算符, 乘/除/取余(优先级三) 比 加减(优先级四)优先级高,符合直觉。

优先级第五/六高: 算术运算比关系运算优先级高。 加减乘除移位比所有比较符号优先级高。移位这种算术运算 >>, << 比 >,< >=,<= 的符号优先级高。先算出来再比较。

优先级第六/七高: >=, <= 比 ==, !==优先级高。这个没有什么规则。就是C语言编译器的人自己定的规则。 先比较大小,再比较是否相等。

优先级第八/九/⑩高: 位运算自己的比较,依次是 & ^ |

优先级第十一/十二: 逻辑运算 && ||

第十三: 孤零零的一个 三目。

第十四:各种赋值

第十五:孤独的逗号。

所以,总结起来规则是:

  1. 成员运算优先级最高。成员运算符算是单目的一种,所以剩下的单目运算符次高。(单目优先原则,直觉)
  2. 算术运算符比关系比较运算符高。(先算[包括移位]出来再比较,直觉)
  3. 关系比较运算符比位运算符高。(比较完再来做位运算,非直觉)
  4. 位运算比逻辑运算符高。(有运算结果再来做逻辑与或)
  5. 逻辑运算比赋值运算符高。(做完算术,比较,位和逻辑运算再来赋值)
  6. 逗比排在最后。
  7. 结合规则完全从直觉去判断。

Lockless stack

The core logic of lockless programming is the CAS operation brought by processor. CAS can compare and exchange value between two number in a single instruction. From the perspective of CPU synchronization primitive, CAS instruction like “cmpxchg” is expensive. but from the view of application programmer, the lockfree data structure is really attractive for its free user from using more expensive operation system “mutex locks” which will switch the worker thread from background back and forth.

LockFree data-structure always comes with ABA problem, according herb sutter. for example, We need to resolve the deletion issue: we can’t delete a node if a concurrent reader/writer might be pointing to it.

there are 4 options to solve the problem:

  • Option1: Use lazy garbage collection. solve the problem because memory cannot be reused while any pointers to it exist, However, destruction of nodes becomes nondeterministic.
  • Option2: Use reference counting ( garbage collection). solve the problem in cases without cycles.
  • Option3: Never actually delete a node ( only logically delete) , can work when deleting is rare.
  • Option4: Put auxiliary nodes in between actual nodes. Contains a next pointer only, no data. These links don’t move. Enables operations on adjacent nodes to run without interference.

STL Review

159436 share 352353.56 bonus 70470.71 tax 281882.85 actual

STL六大组件:1,容器。2,算法。3,迭代器。4,仿函。 5,适配器。6,分配器

顺序容器 Vector:

1,  分配的内存是以2的倍数动态增长。内存不够时,分配空间是当前的2倍。
    同时将原来的内容复制到新的空间。消耗较大。
    vector的allocator实现二级空间配置器,二级配置直接管理内存池, 伙伴系统,自由链表组成。
   
 
2,  连续存储空间,插入删除较慢。
3, 使用vector<int>.swap(vector_to_be_clear)来消除内存。
使vector_to_be_clear成为临时对象,释放内存。->小技巧。 
4, clear只会把vector的size清为零,不会释放内存。
5, vector是随机迭代器的顺序容器。
6, 随机存取优势。插入劣势。
7, 和dque相比,删除元素不会释放空间,重新申请空间时有额外的元素拷贝操作。
8,  access方法: [] , at,  front, back, data
    modifier方法: 
     assign, 
     push_back(), 
     pop_back(), 
     insert, 
     erase, 
     swap, 
     clear, 
     emplace, 
     emplace_back()

    capacity方法: 
     size, 
     max_size, 
     resize,  
     capacity, 
     empty, 
     reserve, 
     shrink_to_fit 

顺序容器 Deque:

1, 支持随机访问迭代器。
2, deque的内存空间分布是小片的连续,小片间用链表相连。
3, deque的重新分配要比vector快。重新分配后原有元素不需要拷贝。
4, 随机存取优势。插入劣势。 
5, 和vector相比,删除元素释放空间,重新申请空间没有额外元素拷贝操作。
6,  access方法: 
      [],
      at, 
      front, 
      back

    modifier方法: 
      assign, 
      push_back(), 
      push_front(), 
      pop_back(), 
      pop_front(), 
      insert, 
      erase,
      swap,  
      clear,  
      emplace, 
      emplace_front, 
      emplace_back

顺序容器 List:

1, 双向链表,内存空间不连续。
2, List支持双向迭代器。
3, 很好的支持任意地方插入和删除。
4, 频繁插入优势。存取劣势。
5, 方法:
      push_back(element)
      pop_back()  

关联容器 Map:

1,  map不保存数据时,每个节点占用 2个指针+1个枚举的空间。 
2, map是一个有序的key排列,提供双向迭代器。(非严格意义的平衡二叉树)。 
3, 插入相同的key会返回不成功。如果想支持使用multimap以支持1:N的存储能力。 
4, access: [], at     
    modifier: insert, erase, swap, clear, emplace,     emplace_hint     
    observer: key_comp, value_comp     
    operation:  find, count, lower_bound, upper_bound, equal_range     
    allocator: get_allocator

关联容器 unordered_map

 1, 方法buckets:
     bucket_count
     max_bucket_count
     bucket_size
     bucket

     Hash策略:
     load_factor
     max_load_facotr
     rehash
     reserve

     Observer:
     hash_function
     key_eq
     get_allocator

     Modifier:
     emplace
     emplace_hint
     insert
     erase
     clear
     swap

     元素查找:
     find
     count
     equal_range

     元素访问:
     at
     []

关联容器 Set:

1, 底层使用红黑树,同map
2, 默认对元素进行升序排列。
3, 不能直接修改key。需要先删除后插入。
4,  方法上就是红黑树的抽象方法,同map。
    capacity: empty, size, max_size
    modifier: insert, erase, swap, clear, emplace, emplace_hint
    observer: key_comp, value_comp
    operation: find, count, lower_bound, upper_bound, equal_range
    allocator: get_allocator

容器适配器 queue

1, 以标准STL容器deque为基础。
2, FIFO功能。
3,  方法: empty, size, front, back, push, pop, emplace,swap

容器适配器 stack

1, 以标准STL容器deque为基础。
2,FILO功能。
3, 方法: empty, size, top, push, emplace, pop, swap

顺序容器 array

1, 定长,定类型。
2, capacity方法: size, max_size(),empty
    access方法: [], at, front, back, data
    modifier:  fill, swap

mmap on MacOS

let’s see a snippet of code, Guess what happened on MacOS ?

 size_t pageSize = getpagesize();

  std::cout << " page size is " << pageSize << std::endl;

  int handle = shm_open("mempool_tag", O_CREAT | O_RDWR , 0777 );

  if (handle) {
     std::cout << std::setw(20) << "shm_open yield with  " << handle << std::endl;
  }
  else {
     ftruncate(handle, GetPoolSize());  
  }
  
  ftruncate(handle, GetPoolSize());  

  char* region = (char*) mmap( NULL, 
                               GetPoolSize(),
                               PROT_READ | PROT_WRITE | PROT_EXEC,
                               MAP_ANONYMOUS,
                               handle,
                               GetPoolSize()*20);
  if (region == MAP_FAILED) {
     return -1; 
  }

  return 0;

NerdTree hacks

I came with the problem when exit from the vim with “:q” command.

NERDTree still display after :q command

and this seems the buffer window counter problem.

fix by:

function! CheckLeftBuffers()
  if tabpagenr('$') == 1
    let i = 1
    while i <= winnr('$')
      if getbufvar(winbufnr(i), '&buftype') == 'help' ||
          \ getbufvar(winbufnr(i), '&buftype') == 'quickfix' ||
          \ exists('t:NERDTreeBufName') &&
          \   bufname(winbufnr(i)) == t:NERDTreeBufName ||
          \ bufname(winbufnr(i)) == '__Tag_List__'
        let i += 1
      else
        break
      endif
    endwhile
    if i == winnr('$') + 1
      qall
    endif
    unlet i
  endif
endfunction
autocmd BufEnter * call CheckLeftBuffers()

https://yous.be/2014/11/30/automatically-quit-vim-if-actual-files-are-closed

Is Joinable reall joinable?

I found the semantics of joinable thread is really confusing.

if thread start working, the status use “joinable” to indicate the thread is working if joinable is false.

if thread stop working, we wish it exit gracefully, and call join() to tell the system thread has complete it’s destiny.

I just don’t quite understand that: why the author use the word “JOIN”

wordpress换血

使用nginx

使用上了大名鼎鼎的nginx,配置wordpress后,静态链接会失效,nginx和apache的重定向配置不同。配置文件如下:

server {
    listen         80 default_server;
    listen         [::]:80 default_server;
    server_name    arthurcode.com www.arthurcode.com;
    root           /foo/bar/arthurcode;
    index          index.php index.html;

  location ~* \.php$ {
    fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    include         fastcgi_params;
    fastcgi_param   SCRIPT_FILENAME    $document_root$fastcgi_script_name;
    fastcgi_param   SCRIPT_NAME        $fastcgi_script_name;
  }

  location = /favicon.ico { log_not_found off; access_log off; }
  location = /robots.txt { log_not_found off; access_log off; allow all; }
  location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
        expires max;
        log_not_found off;
  }
    
  location / {
        #try_files $uri $uri/ =404;
        try_files $uri $uri/ /index.php$is_args$args;
  }

}

wordpress 手动更新

  1. 备份 WordPress 网站文件及数据库;(用phpadmin导出的sql文件是可编辑的,部分不能导入的SQL语句可删除)。
  2. 下载最新版的 WordPress,切记进入WordPress 官网下载 zip 程序包,并用md5校验。
  3. wp-content文件夹和wp-config.php文件替换最新版本中对应wp-content和wp-config.php。
  4. 最后,在浏览器中访问http://www.domain_name.com/wp-admin/upgrade.php
  5. 中国区会出现很多327 too many request 错误。根据观察可能是太多的重定向导致。

本来以为wp这么流行的系统不会碰到很多问题,实际上并非如此。。加了cache系统速度也没太快,可能是优化不够到位。严重怀疑wordpress各种xhr请求影响了速度。。

有点想去折腾简单的静态页面。(我这性格也不想暂时放弃,这浪费时间的问题下次靠脚本来解决问题。