秋招面经(后端开发) - 2021届

843 阅读12分钟

字节和shopee已拿意向书。

字节跳动提前批 - 后端开发 - 技术中台方向

字节提前批一面

先来个个人介绍

实习经历 介绍实习经历; Svrkit(rpc 框架)的原理; Protocol buffer 相对于 json 的优点;

计网 http 1.0、1.1、2.0 的区别 三次握手、四次挥手; accept() 等待的时候,底层的原理是啥?有哪些结构? 会有三次挥手的情况吗? 服务端和客户端同时调用 close() 会怎么样;

数据库 Innodb 中的索引用了什么数据结构?B+树 什么情况会用到哈希索引?会存到硬盘吗?

Redis (个人项目中用到了 Resis、kafka、Elasticsearch 等) Redis 的 zset 底层是啥?跳表; 讲一讲跳表; zset 还用到了哈希表,用来干啥? Redis 持久化的方式 - rdb 和 aof; rdb 结束或者 aof 重写结束的时候,子进程如何通知父进程;

Kafka 原理; 发送消息的三种方式; reblance 原理(消费者组中的数量变化时);

操作系统 linux 的虚拟内存的布局(堆、栈、内存低地址到高地址存的是什么); 进程栈和线程栈的区别?

算法题 判断单链表有没有环,有的话找出入环节点

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        
    }
}

我写的:

import java.util.Scanner;
public class Main {
    public ListNode detechCycle(ListNode head) {
        if (head == null && head.next == null) {
            return null;
        }
        
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null && slow != fast) {
            slow = slow.next;
            fast = fast.next.next;
        }
        
        if (slow == fast) {
            fast = head;
            while (slow != fast) {
                fast = fast.next;
                slow = slow.next;
            }
            return slow;
        } else {
            return null;
        }
    }
    
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int a = in.nextInt();
        System.out.println(a);
        System.out.println("Hello World!");
        
    }
}

字节提前批二面

Java

volatile 和 synchronized 关键字 - 功能和区别; ReentrantLock 和 Condition 的用法; signal() 和 signalAll() 用哪个比较好;

Linux

怎么看内存情况:free -h ;(刚好面试前看到了,你说巧不巧) free -h 显示的 buffer 和 cache 有什么区别?(超出知识范围,瞎扯了一下 CPU 和内存间的高速缓存,内存和磁盘间的写缓存,还有redo log buffer,能水就水)

计网

DNS 访问流程:本地DNS服务器 - 根DNS服务器 - 顶级域DNS服务器 - 权威DNS服务器; DNS 用的传输层协议:UDP; DNS 劫持,怎么防御:答了HTTPS; HTTPS 流程:证书 - 验证 - 用对称加密来通信

数据库

事务的四个隔离级别; 有了可重复读,为什么还需要串行化:防止幻读; 有了解 MVCC 吗:不了解。告辞;

个人项目

怎样提高网站的性能:ThymeLeaf 模板引擎的缓存;借助 Kafka 来异步处理事件;用 Redis 做缓存和存储存活期短的信息(如验证码);用 Elasticsearch 做全文搜索(不会吧不会吧,不会还有人用 MySQL 的全文索引吧,手动狗头.jpg);利用线程池的方式,一个线程处理一个请求,同时通过 ThreadLocal 给每个线程绑定当前请求对应的用户信息,比如通过拦截器的方式,请求到达时,检查cookie信息,然后。。。;

加入帖子很多,用户数量多,怎么搞:(面试官应该是想我答分库分表,但是我没答出来)

数据结构

建堆(heap)的复杂度:如果已经有全部数据,那就从下到上建堆,O(N);如果是数据流,只能从上到下建堆,O(N*logN)

算法题

给定m * n矩阵matrix,可以从任意位置开始,向上、向下、向左、向右移动,但要求下一个位置上的元素要大于当前元素,找出矩阵中最长的递增路径的长度。

我写的:

import java.util.Scanner;
public class Main {
    
    public static int findLength(int[][] matrix, int[][] length, int i, int j) {
        if (length[i][j] != 0) {
            return length[i][j];
        }
        
        int result = 1;
        if (i - 1 >= 0 && matrix[i - 1][j] > matrix[i][j]) {
            result = Math.max(result, findLength(matrix, length, i - 1, j) + 1);
        }
        
        if (i + 1 < matrix.length && matrix[i + 1][j] > matrix[i][j]) {
            result = Math.max(result, findLength(matrix, length, i + 1, j) + 1);
        }
        
        if (j - 1 >= 0 && matrix[i][j - 1] > matrix[i][j]) {
            result = Math.max(result, findLength(matrix, length, i, j - 1) + 1);
        }
        
        if (j + 1 < matrix[0].length && matrix[i][j + 1] > matrix[i][j]) {
            result = Math.max(result, findLength(matrix, length, i, j + 1) + 1);
        }
        length[i][j] = result;
        return result;
    }
    
    public static int longestPath(int[][] matrix) {
        if (matrix == null || matrix.length == 0 || matrix[0] == null) {
            return 0;
        }
        
        int rowSize = matrix.length;
        int colSize = matrix[0].length;
        int[][] length = new int[rowSize][colSize];
        int result = 0;
        for (int i = 0; i < rowSize; i++) {
            for (int j = 0; j < colSize; j++) {
                result = Math.max(result, findLength(matrix, length, i, j));
            }
        }
        return result;
    }
    
    public static void main(String[] args) {
        //Scanner in = new Scanner(System.in);
        //int a = in.nextInt();
        //System.out.println(a);
        System.out.println("Hello World!");
    }
}

字节提前批三面

之前面试中不熟悉的知识点,有没有去康康:有; 来讲讲:讲了MVCC,undo log,两段锁协议等等;

一面中http 1.0/1.1/2.0答得没有很好,回去有看吗:看了,(可看我的总结:blog.csdn.net/alexkx/arti…

TCP连接中,影响带宽的因素有哪些?(我答了发送到缓存的速度,对方读缓存的速度,拥塞控制等等)

编程语言,数据库,计网,操作系统,哪个最熟:瑟瑟发抖;

(中间有的问题忘了)

写个 sql 吧: 给定一个表 T(id, name, age, sex, city),写出一下查询语句:男性平均年龄最高的城市。

select city
from (select avg(age) as A, city
      from T
      where sex = "male"
      group by city) as Temp
where A >= (select Temp.A from Temp);

select city
from (select avg(age) as A, city
      from T
      where sex = "male"
      group by city) as Temp
order by A desc
limit 0,1;

做个概率题 一种流行病患病概率是1%,有一种检测试纸,检测的准确率是99%,我现在试纸检测阳性了,请问我患病的概率有多大? (概率论有点忘了)

再来做两题: 用一个[0,4)的随机数生成器rand4(),模拟一个[0.6)的随机数rand6() - 常见题,直接口述

32个石块,重量不等,用天平来进行衡量,找出最重的石块,最少需要几次? - 考官提示了一下,用归并的方法,应该是31次

如果是找出最重的两块,需要几次?我答了46次,看网上有的人说31次,感觉不是很对。

shopee 后端开发

笔试(2020.7.29)

10题计算机相关的选择题 5题数学相关的选择题

3题编程题:

  1. 用多重背包算法来做
  2. 根据给出的数据建二叉搜索树,在返回所有叶子节点
  3. k个一组翻转链表 - leetcode25 (AC了2.8题)

一面

进程间的通信方式:管道(匿名和命名)、消息队列、共享内存&信号量、信号;ipcs 查看 ipc 对象;

内核空间和用户空间;为啥要这么区分;

分段机制、分页机制;

TCP四次挥手;

TCP 流量控制和拥塞控制;

输入 www.shopee.com 以后的过程:DNS查询、TCP 三次握手、验证证书、生成对称加密的秘钥,如果是 http/2.0,服务器还会主动推送相关资源。

websocket 有了解吗:没有;

IO 多路复用的几种系统调用(select、poll、epoll),主要区别;

介绍一下红黑树;

介绍一下 B+树、B树;

Java 多线程实现方式(就是怎么用多线程执行任务,比如实现 Runnable接口,实现 Callable 接口,继承 Thread 类);

MySQL的持久化手段,比如执行update 语句时,如何保证数据持久化:答了redo log,binlog,undo log等等;

ACID 是啥;

Redis 的 sorted set 底层的数据结构(跳表),原理;

Kafka 原理,怎么保证高可用、高性能:分区、主从备份、顺序IO、每个消息只在分区主节点保存一份等等;

最后是一题简单题:

在字符串中找出连续最长的数字串
样例输出

输出123058789,函数返回值9

输出54761,函数返回值5

 

接口说明

函数原型:

   unsignedint Continumax(char** pOutputstr,  char* intputstr)

输入参数:
   char* intputstr  输入字符串;

输出参数:
   char** pOutputstr: 连续最长的数字串,如果连续最长的数字串的长度为0,应该返回空字符串;如果输入字符串是空,也应该返回空字符串;  

返回值:
  连续最长的数字串的长度


输入描述
输入一个字符串。

输出描述
输出字符串中最长的数字字符串和它的长度。如果有相同长度的串,则要一块儿输出,但是长度还是一串的长度

示例1
输入
abcd12345ed125ss123058789
输出
123058789,9
import java.util.Scanner;
public class Main {
    public static int read(char[] arr, int begin) {
        int cur = begin;
        while (cur < arr.length) {
            if (arr[cur] <= '9' && arr[cur] >= '0'){
                cur++;   
            } else {
                break;
            }
        }
        return cur;
    }
    
    public static void Continumax(String input) {
        if (input == null || input.length() == 0) {
            System.out.println(", 0");
        }
        
        char[] arr = input.toCharArray();
        int resultBegin = -1;
        int resultSize = 0;
        int i = 0;
        while (i < arr.length) {
            if (arr[i] <= '9' && arr[i] >= '0'){
                int end = read(arr, i);
                if (end - i > resultSize) {
                    resultSize = end  - i;
                    resultBegin = i;
                }
                i = end;
            } else {
                i++;
            }

        }
        
        if(resultSize > 0) {
            System.out.printf("%s, %d", input.substring(resultBegin, resultBegin + resultSize), resultSize);
        } else {
            System.out.println(", 0");
        }
    }
    
    public static void main(String[] args) {
        //Scanner in = new Scanner(System.in);
        //int a = in.nextInt();
        //System.out.println(a);
        String input = "abcd12345ed125ss123058789";
        Continumax(input);
    }
}

二面

问实习;

ConcurrentHashMap 的 get、put方法;为什么插入新节点的时候是用尾插法;

web安全有了解吗:讲了下注入攻击和权限管理; CSRF(跨站域请求伪造):不太了解

讲讲 MVCC;(推荐看这篇:blog.csdn.net/waves___/ar…

Kafka 的 partition 的分配策略,就是同一个 topic 的 partition 具体分配给某个订阅它的消费者组的哪个消费者; Kafka 的 rebalance;

TCP 的 CLOSE_WAIT 和 TIME_WAIT 状态; CLOSE_WAIT 的作用;

Linux 用什么命令看 tcp 的状态信息:netstat;

算法题:

  1. 对一个数组进行排序,元素只有0,1,2:我答了用类似快排的方式;
  2. 找一个字符串里面最长回文子串:我简单讲了一下Manache算法 ,然后详细讲了动态规划的方式。

你觉得你技术方面最大的优势是什么? 职业规划?

反问环节: 进了以后怎么选择组:公司是统一招聘的,入职的时候再分配,会考虑到个人意愿。

微信

一面(2020.8.10)

上来先做两题:

  1. 倒转单链表(在原链表上倒转)
struct LinkNode {
  int value;
  struct LinkNode * next;
};
void reverseList( struct LinkNode * head );

注意:翻转后,head 指针仍然要指向链表头部。所以原来的头节点对象,翻转后,仍然需要是头节点。(可以通过修改节点的值来实现)

import java.util.Scanner;
public class Main {
    
    public static void reverseList(LinkNode head) {
        if (head == null || head.next == null) {
            return;
        }
        
        // 从第二个节点开始翻转
        LinkNode cur = head.next;
        LinkNode next = null;
        // 创建一个新节点,作为翻转后的最后一个节点
        LinKNode pre = new LinkNode();
        pre.value = head.value;
        
        while (cur != null) {
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        
        head.value = pre.value;
        head.next = pre.next;
    }
    
    public static void main(String[] args) {
    }
}
  1. 数字1-N 分布在长度为N+1的数组里面,一个元素重复1次,请求重复的元素? 时间复杂度O(N) 空间复杂度O(1)
用异或来做就完事了

(面试官是之前实习的 leader) 讲讲 Java 中的 new 和 C++ 中的 malloc() 的区别; 讲讲 Java 的垃圾回收机制(这里我水了超久); 进程和线程的区别; 为什么进程切换会比线程切换消耗的资源要多? epoll() 和 select() 的区别; TCP 三次握手和四次挥手; 两个进程分别监听 TCP 和 UDP,可以监听同一个端口吗? TIME_WAIT 状态的作用;

二面(2020.8.14)

Elasticsearch 的原理:我讲了下分词和倒排索引; Java 的 HashMap 和 TreeMap; 数据库有哪些索引; 讲讲平衡搜索树;

输入www.qq.com以后的过程:讲了下DNS,http,https,http/2.0,tcp,ip,链路层(ARP协议)等内容;

算法题: 1、单机里面有多个文件,每个文件包含若干个整数,找出这些文件中的值最大的k个数: 讲了下单线程和多线程的处理方式,单线程直接用一个小根堆,然后遍历每个文件;多线程的话,就对每个文件建一个小根堆,取得这个文件的top k,然后对这 k 个数排序,最后将每个文件拿到的结果来建一个大根堆,依次取出top k。

2、给定一个数组,里面的元素先是升序,再降序,比如 [1, 3, 5, 4, 2],找出最大的元素的下标: 我用了下二分法来做:

public class Main {

    public static int find(int[] nums, int left, int right) {
        if (left == right) {
            return left;
        }

        int mid = left + ((right - left) >> 1);
        if (nums[mid] > nums[mid + 1]) {
            return find(nums, left, mid);
        } else {
            return find(nums, mid + 1, right);
        }
    }

    public static int find(int[] nums) {
        if(nums == null || nums.length == 0) {
            return -1;
        }

        return find(nums, 0, nums.length - 1);
    }

    public static void main(String[] args) {
        int[] nums = new int[]{10, 15, 20, 20, 21,  22, 35,  20, 10, 9, 8, 7, 6};
        System.out.println(find(nums));
    }
}

三面(2020.8.18)

一共面了三个小时,前两个小时做题,最后一个小时问答。我人都傻了。

一共出了5题,中间跑测试样例以及面试官换题花了点时间,只做出了前4题,最后一题没写完。

1、LRU

/*
1. 某操作系统采用 LRU 作为内存页面置换算法。
    假设初始内存为空,现给定将访问的内存页序列 pages, 序列长度 page_cnt 和内存总容量(页面数) mem,请返回缺页中断的次数。
    例如:序列 pages = 1, 2, 3, 2, 1, 4; 内存 mem = 2
    返回:5
    int check_page_faults(int pages[], int page_cnt, int mem);
*/

import java.io.*;
import java.util.*;

class LRU {
  class Node{
    int key;
    Node pre;
    Node next;
    
    Node(int key, Node pre, Node next) {
      this.key = key;
      this.pre = pre;
      this.next = next;
    }
  }
  
  int capacity;
  int size;
  Node head = new Node(-1, null, null);
  Node tail = new Node(-1, null, null);
  
  Map<Integer, Node> map = new HashMap<>();
  
  {
    head.next = tail;
    tail.pre = head;
    size = 0;
  }
  
  public LRU(int capacity) {
    if (capacity <= 0) {
      throw new IllegalArgumentException();
    }
    this.capacity = capacity;
  }
  
  public boolean access(int key) {
    boolean result = false;
    
    Node node = map.get(key);
    if (node != null) {
      // 命中
      result = true;
      moveToHead(node);
    } else {
      // 缺页
      if (size == capacity) {
        // LRU 已满
        Node last = removeLast();
        map.remove(last.key);
        size--;
      }
      
      node = new Node(key, null, null);
      map.put(key, node);
      addToHead(node);
      size++;
    }
    return result;
    
  }
  
  private void addToHead(Node current) {
    Node oldHead = head.next;
    head.next = current;
    current.pre = head;
    current.next = oldHead;
    oldHead.pre = current;
  }
  
  private void remove(Node node) {
    Node pre = node.pre;
    Node next = node.next;
    pre.next = next;
    next.pre =  pre;
  }
  
  private void moveToHead(Node node) {
    remove(node);
    addToHead(node);
  }
  
  private Node removeLast() {
    if (size == 0) {
      return null;
    }
    
    Node last = tail.pre;
    remove(last);
    return last;
  }
  
  
}

public class MyCode {
  
  public static int check_page_faults(int pages[], int page_cnt, int mem) {
    int result = 0;
    
    if (pages == null || page_cnt <= 0 || pages.length < page_cnt) {
      return result;
    }
    
    LRU lru = new LRU(mem);
    for (int i = 0; i < page_cnt; i++) {
      boolean isIn = lru.access(pages[i]);
      if (!isIn) {
        result++;
      }
    }
    
    return result;
  }
  
  public static void main (String[] args) {
	int[] pages = new int[]{1, 1, 1,1,1,1};
    int mem = 2;
    
    System.out.println(check_page_faults(pages, pages.length, mem));
  }
}

2、表达式比较

注意:这里不能直接把 a-z 当成 1-26 来计算,如果这样做的话,第五个例子就会错了。

/*
2. 检查两个表达式是否等价。表达式仅包含小写字母 'a'-'z', '+', '-', '(', ')',且表达式里的未知数仅有一个字符。
例如:
1) exp1 = "a+b+c-a", exp2 = "(b+c)", result: true
2) exp1 = "a-b-c", exp2 = "a-(b+c)", result: true
3) exp1 = "a-b+c", exp2 = "a-(b+c)", result: false
4) exp1 = "a-b+c", exp2 = "a-(b-(c-d)-d)", result: true
5) exp1 = "a+d", exp2 = "b+c", result: false

bool check(const char* exp1, const char* exp2);
*/


public class MyCode {
  
  public static int process(char[] arr1, int begin, int[] count) {
    // int[] result = new int[26];
    
    int i = begin;
    while (i < arr1.length) {
      if (arr1[i] == '(') {
        
        // 处理括号情况
        int[] child = new int[26];
        int next = process(arr1, i + 1, child);
        
        for (int j = 0; j < 26; j++) {
          if (i > 0 && arr1[i - 1] == '-') {
            // 负号
            count[j] -= child[j];
          } else {
            // 括号前为正号
            count[j] += child[j];
          }
        }
        i = next;
      } else if (arr1[i] == ')'){
        i++;
        return i;
      } else {
        
        if (arr1[i] <= 'z' && arr1[i] >= 'a') {
          if (i > 0 && arr1[i - 1] == '-') {
            count[arr1[i] - 'a'] -= 1; 
          } else {
            count[arr1[i] - 'a'] += 1;
          }
        }
        i++;
      }
      
      
    }
    return i;
  }
  
  public static boolean check(String str1, String str2) {
    char[] arr1 = str1.toCharArray();
    char[] arr2 = str2.toCharArray();
    
    int[] result1 = new int[26];
    int[] result2 = new int[26];
    
    process(arr1, 0, result1);
    process(arr2, 0, result2);
    
    for (int i = 0; i < 26; i++) {
      if (result1[i] != result2[i]){
        return false;
      }
    }
    
    return true;
  }
  
  public static void main(String[] args) {
    String str1 = "a+b+c-a";
    String str2 = "(b+c)";
    System.out.println(check(str1, str2));
  
    String str3 = "a-b-c";
    String str4 = "a-(b+c)";
    System.out.println(check(str3, str4));
    
    String str5 = "a-b+c";
    String str6 = "a-(b+c)";
    System.out.println(check(str5, str6));
    
    String str7 = "a-b+c";
    String str8 = "a-(b-(c-d)-d)";
    System.out.println(check(str7, str8));
    
    String str9 = "a+d";
    String str10 = "b+c";
    System.out.println(check(str9, str10));
  }
}

3、恢复 IP 地址

/*
3. 给一个由数字组成的字符串,求出所有可能的 IP 地址。
例如:给出字符串 "25525511135",所有可能的 IP 地址为:
[  "255.255.11.135", "255.255.111.35"  ]
vector<string> restore_ip_addrs(const string& s);
*/

import java.util.*;

public class Main1 {

    static void process(String s, int sectionNum, int begin, String ip, List<String> result) {
        if (sectionNum ==  4) {
            if (begin == s.length()) {
                result.add(ip.substring(0, ip.length() - 1));
            }
            return;
        }

        if (sectionNum > 4 || (4 - sectionNum) * 3 < (s.length() - begin)) {
            return;
        }

        for (int i = 1; i <= 3; i++) {
            if (begin + i > s.length()) {
                break;
            }
            String nextSection = s.substring(begin, begin + i);
            if (Integer.parseInt(nextSection) > 255) {
                return;
            } else {
                process(s, sectionNum + 1, begin + i, ip + nextSection + ".", result);
            }
        }
    }

    static List<String> restore_ip_addrs(String s) {
        List<String> result = new ArrayList<>();

        if (s == null || s.length() < 4 || s.length() > 12) {
            return result;
        }

        process(s, 0, 0, "", result);
        return result;
    }

    public static void main(String[] args) {
        String str = "25525511135";

        List<String> result = restore_ip_addrs(str);

        for (String s : result) {
            System.out.println(s);
        }

        Map<String, String> map = new HashMap<>();
        for(Map.Entry e : map.entrySet()) {
            e.getValue();
        }
    }
}

4、

/*
4. 对输入的数组按出现的频率进行排序,若出现频率一致则按数字升序排序。
例如:1, 2, 4, 9, 4, 1, 4, 2, 结果为:4, 4, 4, 1, 1, 2, 2, 9

void sort(int arr[], int n);
*/
import java.util.*;

class MyCode{
  static void sort(int[] arr, int n) {
    if (arr == null || arr.length < 0) {
      return;
    }
    
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < arr.length; i++) {
      Integer value = map.get(arr[i]);
      
      if (value == null) {
        map.put(arr[i], 1);
      } else {
        map.put(arr[i], value + 1);
      }
    }
    
    int[][] result = new int[map.size()][];
    int i = 0;
    for (Map.Entry<Integer, Integer> e : map.entrySet()) {
      result[i++] = new int[]{e.getKey(), e.getValue()};
    }
    
    Arrays.sort(result, (int[] a, int[] b) -> {
      if (a[1] == b[1]) {
        return a[0] - b[0];
      } else {
        return b[1] - a[1];
      }
    });
    
    int cur = 0;
    for (i = 0; i < result.length; i++) {
      for (int j = 0; j < result[i][1]; j++){
        arr[cur++] = result[i][0];
      }
    }
  }
  
  public static void main(String[] args) {
    int[] arr = new int[]{1, 2, 4, 9, 4, 1, 4, 2};
    sort(arr, arr.length);
    for(int i = 0; i < arr.length; i++) {
      System.out.printf("%d, ", arr[i]);
    }
  }
}

5、

这题我还没做完,只贴个题目吧

/*
5. 给定一个排序链表,删除所有重复的元素,只留下原链表中没有重复的元素。
例如: 1->1->2->3->3->4->4->5->6->6->null, return: 2->5->null

struct LinkNode {
  int value;
  struct LinkNode * next;
};
struct LinkNode* delete_duplicates(struct LinkNode* head);
*/

-----------------------

做完这些题以后,休息了五分钟,然后开始问操作系统、计网、Redis:(只记得一部分) 进程和线程的区别; 什么时候用多线程,什么时候用多进程; 32位和64位系统中,进程虚拟内存的布局; 进行系统调用时的过程;

介绍一下Redis; Redis 的内存布局;(不会) 为什么Redis单线程也这么快:I/O复用,使用子进程去处理耗时多的任务(如rdb快照,aof文件改写);

select / epoll; TCP握手过程; 监听未完成的连接时(listen阶段),如何防止DDoS;

四面(2020.8.21)

问的很深,把我问傻了。只记得部分问题:

问了实习内容; 内核态和用户态的区别?为啥要区分内核空间和用户空间?怎么实现内核态?和底层的CPU指令有关吗? 进程间通信方式?(面试官说有十多种。。。我迷惑了) UnixSocket; sleep() 等定时机制,定时精度是由什么因素决定的?怎么实现? 睡眠的进程/线程怎么被唤醒;

堆排序;

Mysql 联合索引的结构;

找了一下,进程间通信大概有下面这些类别:(参考《Linux/UNIX系统编程手册》 book.douban.com/subject/258…