• Linux常用命令行指令 - lsof

    lsof是list open files的简称,用于列出系统中所有打开的文件。由于在linux系统下,所有 对象都是文件,所以这个指令十分有用。

    下面就给出了几种常见的用法,需要注意的是有些指令需要root权限执行。

    1. 查看打开指定文件的进程

    lsof /var/syslog.log
    

    2. 查看指定目录下打开的文件

    lsof +D /home/
    

    3. 查看以指定进程名所有打开的文件

    使用 -c 命令能够查看指定进程名使用的文件,允许在一个命令行指令中使用多个 -c 参数。

    lsof -c procname
    

    4. 查看使用指定挂载点的进程

    在停止挂载某个目录时,系统会告诉我们“设备资源繁忙”,所以我们需要找出使用挂载点的程序,终止这些进程,解除目录的占用状态。

    lsof /home
    


    lsof +D /home/
    

    5. 查看指定用户打开的文件

    lsof -u username
    

    另外还可以进行反向过滤,在输出中不显示指定用户的文件,语法如下。

    lsof -u ^username
    

    6. 查看指定进程打开的文件

    lsof -p pid
    

    7. 终止属于某个用户的所有进程

    kill `lsof -t -u username`
    

    相似的,可以使用 -t 参数打印使用某个文件的进程id。

    lsof -t /var/log
    

    8. 让结果同时满足多个选项

    默认情况下,多个参数是以或(or)进行操作的,可以使用 -a 选项让结果同时满足多个参数。

    lsof -a -u username -c procname
    

    9. 让命令重复执行

    可以使用 -r 参数让命令重复执行。以下命令会每隔5s执行一次,每次的执行结果会用 “====” 分割开。

    lsof -u username -c procname -a -r5
    

    10. 列出所有的网络连接

    lsof -i
    

    让结果中的ip地址和端口号显示数字而不是英文别名

    lsof -i -Pn
    

    显示所有的 AF_UNIX socket

    lsof -U
    

    11. 列出指定进程使用的网络连接

    lsof -i -a -p pid
    

    12. 查看指定端口上的监听

    lsof -i :portid
    

    13. 列出所有tcp和udp连接

    lsof -i tcp; lsof -i udp
    

    14. 列出所有的网络文件系统

    网络文件系统(network file system)

    lsof -N -u username
    
  • 重载、覆盖和隐藏的区别

    重载(overload) :就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。
    多个重载函数在调用的时候根据函数的参数来区别不同的函数。
    关键点:函数名相同,参数表不同

    覆盖(override) :是指在派生类中重新对基类中的虚函数重新实现,即函数名和参数都一样,只是函数的实现体不一样。
    关键词:派生类中,虚函数,函数名和参数表完全相同

    隐藏(hide) :派生类中的函数把基类中相同名字的函数屏蔽掉了。
    隐藏一词可以这么理解:在调用一个类的成员函数的时候,编译器会沿着类的继承链逐级的向上查找函数的定义,如果找到了那么就停止查找了,所以如果一个派生类和一个基类都有同一个同名(暂且不论参数是否相同)的函数,而编译器最终选择了在派生类中的函数,那么我们就说这个派生类的成员函数"隐藏"了基类的成员函数,也就是说它阻止了编译器继续向上查找函数的定义.

    关于这三种情况的示例代码如下

    #include <iostream.h>
    class Base
    {
    public:
      virtual void f(float x)
      {
        cout << "Base::f(float) " << x << endl;
      }
    
      void g(float x)
      {
        cout << "Base::g(float) " << x << endl;
      }
    
      void h(float x)
      {
        cout << "Base::h(float) " << x << endl;
      }
    };
    
    class Derived : public Base
    {
    public:
      virtual void f(float x)
      {
        cout << "Derived::f(float) " << x << endl;
      }
    
      void g(int x)
      {
        cout << "Derived::g(int) " << x << endl;
      }
    
      void h(float x)
      {
        cout << "Derived::h(float) " << x << endl;
      }
    };
    

    从以上代码能够看出:
    (1)函数Derived::f(float)覆盖了Base::f(float)。
    (2)函数Derived::g(int)隐藏了Base::g(float),而不是重载。
    (3)函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。

    考虑代码在运行时的结果:

    void main(void)
    {
      Derived  d;
      Base *pb = &d;
      Derived *pd = &d;
    
      // Good : behavior depends solely on type of the object
      pb->f(3.14f); // Derived::f(float) 3.14
      pd->f(3.14f); // Derived::f(float) 3.14
    
      // Bad : behavior depends on type of the pointer
      pb->g(3.14f); // Base::g(float) 3.14
      pd->g(3.14f); // Derived::g(int) 3        (surprise!)
    
      // Bad : behavior depends on type of the pointer
      pb->h(3.14f); // Base::h(float) 3.14      (surprise!)
      pd->h(3.14f); // Derived::h(float) 3.14
    }
    

    在第一种调用中,函数的行为取决于指针所指向的对象。在第二第三种调用中,函数的行为取决于指针的类型。所以说,隐藏破坏了面向对象编程中多态这一特性,会使得开发人员产生混乱。

    函数接口设计注意要点
    在设计基类的函数接口时,不要出现既是虚函数,又对该虚函数进行了重载的情况。这样会对派生类的多态函数实现造成不便。
    示例代码如下

    class Base
    {
      virtual foo();
      virtual foo(int n);
    }
    
    class Derived : public Base
    {
      virtual foo();
    }
    

    从以上代码能够看出:

    1. Derived::foo()Base::foo() 构成了多态关系
    2. Derived::foo() 隐藏了 Base::foo(int)

    所以,如果派生类希望实现 foo() 函数的多态,也就必须实现 foo(int) 的多态,否则就会出现隐藏。
    如果在现有代码中已经发生了上述 Base 的情况,在不改变原有代码的情况下,可以使用在派生类中使用这样的语法 using Base::foo; ,这会将基类中所有的名为 foo 的函数引入到派生类中来。

  • 关于Qt中使用中文编码的一些问题

    Qt库作为一个开源库,并且支持多语言。在开发时需要考虑字符编码问题。
    我现在的使用Qt开发环境为VS2012+Qt5.1.1,所以一下所讨论的也是基于这个版本而言的。目前Qt5已经将 tr() 删掉了。

    在windows下使用Qt库进行开发
    有两种主流开发环境:

    1. 使用Qt Creator作为开发环境
    2. 使用Visual Studio + Visual Assist + Qt Visual Studio Addon作为开发环境

    目前我使用的就是第二种开发环境,鉴于在Windows平台下没有哪个开发环境能与Visual Studio相媲美。

    使用VS开发Qt程序时需要注意源文件的编码格式
    Qt5官方推荐的源文件编码格式为UTF-8,QString内部的编码格式就是UTF-8,使用QtCreator创建的源文件的编码格式也是UTF-8 without BOM。
    但是VS在中文操作系统上,如果不进行特殊的设置,默认创建的含有中文的源文件编码格式为gb3212。这样的原始字符串就是gb2312格式的,在使用时需要进行特殊转换,这样显示在界面上才不会乱码。
    推荐使用一下两种方法解决gb2312编码问题:

    • QString::fromLocale8Bit() 会将gb2312编码的字符串转换为UTF-8格式以便存放在QString中。
    • QStringLiteral() 是一个宏定义,会在编译期将字符串实例化,对静态字符串使用这个字符串也是一个提高效率的方法(因为减少了运行时的内存申请开销)。

    在VS中使用utf-8 with BOM的源文件格式
    如果使用了utf-8 with BOM的源文件格式,VS会将其中的字符串转换为gb2312编码的中文,这样是为了兼容旧版本的编译器。
    可以使用编译选项让VS编译器不进行这项转换,这样就可以直接使用字符串 char* 初始化QString了。

    • 微软在VS2010中提供了 #pragma execution_character_set("utf-8") 这样的编译选项,能够防止编译器进行文件编码转换,保证字符串保留utf-8格式。
    • VS2012 并不支持以上编译选项,VS2012将这个特性取消了
    • VS2015 可以使用 /utf-8 让编译器能够识别 utf-8 without BOM格式的文件,并保留utf-8的字符串编码。

    C++11的编码格式支持
    对于支持C++11标准的编译器,可以采用如下的写法产生utf-8格式的字符串。

    const char* szMsg = u8"字符串";
    
  • 为git设置socks5代理

    在某些时候,为了绕过网络封锁,在使用git时需要借助代理完成相关网络操作。

    一个可用的VPN代理是先决条件

    关于VPN的架设就不在此讨论了。

    将repo的链接由ssh改为为https

    已经使用https的repo跳过此步骤。
    本教程需要通过https(http)的方式使用代理,所以如果现有的repo是使用ssh方式clone到本地的,需要修改其为https方式。
    需要修改 repo_name/.git/config 中的url配置,将其中的git格式改为https格式。使用文本编辑工具将其打开进行修改,以github为例,需要将 [email protected]:user_name/repo_name.git 改为 https://github.com/user_name/repo_name.git
    可以使用sed指令完成替换。

    sed -i 's/[email protected]:/git:\/\/github.com\//' ./.git/config
    

    设置git使用代理

    将git配置为使用socks5代理,以socks5的ip和端口是 127.0.0.1:1080 为例。

    git config --global http.proxy 'socks5://127.0.0.1:1080'
    git config --global https.proxy 'socks5://127.0.0.1:1080'
    

    将登录信息缓存下来

    使用https时,在向远程仓库进行推送(git push)时会要求验证用户名和密码,由于每次都输入验证信息是十分繁琐的,考虑将验证信息缓存下来。

    git config --global credential.helper cache
    

    git默认会将密码缓存15分钟,在这期间进行的操作是不需要再次进行密码验证的,可以使用下面命令修改缓存的时长。

    git config --global credential.helper "cache --timeout=3600"
    

    将登陆信息保存下来

    也可以考虑将用户名和密码永久保存下来,这样就不需要再次进行密码验证了。
    注意: 使用这种配置会将明文的账户和密码保存在 ~/.git-cretidentials 文件中,所以仅推荐在个人电脑中使用这个配置。

    git config --global credential.helper store
    

    参考资料

    (全文完)

  • windows平台下的C++开发框架

    在开发windows应用程序时,经常会遇到win32、mfc、atl的函数。下面就对三种API(或者说是开发框架)进行了总结。

    Win32开发框架

    win32开发框架是最基础的函数接口,更接近操作系统底层。
    使用Win32接口的优点是程序的依赖最小,生成的可执行文件的体积也最小。

    MFC开发框架

    MFC是Microsoft Foundation Class的缩写,将win32的相关接口按照OOP的思想封装了起来,目的是提供一个快速开发原生应用的框架。
    使用MFC开发的程序体型略显臃肿,而且MFC也比较陈旧了,并不推荐使用MFC进行开发。

    ATL开发框架

    ATL是Active Template Library的缩写,是针对COM组件的开发实现的一套开发框架。ATL简化了ActiveX COM组件的开发。

  • 使用emacs在目录中递归搜索字符串

    对文件中的字符串搜索是开发中经常遇到的情景,下面就对常见的搜索字符串的方式进行了总结。

    对当前buffer进行搜索(单个文件)
    C-s 向后搜索
    C-r 向后搜索
    M-s o 使用occur对当前buffer中的指定关键字进行统计
    在安装了helm以后可以使用 helm-occur 完成当前buffer的搜索

    对目录中的指定文件进行搜索(一个目录下的多个文件)
    C-x d 进入dired模式,在dired模式中使用 M-x <dired-do-find-regexp> (快捷键 A )对标记的文件进行搜索。

    对目录中的所有文件进行递归搜索(一个目录下的所有文件和所有子目录中的文件)
    M-x <rgrep> 能够对一个目录下所有的文件进行正则表达式匹配

    注意事项
    在windows平台上使用grep和find指令时,需要借助仿linux环境中的程序完成,如cygwin中的程序。
    需要将程序的路径添加到系统环境变量path中,并且为了覆盖windows系统自带的find命令,需要将该路径放在系统find路径之前。
    tips: 对于安装了 git bash 的同学,直接将其中的 mingw64\bin 的绝对路径加入系统path中即可。