ubuntu下使用GDB调试segment fault错误

注意:下面的使用仅在ubuntu实验过。

1.打开ubuntu的core dump,这样程序出错后会生成corefile以供我们分析

echo '/tmp/corefile/core.%e.%p.%t' | sudo tee /proc/sys/kernel/core_pattern
ulimit -c unlimited

然后创建/tmp/corefile文件夹

mkdir /tmp/corefile/

2.运行程序,如果出现段错误(核心已转储),则说明corefile已经生成了。检查/tmp/corefile下是否有新生成的错误文件。

如果没有,执行cat /proc/sys/kernel/core_pattern,检查/tmp/corefile/core.%e.%p.%t是否写入了

3.使用gdb调试错误

Swoole Server架构分析

一.简介

首页这里引用一下swoole的官方介绍:

swoole:面向生产环境的 PHP 异步网络通信引擎
使 PHP 开发人员可以编写高性能的异步并发 TCP、UDP、Unix Socket、HTTP,WebSocket 服务。Swoole 可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(IOT)、车联网、智能家居等领域。 使用 PHP + Swoole 作为网络通信框架,可以使企业 IT 研发团队的效率大大提升,更加专注于开发创新产品。

通过上述的介绍,我们是可以得出几点信息的

  • 1.swoole可以投入生产环境
  • 2.使用php编写
  • 3.异步网络通信引擎,支持大量的网络协议,并且具有很高的网络性能

swoole server与传统的php运行模式是完全不同的,它是常驻内存的,省去了大量的php脚本的初始化。

二.swoole server的是怎么运行的

swoole进程/线程模型

这是一张官方的swoole运行时进程/线程模型。

1.Master进程

Master进程是一个多线程模型,其中包括Master线程,Reactor线程组,心跳检测线程,UDP收包线程。

以http server为例,Master线程负责监听(listen)端口,然后接受(accept)新的连接,然后将这个连接分配给一个Reactor线程,由这个Reactor线程监听此连接,一旦此连接可读时,读取数据,解析协议,然后将请求投递到worker进程中去执行。

Master进程是使用select/poll进行IO事件循环的,这是因为Master进程中的文件描述符只有几个(listenfd等),Reactor线程使用的是epoll,因为Reactor线程中会监听大量连接的可读事件,使用epoll可以支持大量的文件描述符。

2.Manager进程

Manager进程是专门用来管理Worker进程组和Task进程组的。它会Fork出指定数量的Worker进程和Task进程,并且有以下职能:

  • 子进程结束运行时,manager进程负责回收此子进程,避免成为僵尸进程。并创建新的子进程
  • 服务器关闭时,manager进程将发送信号给所有子进程,通知子进程关闭服务
  • 服务器reload时,manager进程会逐个关闭/重启子进程

3.Worker进程和Task进程

Worker进程接收Reactor线程投递过来的数据,执行php代码,然后生成数据交给Reactor线程,由Reactor线程通过tcp将数据返回给客户端。(如果是UDP,Worker进程直接将数据发送给客户端)。

Worker进程中执行的php代码和我们平时写php是一样的,它等同于php-fpm。但是众所周知,php-fpm下,php在处理异步操作时是很无力的,swoole提供的Task进程可以很好的解决这个问题。Worker进程可以将一些异步任务投递给Task进程,然后直接返回,处理其他的由Reactor线程投递过来的事件。

Task进程以完全同步阻塞的方式运行,一个Task进程在执行任务期间,是不接受从Worker进程投递的任务的,当Task进程执行完任务后,会异步地通知worker进程告诉它此任务已经完成。

所以介绍完上述的一些概念后,再引用一张官方的swoole执行流程图。
swoole运行流程图

这里需要注意,在文档上说的是:Workder进程组和Task进程组是由Manager进程Fork出来的,但是流程图上画的是在启动服务器时Fork出主进程和Worker进程组以及Tasker进程组。

三、使用swoole和传统php开发的优缺点

在说这个话题之前,需要先了解一下CGI,FASTCGI。

1.CGI
CGI的全称是Common Gateway Interface,通用网关接口,它使得任何一个拥有标准输入输出的程序拥有提供web server的能力。假设我们写了一个Hello World的c++程序,这个程序接受输入{text},输出{text},Hello World。

以nginx作为接受http请求为例,nginx接受一个http请求,Fork出一个进程,将http请求带来的text参数作为输入,执行完hello world程序,将输出{text},Hello World作为输出,销毁这个Fork出来的进程,由nginx返回给客户端。

这种方式虽然简单,但是要不断的Fork进程,销毁进程。

2.FASTCGI
FASTCGI,顾名思义,它是CGI的改进版,是一个常驻型的CGI服务。我们常用的php-fpm就是这种模式运行的,php-fpm负责Forl多个进程,每个进程中都运行了php的解释器。可以在终端下看一下php-fpm的进程:
php-fpm进程

一个php-fpm主进程,pid是1263,Fork出了3个子进程。在nginx+php-fpm的组合中,nginx负责接受http请求,将请求封装好交给php-fpm,php-fpm将请求按照一定的规则交给一个子进程去执行,这个子进程中的php解释器加载php代码运行。也是因为这个原因,传统的php只能作为web server。

然后我们发现,nginx+php-fpm的组合和我们Reactor+Worker子进程的运行方式非常相似。

3.swoole的运行方式
这里以swoole作为http server为例(传统php几乎都是作为web服务)。

首先swoole是实现了http server的,也就是说不需要nginx作为http服务器了,当然swoole并不是为了取代nginx,实际上swoole当前实现的http server功能有限,比如说只支持Get和Post,所有往往swoole前面还要运行一个nginx来作为前端代理服务器。

其次,swoole是内存常驻的。和php-fpm的常驻服务不同,php-fpm中常驻的是php的解释器,这个解释器会重复加载php代码,初始化环境,而swoole只在启动的时候加载,这样一来,性能就自然而然的提高了。这一点可以在开发中很明显的体现出来,php-fpm下,修改的php代码会即时生效,而使用swoole则需要重启swoole的server才能使代码生效。

通过上面的一些说明,就可以很明显的得出swoole和传统php开发的优缺点了。

swoole server优点:
– swoole性能更高
– 可以做为tcp,udp服务器
– 在高io高并发的服务器要求下,swoole的运行模式是完全可以胜任的

swoole server缺点:
– 更难上手。这要求开发人员对于多进程的运行模式有更清晰的认识
– 更容易内存泄露。在处理全局变量,静态变量的时候一定要小心,这种不会被GC清理的变量会存在整个生命周期中,如果没有正确的处理,很容易消耗完所有的内存。而以往的php-fpm下,php代码执行完内存就会被完全释放。
– 无法做密集计算。当然这一点是php甚至是所有动态语言都存在的问题。写在这里是因为防止误导读者以为使用swoole后,php可以用来做密集计算。

LINUX下修改php.ini配置报错输出

首先是找到php.ini文件

输入

find / -name php.ini

总共有两个结果

/etc/php5/cli/php.ini
/etc/php5/apache2/php.ini

cli/php.ini指的是在控制台环境下运行php脚本使用的配置文件

apache2/php.ini是apache2环境下运行php脚本使用的配置文件

如果你还不确定是用哪一个,可以新建一个php代码

使用 echo phpinfo(); 来输出php信息,其中有一项是加载的php.ini路径。我的就是apache2/php.ini

然后编辑apache2/php.ini,我这里是要开启他的报错,不然如果代码中有错,浏览器访问就会直接报500错误

找到display_errors这一行,去除前面的;

display_errors=On

配置错误级别

error_reporting=E_ALL & ~E_NOTICE

修改完成后要使php.ini配置生效,网上普遍的说法是重启apache服务生效

service apache2 restart

代码出错的地方仍然报500错误

准备去看apache的日志文件,同样使用find / -name error.log找到日志,由于日志太多,就把之前的error.log删掉,重启apache,浏览器打开出错页面,然后查看error.log。发现日志中是记录了代码出错的信息的。确定就是没有开启报错,导致500错误。

修改apache2.conf,在/etc/apache2下面。

添加

php_flag display_errors on
php_value error_reporting 2039

重启apache
service apache2 restart

浏览器打开错误页面,成功报错!

linux下一些好用的指令记录

查找当前目录下包含指定字符串的文件

grep -rn “hello,world!” *

  • : 表示当前目录所有文件,也可以是某个文件名
    -r 是递归查找
    -n 是显示行号
    -R 查找所有文件包含子目录
    -i 忽略大小写

mysql数据库相关操作
创建用户
mysql> insert into mysql.user(Host,User,Password) values(“localhost”,”phplamp”,password(“1234”));
授权

grant all privileges on phplampDB.* to phplamp@localhost identified by ‘1234’;
删除用户
mysql>DELETE FROM user WHERE User=”phplamp” and Host=”localhost”;
mysql>flush privileges;
修改密码
mysql>update mysql.user set password=password(‘新密码’) where User=”phplamp” and Host=”localhost”;
mysql>flush privileges;
刷新系统权限表
mysql>flush privileges;

cinnamon桌面图标重复
gsettings set org.gnome.desktop.background show-desktop-icons false

ubuntu下linux创建桌面启动器以及菜单栏点击失效的解决方法

创建桌面启动器

进入/usr/share/applications

vi eclipse.desktop

输入一下内容

[Desktop Entry]
Encoding=UTF-8
Name=eclipse
Comment=Eclipse IDE
Exec=/home/jiang/eclipse/eclipse
Icon=/home/jiang/eclipse/icon.xpm
Terminal=false
StartupNotify=true
Type=Application
Categories=Application;Development;
Exec=env UBUNTU_MENUPROXY=0 /home/jiang/eclipse/eclipse

保存后,将这个文件复制到桌面文件夹
cp /usr/share/applications/eclipse.desktop /home/jiang/桌面

对于eclipse打不开菜单的情况,可以修改eclipase文件夹下的eclipse.ini为

-startup
plugins/org.eclipse.equinox.launcher_1.3.100.v20150511-1540.jar
–launcher.GTK_version
2
–launcher.library
plugins/org.eclipse.equinox.launcher.gtk.linux.x86_64_1.1.300.v20150602-1417
-product
org.eclipse.epp.package.jee.product
–launcher.defaultAction
openFile
-showsplash
org.eclipse.platform
–launcher.XXMaxPermSize
256m
–launcher.defaultAction
openFile
–launcher.appendVmargs
-vmargs
-Dosgi.requiredJavaVersion=1.7
-XX:MaxPermSize=256m
-Xms256m
-Xmx2048m

————————————

–launcher.GTK_version
2

这一部分是新增的部分

linux下编译安装mysql-connector-cpp

首先在github上下载mysql-connector-cpp

git clone https://github.com/mysql/mysql-connector-cpp.git

git上面有两个分支,2.0版本是支持mysql作为文档存储的接口,1.1版本是支持正常的关系数据库存储接口

首先我们编译安装2.0版本,进入代码目录执行

cmake .

cmake –build . –config CCC

cmake –build . –target install –config CCC

到这里已经安装完毕.

然后到testapp下编译测试样例

cmake .

提示出错,WITH-CONCPP没有设置,我试了编辑/etc/profile文件,设置这个变量为/usr/local/mysql/connector-c++-2.0/,但是仍然提示没有设置,于是我将CMakeLists中的

set(WITH_CONCPP $ENV{WITH_CONCPP} CACHE PATH “MySQL Connector/C++ 2.0 install location”)

改为

set(WITH_CONCPP “/usr/local/mysql/connector-c++-2.0/” CACHE PATH “MySQL Connector/C++ 2.0 install location”)

继续cmake .

提示出错,在/usr/local/mysql/connector-c++-2.0/lib64下找不到依赖库

那么将目录下生成的 libmysqlcppconn2.so libmysqlcppconn2.so.1 libmysqlcppconn2.so.1.0 拷贝到/usr/local/mysql/connector-c++-2.0/lib64中,没有的创建一个即可。

cmake .

make

在run文件夹下面生成了两个可执行文件

devapi_test xapi_test

这两个文件的区别在于,xapi_test实现了c的接口,是通过mysql的一个叫做x plugin的插件连接mysql的,而devapi则是使用了c++的接口。所以如果使用xapi接口的话,mysql安装时必须安装x plugin

接下来编译安装1.1版本,先将当前的改动都commit了,然后

git checkout 1.1,

cmake .

如果出错。

报缺少boost库错误,可以

sudo apt-get install libboost-all-dev

报缺少mysql.h等错误,可以通过sudo apt-get install libmysql++-dev

然后make

sudo make install

然后将生成的动态库拷贝的/usr/local/lib下

sudo cp libmysqlcppconn.so* /usr/local/lib/

linux下使用crontab进行网站内容和数据库定期备份

crontab有几个常用的命令

crontab -l 列举crontab的任务

crontab -e 编辑crontab的任务

crontab -r 删除crontab的任务

crontab -h crontab的帮助

crontab -i 删除crontab前进行提示

crontab -e进行编辑任务时会使用nano进行编辑,编辑命令如下示例。

分 时 每月的第几天 每周的周几 命令

前5个参数可以使用通配符 *

m h dom mon dow command

设置每两分钟扫描一次,清理过期记录

*/2 * * * * /usr/bin/php /var/www/poker/scan.php >> /$

设置每周二23:29分备份一次

59 23 * * 2 /bin/bash /home/back.sh
59 23 * * 2 /bin/bash /home/back.sh就是我用来定期备份的指令,主要就是配合一个shell脚本

#!/bin/bash
cd /home
mv backup/* oldbackup/
echo “old backup file has been moved to oldbackup folder”;
cd /home/backup
Now=date "+%Y-%m-%d-%H-%M-%S"
File_emlog=”backup-emlog-“{Now}”.sql”
mysqldump -u me -ppassword emlog >
File_emlog
File_java=backup-java-Now.sql
mysqldump -u me -ppassword java >
File_java
File_plan=backup-plan-Now.sql
mysqldump -u me -ppassword plan >
File_plan
File_sms=backup-sms-Now.sql
mysqldump -u me -ppassword sms >
File_sms
File_tu=backup-tu-Now.sql
mysqldump -u me -ppassword tu >
File_tu
File_wordpress=backup-wordpress-Now.sql
mysqldump -u me -ppassword wordpress >
File_wordpress

echo “your databases backup successfully completed”;
#数据文件到这里备份完毕

#下面备份网站的文件
tar -czf /home/backup/back_www.tar.gz /var/www

linux下c/c++的内存泄漏分析

使用valgrind进行内存分析

简介

官网地址:http://valgrind.org
主要提供以下工具:

Memcheck 是一个内存错误的检测工具,帮助你的程序,尤其是c/c++程序出现更少内存的问题。
Cachegrind 是一个缓存和分支预测探查工具,帮助你的程序运行的更快。
Callgrind 也是一个和缓存相关的调用图工具,和Cachegrind有一部分重叠,但也生成一些Cachegrind不提供的信息。
Helgrind 是一个线程错误的检测工具,在多线程场景下能派上用场。
DRD 也是一个线程错误的检测工具,与Helgrind功能一样,但是使用了不同的分析技术,可能会发现不同的问题。
Massif 是一个堆分析工具,帮助程序使用更少的内存。
DHAT 是不同与Massif的堆分析工具,帮助你理解块的生命周期, 块的利用率, 以及layout的低效。
SGcheck 是一个实验性的工具,帮助检测栈的超支和全局数组。是Memcheck的功能方面的补充:可以检测出Memcheck无法发现的问题,反之亦然。
BBV 是一个实验SimPoint基本块向量生成器。对于进行计算机体系结构研究和开发的人来说,这是有用的。

这里记录的只是Memcheck的使用,其他的使用可以参考上述的官网的网址。

ubuntu下的安装

sudo apt-get install valgrind

使用方法

valgrind [valgrind-options] your-prog [your-prog-options]

例如,对ls -l进行分析:

valgrind –tool=memcheck ls -l

valgrind的默认工具就是memcheck,所以使用memcheck工具时可以省略–tool参数

注意事项:

1、valgrind工具会减慢程序的运行速度
2、程序编译时需要开启-g,帮助valgrind可以更精准的定位到错误
3、程序编译时最好关闭优化,否则可能产生不正确的未初始化错误信息,以及遗漏未初始化错误。
4、程序编译时最好使用-Wall,帮助valgrind在高优化等级的程序中精准识别一些甚至是全部的问题

具体使用说明

Valgrind会记录下一些注释,文本流,具体的错误报告以及其他的重要的事件。类似与以下的格式:

==12345== some-message-from-Valgrind

12345代表进程Id,这个格式方便区分程序输出和Valgrind的注释输出,以及区分多个进程的输出。Valgrind只会输出最重要的信息,如果需要一些次要的信息,可以使用-v参数。

你可以使用三种方式去导出这些错误

1、默认情况:会直接在控制台打印出来
2、使用文件记录,这个时候需要使用参数–log-file=filename,filename代表存储的文件名
3、通过socket发送:使用参数–log-socket=192.168.0.1:12345,不加端口号会使用默认的1500端口,Valgrind提供了一个叫Valgrind-listener的工具去监听这个网络流。

读懂memcheck工具产生的错误信息

1.非法读/非法写的错误(Illegal read/Illegal write errors)

例如:

Invalid read of size 4
at 0x40F6BBCC: (within /usr/lib/libpng.so.2.1.0.9)
by 0x40F6B804: (within /usr/lib/libpng.so.2.1.0.9)
by 0x40B07FF4: read_png_image(QImageIO *) (kernel/qpngio.cpp:326)
by 0x40AC751B: QImageIO::read() (kernel/qimage.cpp:3621)
Address 0xBFFFF0E0 is not stack’d, malloc’d or free’d

出现这个错误是因为程序读或写了Valgrind认为不应该读写的内存区域

2.使用了为初始化的值

例如:

Conditional jump or move depends on uninitialised value(s)
at 0x402DFA94: _IO_vfprintf (_itoa.h:49)
by 0x402E8476: _IO_printf (printf.c:36)
by 0x8048472: main (tests/manuel1.c:8)
这样一段错误可能就是由以下的代码产生

int main()
{
int x;
printf ("x = %d\n", x);
}

Valgrind会跟踪变量x,直到x被使用时才会报错。在这里x被传入了printf,进而进入_IO_printf,但是这些都不会报错,只有当x被传递到_IO_vfprintf,_IO_vfprintf开始检查x是否可以被转换为ASCII码时才报错。

未初始化值一般有两种情况:

  • 1、局部变量没有被初始化,就像上面一样。
  • 2、The contents of heap blocks (allocated with malloc, new, or a similar function) before you (or a constructor) write something there.

为了找到未初始化变量一开始的位置,可以使用–track-origins=yes参数。当然这会减慢Valgrind的使用速度。

3.在系统调用中使用了未初始化或者不可寻址的值

Valgrind会检查所有系统调用的参数,一般有以下3类:

  • 1、检查所有直接调用的参数,即使已经初始化了。
  • 2、如果系统调用需要你的程序申请的缓冲区,Valgrind会检查所有的缓冲区内容,看它是否可寻址,内容是否初始化了。
  • 3、如果系统调用需要写入用户提供的缓冲,Valgrind会检查是否可寻址。

下面是两个使用了无效参数的系统调用的例子:

#include
#include
int main( void )
{
char* arr = malloc(10);
int* arr2 = malloc(sizeof(int));
write( 1 /* stdout */, arr, 10 );
exit(arr2[0]);
}

得到这样的错误信息:

Syscall param write(buf) points to uninitialised byte(s)
at 0x25A48723: __write_nocancel (in /lib/tls/libc-2.3.3.so)
by 0x259AFAD3: __libc_start_main (in /lib/tls/libc-2.3.3.so)
by 0x8048348: (within /auto/homes/njn25/grind/head4/a.out)
Address 0x25AB8028 is 0 bytes inside a block of size 10 alloc’d
at 0x259852B0: malloc (vg_replace_malloc.c:130)
by 0x80483F1: main (a.c:5)

Syscall param exit(error_code) contains uninitialised byte(s)
at 0x25A21B44: __GI__exit (in /lib/tls/libc-2.3.3.so)
by 0x8048426: main (a.c:8)

write(a)和exit(b)都是错误的,a从堆中向标准输出中写入了未初始化的arr。b向exit传递了为初始化的值。注意a的错误在于arr指向的内存区域,而b的错误直接是arr2[0]。

4.非法的释放(Illegal frees)

例如:

Invalid free()
at 0x4004FFDF: free (vg_clientmalloc.c:577)
by 0x80484C7: main (tests/doublefree.c:10)
Address 0x3807F7B4 is 0 bytes inside a block of size 177 free’d
at 0x4004FFDF: free (vg_clientmalloc.c:577)
by 0x80484C7: main (tests/doublefree.c:10)

这个例子中,一块区域被free了两次,所以出现Illegal frees的错误。

5.使用不合适的释放函数去释放堆区域的内存

例如:

Mismatched free() / delete / delete []
at 0x40043249: free (vg_clientfuncs.c:171)
by 0x4102BB4E: QGArray::~QGArray(void) (tools/qgarray.cpp:149)
by 0x4C261C41: PptDoc::~PptDoc(void) (include/qmemarray.h:60)
by 0x4C261F0E: PptXml::~PptXml(void) (pptxml.cc:44)
Address 0x4BB292A8 is 0 bytes inside a block of size 64 alloc’d
at 0x4004318C: operator new[](unsigned int) (vg_clientfuncs.c:152)
by 0x4C21BC15: KLaola::readSBStream(int) const (klaola.cc:314)
by 0x4C21C155: KLaola::stream(KLaola::OLENode const *) (klaola.cc:416)
by 0x4C21788F: OLEFilter::convert(QCString const &) (olefilter.cc:272)

这个错误是因为使用new[]开辟内存空间,却使用了free去释放内存。
使用malloc, calloc, realloc, valloc or memalign,必须使用free释放内存。
使用new, 必须使用delete释放内存。
使用new[],必须使用delete[]释放内存。

6.源内存区域和目标内存区域重叠

memcpy, strcpy, strncpy, strcat, strncat这些函数可以从源内存区域复制内容到目标内存区域。这两块区域是不可以重叠的。POSIX标准规定这种行为是未定义的。
例如

==27492== Source and destination overlap in memcpy(0xbffff294, 0xbffff280, 21)
==27492== at 0x40026CDC: memcpy (mc_replace_strmem.c:71)
==27492== by 0x804865A: main (overlap.c:40)

7.Fishy argument values

所以的内存分配函数都指定了分配的内存大小,这个大小必定为正数,或者一般情况下不会极度的大。例如在64位的机器上,不会申请分配2^63大小的内存。这种为负数的或者过于大的参数被成为Fishy argument。
例如:

==32233== Argument ‘size’ of function malloc has a fishy (possibly negative) value: -3
==32233== at 0x4C2CFA7: malloc (vg_replace_malloc.c:298)
==32233== by 0x400555: foo (fishy.c:15)
==32233== by 0x400583: main (fishy.c:23)

8.内存泄漏检测

Valgrind会跟踪所有由malloc或new申请的内存,所以当程序退出时,Valgrind知道哪些内存没有被主动释放。
如果–leak-check参数设置得当,对于每一个未被释放的内存块,Valgrind判断从root-set中的指针是否能到达这些内存块。root-set包含(a)普通的所有线程使用的寄存器,(b)初始化的, 对齐的, 指针大小的数据块,包括栈。一个数据块有两种方式可到达,第一种是“start-pointer”,即指针在数据块的开头;第二种是“interior-pointer”,即指针在数据块的中间。“interior-pointer”有多种方式出现:

  • 指针一开始是“start-pointer”,被程序有意或无意的移动到中间。

  • 可能只是巧合。

  • std::string中的char的指针。

  • 有些代码分配块内存,使用前8个去存储作为64位的数。例如sqlite3MemMalloc就是这样做的。

  • 可能是一个c++对象(具有析构函数)数组的指针,由new[]来分配内存。这种情况下,有些编译器存储一个“magic cookie”,包含数组长度存储在分配的块开头。

  • 可能是一个多重继承产生的c++对象的内部部分的指针。

在使用了启发式(heuristics)的情况下,stdstring, length64, newarray and multipleinheritance情况下的“interior-pointer”会被当成“start-pointer”对待。

考虑下面这九种情况:

Pointer chain AAA Leak Case BBB Leak Case


(1) RRR ————> BBB DR
(2) RRR —> AAA —> BBB DR IR
(3) RRR BBB DL
(4) RRR AAA —> BBB DL IL
(5) RRR ——?—–> BBB (y)DR, (n)DL
(6) RRR —> AAA -?-> BBB DR (y)IR, (n)DL
(7) RRR -?-> AAA —> BBB (y)DR, (n)DL (y)IR, (n)IL
(8) RRR -?-> AAA -?-> BBB (y)DR, (n)DL (y,y)IR, (n,y)IL, (_,n)DL
(9) RRR AAA -?-> BBB DL (y)IL, (n)DL

Pointer chain legend:
– RRR: a root set node or DR block(一个root set或者直接可达的块)
– AAA, BBB: heap blocks(堆块)
– —>: a start-pointer (头指针)
– -?->: an interior-pointer (内部指针)

Leak Case legend:
– DR: Directly reachable (直接可达)
– IR: Indirectly reachable (不直接可达)
– DL: Directly lost (直接丢失)
– IL: Indirectly lost (不直接丢失)
– (y)XY: it’s XY if the interior-pointer is a real pointer (内部指针是一个真实的指针)
– (n)XY: it’s XY if the interior-pointer is not a real pointer (内部指针不是一个真实的指针)
– (_)XY: it’s XY in either case (任意一个情况)

任意一种情况都可以被归为上述9种情况之一,Valgrind合并其中一些情况,得出4种可能

  • “Still reachable(依然可达)”。 这包含情况 1 和 2 (for the BBB blocks) 。 一个内存块的头指针的或者头指针的链被发现,程序员至少在原理上释放了这块内存在程序退出之前。这是一个非常普遍并且不算是一个问题,Valgrind默认不报告这个问题。

  • “Definitely lost(绝对丢失)”。 这包含情况3 (for the BBB blocks) 。这意味着这个数据块没有指针可达。数据块被归为丢失,因为程序员在程序结束时不能主动释放它,原因是没有指针指向这块内存。 这可能是在较早之前丢失了指向内存区域的指针。

  • “Indirectly lost(非直接丢失)”。这包含情况4和9 (for the BBB blocks)。这意味着数据块丢失不是因为没有指针指向它,而是因为所有的指向数据块的指针自己丢失了。 举例来说,如果你有一个二叉树,根节点丢失,所有的他的子节点都变成非直接丢失。因为根节点的直接丢失问题被解决,子节点的非直接丢失问就会消失。Valgrind默认不报告这个问题。

  • “Possibly lost(可能丢失)”。 这包含情况5、6、7、8 (for the BBB blocks) 。 这意味着一个或多个数据块指针被发现,但是至少一个指针是内部指针。这可能只是一个内存中的随机值,刚好指向一个数据块,所以你不需要考虑这个情况除非你知道你的代码中出现了内部指针。

下面是一个内存泄漏的总结的例子

LEAK SUMMARY:
definitely lost: 48 bytes in 3 blocks.
indirectly lost: 32 bytes in 2 blocks.
possibly lost: 96 bytes in 6 blocks.
still reachable: 64 bytes in 4 blocks.
suppressed: 0 bytes in 0 blocks.

如果开启的启发式的选项,类似于以下输出

LEAK SUMMARY:
definitely lost: 4 bytes in 1 blocks
indirectly lost: 0 bytes in 0 blocks
possibly lost: 0 bytes in 0 blocks
still reachable: 95 bytes in 6 blocks
of which reachable via heuristic:
stdstring : 56 bytes in 2 blocks
length64 : 16 bytes in 1 blocks
newarray : 7 bytes in 1 blocks
multipleinheritance: 8 bytes in 1 blocks
suppressed: 0 bytes in 0 blocks

如果 –leak-check=full 被指定, Memcheck 会给出每一个绝对丢失或可能丢失块的详细情况,包括他们在哪里被分配。它不能告诉你何时、如何、为何指向一个泄露内存块的指针丢失了;这个需要自己解决。通常,你需要保证在程序退出时,你的程序没有任何的绝对丢失或者可能丢失的内存块。

例如

8 bytes in 1 blocks are definitely lost in loss record 1 of 14
at 0x……..: malloc (vg_replace_malloc.c:…)
by 0x……..: mk (leak-tree.c:11)
by 0x……..: main (leak-tree.c:39)

88 (8 direct, 80 indirect) bytes in 1 blocks are definitely lost in loss record 13 of 14
at 0x……..: malloc (vg_replace_malloc.c:…)
by 0x……..: mk (leak-tree.c:11)
by 0x……..: main (leak-tree.c:25)

第一条信息描述了一种简单的情况,一个8byte的内存块绝对丢失了。第二种情况描述了另外一个8byte内存块绝对丢失;不同在于第二种情况会引起在另外内存块中的更多的80bytes内存非直接丢失了。loss number没有任何特殊的含义。这个loss number可以在Valgrind gdbserver中用来列出泄漏内存块的地址,或者给出更多的信息关于为何一个内存块仍然可达。

当 –leak-check=full 被指定时,选项–show-leak-kinds= 控制显示的泄漏类型。

有下面几种类型:

  • 单独指定一或多个: definite indirect possible reachable。
  • all代表所有。
  • none 代表空集合。

例如使用 –show-leak-kinds=definite,possible 来只显示绝对或者可能的内存丢失。

注意事项

在调试php时,因为php自己实现了内存管理机制,所有使用valgrind时,会检测出很多php上的内存泄露,我们可以通过export USE_ZEND_ALLOC=0来让php直接向内存申请内存,这样有助于发现问题

unable to make backup link of `./usr/bin/chattr’ before installing new version: Operation not permitted

在公司服务器上使用apt-get upgrade遇到这个问题。通过查阅资料,发现问题的关键在于,chattr需要升级但是chatter无法被删除。

使用:

lsattr /usr/bin/chattr

发现chattr的属性包括i和a,i代表immutable,不可更改,a代表append only,只能增加。这样问题就清楚了,chattr不可更改导致无法升级,至于出现这个情况的原因也不清楚。于是使用chattr更改自己的i和a属性

chattr -i /usr/bin/chattr
chattr -a /usr/bin/chattr

没有任何效果,而且提示我chattr的用法。出现这种提示的原因往往都是用错了指令,我反复确认指令都没有错。

于是在本地机器上验证chattr的属性,发现是没有i和a属性的,而且上述指令也可以正常工作。

实在没有办法,使用sftp将本地的chattr传到服务器上,命名为chattr_new,再用传上去的chattr_new更改chattr的属性

chattr_new -i /usr/bin/chattr
chattr_new -a /usr/bin/chattr

然后在执行apt-get upgrade,没有任何报错。

注:
chattr是用来防止误删操作的,即使是root用户,在chattr为文件添加了i属性后,root用户也无法删除。
lsattr则是用来查看文件的这方面的属性的。

Ubuntu下Docker安装遇到的问题记录

Ubuntu下安装docker的几点问题记录

1.要注意系统的内核和版本是否支持,内核最低要求为3.10,版本最低为12.04,这两点必须同时满足。
使用uname -a查看系统内核
Linux jiang-PC 4.4.0-72-generic #93-Ubuntu SMP Fri Mar 31 14:07:41 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
这里4.4.0-72则是内核的版本号
使用cat /etc/issue查看系统版本号
Ubuntu 16.04 LTS \n \l

2.注意查看docker的日志记录
使用service docker start(或者systemctl docker start)启动docker时,没有任何输出提示,但是往后执行可能就发现docker没有正常启动,但是又不知道问题出在哪里。/var/log/upstart/docker.log记录了docker启动日志。

3.AppArmor的问题
AppArmor enabled on system but the docker-default profile could not be loaded。出现这个问题时,使用
apt-get install apparmor即可解决

附一段webserver的Dockerfile:

# 这是服务器环境的Docker,会安装好php,nginx等环境,并且进行配置
# author:jiangpengfei
# date: 2017-04-19

FROM ubuntu:16.04
RUN apt-get update \
    && DEBIAN_FRONTEND=noninteractive apt-get install -y software-properties-common \
    && DEBIAN_FRONTEND=noninteractive apt-add-repository -y ppa:nginx/stable \
    && DEBIAN_FRONTEND=noninteractive apt-get update \
    && DEBIAN_FRONTEND=noninteractive apt-get install -y nginx
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y php7.0
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y php-redis
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y php-mysql
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y php-imagick \
    && apt-get autoremove \
    && apt-get autoclean
# 上面是从ubuntu的源中安装必要组件,下面开始进行配置
RUN mkdir /var/www/family \
    && mkdir -p /var/log/nginx/access/ \
    && touch /var/log/nginx/access/default.log

COPY family /etc/nginx/sites-enabled 
COPY start.sh /usr/local/bin
EXPOSE 9090 81
CMD ["start.sh"]