GDB 简介

GDB 是 Linux 系统中,非常常见的调试工具,它有以下功能:

  • Start your program, specifying anything that might affect its
    behavior.
  • Make your program stop on specified conditions.
  • Examine what has happened, when your program has stopped.
  • Change things in your program, so you can experiment with correcting the effects of one bug and go on to learn about another.

常用的参数命令:

  • info threads:查看全部线程
  • thread n:指定某个线程
  • b:在某处打断点
  • c:继续往下走
  • s:执行一行代码,如果代码函数调用,则进入函数
  • n:执行一行代码,函数调用不进入
  • p:打印某个变量值
  • list:打印代码的文本信息
  • bt:查看某个线程的栈帧
  • info b:查看当前所有断点信息

调试环境搭建

直接在 linux 下面使用 gdb,这种应该是目前市面上最简单有效的方式。

  1. 安装gdb
yum install -y cmake make gcc gcc-c++ ncurses-devel bison gdb
  1. 下载、解压源码
wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-boost-5.7.25.tar.gztar zxvf mysql-boost-5.7.25.tar.gzmkdir -p /gdb/mysql/mkdir -p /gdb/data/
  1. 安装数据库
cmake -DCMAKE_INSTALL_PREFIX=/gdb/mysql/ -DMYSQL_DATADIR=/gdb/data/ -DSYSCONFDIR=/gdb/mysql/ -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DWITH_FEDERATED_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DMYSQL_UNIX_ADDR=/gdb/mysql/mysql3.sock -DMYSQL_TCP_PORT=3306 -DENABLED_LOCAL_INFILE=1 -DEXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DMYSQL_USER=mysql -DWITH_BINLOG_PREALLOC=ON -DWITH_BOOST=/gdb/mysql-5.7.25/boost/boost_1_59_0 -DWITH_DEBUG=1

-DWITH_DEBUG=1 是最关键的,它的作用是开启DBUG

make&&make install
  1. 初始化数据库
vim /etc/my.cnf#简易配置下my.cnf文件[client]port            = 3306socket          = /gdb/data/mysqld.sock[mysqld]port            = 3306socket          =/gdb/data/mysqld.sockskip-external-lockingkey_buffer_size = 8Mmax_allowed_packet = 1Mtable_open_cache = 64sort_buffer_size = 512Knet_buffer_length = 8Kread_buffer_size = 128Kread_rnd_buffer_size = 256Kmyisam_sort_buffer_size = 8Mlower_case_table_names=1innodb_buffer_pool_size=300Mlog-bin=mysql-bincharacter_set_server=utf8binlog_format=rowdatadir=/gdb/datalog-error =/gdb/data/error.logpid-file = /gdb/data/mysql.pidinnodb_log_file_size=512Minnodb_log_files_in_group = 3sql_mode=''autocommit=1server-id       = 1max_connections=1500wait_timeout=70interactive_timeout=70skip-name-resolve[mysqldump]quickmax_allowed_packet = 16M[myisamchk]key_buffer_size = 20Msort_buffer_size = 20Mread_buffer = 2Mwrite_buffer = 2M
  1. 启动数据库

赋权,以便mysql用户有权限在该目录下生成文件:
chown -R mysql:mysql /gdb/data

初始化数据库命令:

cd /gdb/mysql/bin
./mysqld --initialize --user=mysql --basedir=/gdb/mysql --datadir=/gdb/data

启动数据库:

cd /gdb/mysql/support-files./mysql.server start

insert 断点调试

  1. 查看 mysql 进程 id
[root@ops sql]# ps aux | grep mysql
root       629  0.0  0.0 112724   972 pts/2    S+   14:52   0:00 grep -E --color=auto mysql
root     20926  0.0  0.0 113312  1628 pts/0    S    11:15   0:00 /bin/sh /gdb/mysql/bin/mysqld_safe --datadir=/gdb/data --pid-file=/gdb/data/mysql.pid
mysql    21357  0.0  5.8 1740820 223820 pts/0  Sl   11:15   0:01 /gdb/mysql/bin/mysqld --basedir=/gdb/mysql --datadir=/gdb/data --plugin-dir=/gdb/mysql/lib/plugin --user=mysql --log-error=/gdb/data/error.log --pid-file=/gdb/data/mysql.pid --socket=/gdb/data/mysqld.sock --port=3306

可以看到此时mysql的进程号为:20926

  1. gdb 中 attach mysql 进程
[root@ops ~]# gdb
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-119.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
(gdb) attach 21357
Attaching to process 21357
Reading symbols from /gdb/mysql/bin/mysqld...done.
Reading symbols from /lib64/libpthread.so.0...(no debugging symbols found)...done.
[New LWP 21617]
[New LWP 21387]
[New LWP 21386]
[New LWP 21384]
[New LWP 21383]
[New LWP 21382]
[New LWP 21381]
[New LWP 21380]
[New LWP 21379]
[New LWP 21378]
[New LWP 21377]
[New LWP 21376]
[New LWP 21375]
[New LWP 21374]
[New LWP 21373]
[New LWP 21369]
[New LWP 21368]
[New LWP 21367]
[New LWP 21366]
[New LWP 21365]
[New LWP 21364]
[New LWP 21363]
[New LWP 21362]
[New LWP 21361]
[New LWP 21360]
[New LWP 21359]
[New LWP 21358]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Loaded symbols for /lib64/libpthread.so.0
Reading symbols from /lib64/libcrypt.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/libcrypt.so.1
Reading symbols from /lib64/libdl.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libdl.so.2
Reading symbols from /lib64/librt.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/librt.so.1
Reading symbols from /lib64/libstdc++.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libstdc++.so.6
Reading symbols from /lib64/libm.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libm.so.6
Reading symbols from /lib64/libgcc_s.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/libgcc_s.so.1
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Reading symbols from /lib64/libfreebl3.so...Reading symbols from /lib64/libfreebl3.so...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /lib64/libfreebl3.so
Reading symbols from /lib64/libnss_files.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libnss_files.so.2
Reading symbols from /lib64/libnss_sss.so.2...Reading symbols from /lib64/libnss_sss.so.2...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /lib64/libnss_sss.so.2
0x00002b15ce803f0d in poll () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-222.el7.x86_64 libgcc-4.8.5-39.el7.x86_64 libstdc++-4.8.5-39.el7.x86_64 nss-softokn-freebl-3.34.0-2.el7.x86_64 sssd-client-1.16.0-19.el7.x86_64
(gdb)
  1. 找到断点

这次看的是 insert 插入的流程,找到 sql_insert.cc 文件:

源码中的函数为:Sql_cmd_insert::mysql_insert

  1. 设置断点
(gdb) b Sql_cmd_insert::mysql_insert
Breakpoint 1 at 0x175aed9: file /gdb/mysql-5.7.25/sql/sql_insert.cc, line 423.

然后查看下线程的栈帧:

(gdb) bt
#0  0x00002b15ce803f0d in poll () from /lib64/libc.so.6
#1  0x0000000001667f87 in Mysqld_socket_listener::listen_for_connection_event (this=0x3967430) at /gdb/mysql-5.7.25/sql/conn_handler/socket_connection.cc:852
#2  0x0000000000eb15cc in Connection_acceptor<Mysqld_socket_listener>::connection_event_loop (this=0x4f882e0) at /gdb/mysql-5.7.25/sql/conn_handler/connection_acceptor.h:66
#3  0x0000000000ea904a in mysqld_main (argc=38, argv=0x383c248) at /gdb/mysql-5.7.25/sql/mysqld.cc:5149
#4  0x0000000000ea01bd in main (argc=9, argv=0x7ffc73765b88) at /gdb/mysql-5.7.25/sql/main.cc:25
  1. 数据库登陆

gdb断点设置完后,起个新的数据库连接:

会发现此时无法登陆,在gdb中执行next:

(gdb) n
Single stepping until exit from function poll,
which has no line number information.
Mysqld_socket_listener::listen_for_connection_event (this=0x3967430) at /gdb/mysql-5.7.25/sql/conn_handler/socket_connection.cc:859
859   if (retval < 0 && socket_errno != SOCKET_EINTR)

通过输出可以知道数据库处于获取系统 socket 状态。接下来需要跳过的步骤有些多,我们直接使用 continue (直接到下一段可执行代码)

(gdb) c
Continuing.

新起客户端连接成功:

[root@ops bin]# mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 5.7.25-debug-log Source distributionType 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql>
  1. 数据库插入

插入操作前,切换schema和查询都是没问题的:

mysql> use gdb
Database changed
mysql> show tables;
+---------------+
| Tables_in_gdb |
+---------------+
| test          |
+---------------+
1 row in set (0.00 sec)mysql> select * from test;
+------+
| id   |
+------+
|    1 |
|    2 |
+------+
2 rows in set (0.00 sec)

插入一条 id=3 的数据,出现了等待:

mysql> insert into test values (3);
  1. 分析断点信息

断点触发,如下:

(gdb) c
Continuing.
[Switching to Thread 0x2b15faf02700 (LWP 21617)]Breakpoint 1, Sql_cmd_insert::mysql_insert (this=0x2b1614008348, thd=0x2b1614003af0, table_list=0x2b1614007db8) at /gdb/mysql-5.7.25/sql/sql_insert.cc:423
423   DBUG_ENTER("mysql_insert");

bt 命令展示栈帧:

(gdb) bt
#0  Sql_cmd_insert::mysql_insert (this=0x2b1614008348, thd=0x2b1614003af0, table_list=0x2b1614007db8) at /gdb/mysql-5.7.25/sql/sql_insert.cc:423
#1  0x000000000176256e in Sql_cmd_insert::execute (this=0x2b1614008348, thd=0x2b1614003af0) at /gdb/mysql-5.7.25/sql/sql_insert.cc:3118
#2  0x000000000153b093 in mysql_execute_command (thd=0x2b1614003af0, first_level=true) at /gdb/mysql-5.7.25/sql/sql_parse.cc:3596
#3  0x0000000001540820 in mysql_parse (thd=0x2b1614003af0, parser_state=0x2b15faf01690) at /gdb/mysql-5.7.25/sql/sql_parse.cc:5570
#4  0x0000000001536131 in dispatch_command (thd=0x2b1614003af0, com_data=0x2b15faf01df0, command=COM_QUERY) at /gdb/mysql-5.7.25/sql/sql_parse.cc:1484
#5  0x0000000001534f9a in do_command (thd=0x2b1614003af0) at /gdb/mysql-5.7.25/sql/sql_parse.cc:1025
#6  0x00000000016658dc in handle_connection (arg=0x39610f0) at /gdb/mysql-5.7.25/sql/conn_handler/connection_handler_per_thread.cc:306
#7  0x0000000001ced592 in pfs_spawn_thread (arg=0x5508e50) at /gdb/mysql-5.7.25/storage/perfschema/pfs.cc:2190
#8  0x00002b15cd699e25 in start_thread () from /lib64/libpthread.so.0
#9  0x00002b15ce80ebad in clone () from /lib64/libc.so.6

接下来输入 n 会逐行输出,我们这里直接 continue,阻塞的 insert 也完成了:

mysql> insert into test values (3);
Query OK, 1 row affected (2 min 49.57 sec)

发布一个特殊版本的mysql

有些函数所在源码的位置比较好理解,比如上面的 insert 功能,或者 delete 功能。是不是可以通过修改 delete 函数,打包一个数据永不会被删除的 mysql 呢?

定位函数位置

先追踪mysql进程:

(gdb) attach 21357
Attaching to process 21357
Reading symbols from /gdb/mysql/bin/mysqld...done.
Reading symbols from /lib64/libpthread.so.0...(no debugging symbols found)...done.
[New LWP 5584]
[New LWP 5583]
[New LWP 21617]
[New LWP 21387]
[New LWP 21386]
[New LWP 21384]
[New LWP 21383]
[New LWP 21382]
[New LWP 21381]
[New LWP 21380]
[New LWP 21379]
[New LWP 21378]
[New LWP 21377]
[New LWP 21376]
[New LWP 21375]
[New LWP 21374]
[New LWP 21373]
[New LWP 21369]
[New LWP 21368]
[New LWP 21367]
[New LWP 21366]
[New LWP 21365]
[New LWP 21364]
[New LWP 21363]
[New LWP 21362]
[New LWP 21361]
[New LWP 21360]
[New LWP 21359]
[New LWP 21358]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Loaded symbols for /lib64/libpthread.so.0
Reading symbols from /lib64/libcrypt.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/libcrypt.so.1
Reading symbols from /lib64/libdl.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libdl.so.2
Reading symbols from /lib64/librt.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/librt.so.1
Reading symbols from /lib64/libstdc++.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libstdc++.so.6
Reading symbols from /lib64/libm.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libm.so.6
Reading symbols from /lib64/libgcc_s.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/libgcc_s.so.1
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Reading symbols from /lib64/libfreebl3.so...Reading symbols from /lib64/libfreebl3.so...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /lib64/libfreebl3.so
Reading symbols from /lib64/libnss_files.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libnss_files.so.2
Reading symbols from /lib64/libnss_sss.so.2...Reading symbols from /lib64/libnss_sss.so.2...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /lib64/libnss_sss.so.2
0x00002b15ce803f0d in poll () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-222.el7.x86_64 libgcc-4.8.5-39.el7.x86_64 libstdc++-4.8.5-39.el7.x86_64 nss-softokn-freebl-3.34.0-2.el7.x86_64 sssd-client-1.16.0-19.el7.x86_64

delete函数这里打上断点:

(gdb) b Sql_cmd_delete::mysql_delete
Breakpoint 1 at 0x175198b: file /gdb/mysql-5.7.25/sql/sql_delete.cc, line 50.

数据库执行delete语句触发断点:

mysql> delete from test where id =3;

gbk断点信息:

Breakpoint 1, Sql_cmd_delete::mysql_delete (this=0x2b16040020c8, thd=0x2b1604007e30, limit=18446744073709551615) at /gdb/mysql-5.7.25/sql/sql_delete.cc:50
50    DBUG_ENTER("mysql_delete");

查看相关栈帧:

#0  Sql_cmd_delete::mysql_delete (this=0x2b16040020c8, thd=0x2b1604007e30, limit=18446744073709551615) at /gdb/mysql-5.7.25/sql/sql_delete.cc:50
#1  0x0000000001755cc6 in Sql_cmd_delete::execute (this=0x2b16040020c8, thd=0x2b1604007e30) at /gdb/mysql-5.7.25/sql/sql_delete.cc:1392
#2  0x000000000153b12f in mysql_execute_command (thd=0x2b1604007e30, first_level=true) at /gdb/mysql-5.7.25/sql/sql_parse.cc:3606
#3  0x0000000001540820 in mysql_parse (thd=0x2b1604007e30, parser_state=0x2b15faf43690) at /gdb/mysql-5.7.25/sql/sql_parse.cc:5570
#4  0x0000000001536131 in dispatch_command (thd=0x2b1604007e30, com_data=0x2b15faf43df0, command=COM_QUERY) at /gdb/mysql-5.7.25/sql/sql_parse.cc:1484
#5  0x0000000001534f9a in do_command (thd=0x2b1604007e30) at /gdb/mysql-5.7.25/sql/sql_parse.cc:1025
#6  0x00000000016658dc in handle_connection (arg=0x54b6510) at /gdb/mysql-5.7.25/sql/conn_handler/connection_handler_per_thread.cc:306
#7  0x0000000001ced592 in pfs_spawn_thread (arg=0x5508e50) at /gdb/mysql-5.7.25/storage/perfschema/pfs.cc:2190
#8  0x00002b15cd699e25 in start_thread () from /lib64/libpthread.so.0
#9  0x00002b15ce80ebad in clone () from /lib64/libc.so.6

修改源码

可以看到 #1 是 Sql_cmd_delete::execute ,这个就是 delete 处理的函数,去源代码中找到相应函数:

把正真实现删除逻辑的代码给注释掉,返回的 res 值直接赋成 true:

bool Sql_cmd_delete::execute(THD *thd)
{DBUG_ASSERT(thd->lex->sql_command == SQLCOM_DELETE);LEX *const lex= thd->lex;SELECT_LEX *const select_lex= lex->select_lex;SELECT_LEX_UNIT *const unit= lex->unit;TABLE_LIST *const first_table= select_lex->get_table_list();TABLE_LIST *const all_tables= first_table;if (delete_precheck(thd, all_tables))return true;DBUG_ASSERT(select_lex->offset_limit == 0);unit->set_limit(select_lex);/* Push ignore / strict error handler */Ignore_error_handler ignore_handler;Strict_error_handler strict_handler;if (thd->lex->is_ignore())thd->push_internal_handler(&ignore_handler);else if (thd->is_strict_mode())thd->push_internal_handler(&strict_handler);/*注释以下删除逻辑的代码*/
/*MYSQL_DELETE_START(const_cast<char*>(thd->query().str));bool res = mysql_delete(thd, unit->select_limit_cnt);MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func());
*//*直接返回true*/bool res =true;/* Pop ignore / strict error handler */
if (thd->lex->is_ignore() || thd->is_strict_mode())thd->pop_internal_handler();return res;
}

然后上文的方法去重新编译mysql,启动后就会发现delete语句无法删除数据了。

调试总结

如果想深入学习源码,就可以从栈帧出发,但是这是基于知道函数接口功能的前提下进行的,如果不知道某个功能会调用什么函数,断点调试就很难进行了。

直接去读完 mysql 所有源码,成本太高,而且 mysql 的代码结构并不友好,耗时耗力不值得。最好的方案还是遇到问题时或针对特殊的一个功能点按需去寻找函数入口,然后逐步深入分析。

希望这篇文章可以帮到想接触mysql源码调试的同学,以后我遇到特殊的问题也会通过gdb去调试涉及的相关函数,大家可以持续关注~

参考链接 :

https://mp.weixin.qq.com/s/lLfbUpd_SMCpFm1vNQlHRQ

利用GDB调试 MSQL相关推荐

  1. dev c++调试怎么看变量的值_利用GDB调试 MSQL

    原创 张政俊 老叶茶馆 来自专辑 MySQL修行 作者:张政俊 就职于中欧基金,知数堂粉丝,数据库爱好者,熟悉RDBMS.nosql.new sql等各类数据库. 啃完O'reilly的<高性能 ...

  2. 【汇编优化】之linux下如何利用gdb调试汇编代码

    1.gdb调试汇编代码 (1).假设有如下代码,test.c /*test.c*/ void main() {int a, int b, int c;a = 1;b = 2;add_mmx(a, b, ...

  3. 【FFMPEG系列】之ffmpeg怎么利用gdb调试以及gdb调试快捷键

    1.编译可调试的FFmepg (1).使用命令: ./configure --enable-debug --disable-optimizations --disable-asm --disable- ...

  4. linux docker中gdb调试断点不停

    1.检测是否有调试信息 编译-g 去掉优化 -o0 2.warning: Error disabling address space randomization: Operation not perm ...

  5. nginx源码分析--使用GDB调试

    在学习优秀的源代码时是少不了源码的跟踪与调试,它不仅是我们解决程序bug的有效途径,也是我们理解.学习优秀源码的有效途径. 本文主要介绍一些源码调试的方法,并结合Nginx源码进行示例. 1,利用GD ...

  6. Nginx学习之十四-GDB调试Nginx初试

    本文的测试环境: Win7+虚拟机VMWareVMware-workstation-full-7.1.4-385536+Ubuntu12.04 Nginx-1.4.0 要想有效的研究Nginx源码,必 ...

  7. qemu debug linux内核,在QEMU环境中使用GDB调试Linux内核

    简介 对用户态进程,利用gdb调试代码是很方便的手段.而对于内核态的问题,可以利用crash等工具基于coredump文件进行调试.其实我们也可以利用一些手段对Linux内核代码进行gdb调试,qem ...

  8. linux core文件GDB调试方法

    一.如何使用core文件 1. 使用core文件 在core文件所在目录下键入: gdb -c coreXXX 它会启动GNU的调试器,来调试core文件,并且会显示生成此core文件的程序名,中止此 ...

  9. gdb 调试+子进程+线程

    一.gdb常用指令: gcc -g main.c //在目标文件加入源代码的信息 gdb a.out (gdb) start //开始调试 (gdb) n //一条一条执行 (gdb) step/s ...

最新文章

  1. 习题3-3 数数字(Digit Counting , ACM/ICPC Danang 2007, UVa1225)
  2. 不能跳过的《程序员的职业素养》(The Clean Coder)中的一个章节
  3. linux 下 `dirname $0` 脚本文件放置的目录
  4. a*算法的优缺点_轻松理解机器学习算法-朴素贝叶斯
  5. CTEX - 在线文档 - TeX/LaTeX 常用宏包
  6. linux根目录挂载到2440开发板,飞凌2440开发板挂载NFS
  7. ack是什么,如何使用Ack机制,如何关闭Ack机制,基本实现,STORM的消息容错机制,Ack机制
  8. 平板游戏问题(luogu 2003/2018 特长生 T4)
  9. 392. 判断子序列 golang 关于布尔类型返回值判断的思考
  10. vue中引用swiper轮播插件
  11. VALSE学习(十):图像去模糊-image deblurring
  12. android 按键,Android 7.0 Power 按键处理流程
  13. logit回归模型假设_LOGISTIC回归分析
  14. IPv6实验5:IPv4到IPv6的过渡
  15. 计算机电源atx,除了ATX和SFX电源,其实还有很多种电源规格,你了解几个?
  16. java 调用felix_使用eclipse开发felix的OSGI插件
  17. 如何安装cadence 软件
  18. SNMP MIB库的介绍
  19. ImportError: No module named 'win32api'
  20. leetcode 1647

热门文章

  1. virtualbox+vagrant快速创建虚拟机
  2. java设置绝对布局_浅谈Java绝对布局 原创
  3. 单肩包属于什么类目_Lazada时尚类目成为优势类目?
  4. 双显卡单独分辨率_甜点光追显卡—带你实现GAMING梦!!!
  5. numpy 转置_Python中Numpy.transpose()
  6. 乔布斯斯坦福大学演讲pdf_演讲能力训练方法amp;优秀的演讲视频
  7. HTML5 Canvas中实现文字链接
  8. Fixcel电子表格——报表模块(二)
  9. java判断题及答案_Java面试题及解析(判断题)
  10. echo字符集 shell_Shell echo命令:输出字符串