存档在 2013年3月

shell一些不为人知的技巧

2013年3月17日

在阅读以下部分前,强烈建议读者打开一个shell实验,这些都不是shell教科书里的大路货哦:)

  • !$
    !$是一个特殊的环境变量,它代表了上一个命令的最后一个字符串。如:你可能会这样:
    $mkdir mydir
    $mv mydir yourdir

    $cd yourdir
    可以改成:
    $mkdir mydir
    $mv !$ yourdir
    $cd !$
  • sudo !!
    以root的身份执行上一条命令 。
    场景举例:比如Ubuntu里用apt-get安装软件包的时候是需要root身份的,我们经常会忘记在apt-get前加sudo。每次不得不加上sudo再重新键入这行命令,这时可以很方便的用sudo !!完事。
    (注:在shell下,有时候你会输入很长的命令,你可以使用!xxx来重复最近的一次命令,比如,你以前输入过,vi /where/the/file/is, 下次你可以使用 !vi 重得上次最近一次的vi命令。)
  • cd –
    回到上一次的目录 。
    场景举例:当前目录为/home/a,用cd ../b切换到/home/b。这时可以通过反复执行cd –命令在/home/a/home/b之间来回方便的切换。
    (注:cd ~ 是回到自己的Home目录,cd ~user,是进入某个用户的Home目录)
  • ‘ALT+.’ or ‘<ESC> .’
    热建alt+. 或 esc+. 可以把上次命令行的参数给重复出来。
  • ^old^new
    替换前一条命令里的部分字符串。
    场景:echo "wanderful",其实是想输出echo "wonderful"。只需要^a^o就行了,对很长的命令的错误拼写有很大的帮助。(注:也可以使用 !!:gs/old/new
  • du -s * | sort -n | tail
    列出当前目录里最大的10个文件。
  • :w !sudo tee %
    在vi中保存一个只有root可以写的文件
  • date -d@1234567890
    时间截转时间
  • > file.txt
    创建一个空文件,比touch短。
  • mtr coolshell.cn
    mtr命令比traceroute要好。
  • 在命令行前加空格,该命令不会进入history里。
  • echo “ls -l” | at midnight
    在某个时间运行某个命令。
  • curl -u user:pass -d status=”Tweeting from the shell” http://twitter.com/statuses/update.xml
    命令行的方式更新twitter。
  • curl -u username –silent “https://mail.google.com/mail/feed/atom” | perl -ne ‘print “\t” if /<name>/; print “$2\n” if /<(title|name)>(.*)<\/\1>/;’
    检查你的gmail未读邮件
  • ps aux | sort -nk +4 | tail
    列出头十个最耗内存的进程
  • man ascii
    显示ascii码表。
    场景:忘记ascii码表的时候还需要google么?尤其在天朝网络如此“顺畅”的情况下,就更麻烦在GWF多应用一次规则了,直接用本地的man ascii吧。
  • ctrl-x e
    快速启动你的默认编辑器(由变量$EDITOR设置)。
  • netstat –tlnp
    列出本机进程监听的端口号。(注:netstat -anop 可以显示侦听在这个端口号的进程)
  • tail -f /path/to/file.log | sed '/^Finished: SUCCESS$/ q'
    当file.log里出现Finished: SUCCESS时候就退出tail,这个命令用于实时监控并过滤log是否出现了某条记录。
  • ssh user@server bash < /path/to/local/script.sh
    在远程机器上运行一段脚本。这条命令最大的好处就是不用把脚本拷到远程机器上。
  • ssh user@host cat /path/to/remotefile | diff /path/to/localfile –
    比较一个远程文件和一个本地文件
  • net rpc shutdown -I ipAddressOfWindowsPC -U username%password
    远程关闭一台Windows的机器
  • screen -d -m -S some_name ping my_router
    后台运行一段不终止的程序,并可以随时查看它的状态。-d -m参数启动“分离”模式,-S指定了一个session的标识。可以通过-R命令来重新“挂载”一个标识的session。更多细节请参考screen用法 man screen
  • wget --random-wait -r -p -e robots=off -U mozilla http://www.example.com
    下载整个www.example.com网站。(注:别太过分,大部分网站都有防爬功能了:))
  • curl ifconfig.me
    当你的机器在内网的时候,可以通过这个命令查看外网的IP。
  • convert input.png -gravity NorthWest -background transparent -extent 720×200  output.png
    改一下图片的大小尺寸
  • lsof –i
    实时查看本机网络服务的活动状态。
  • vim scp://username@host//path/to/somefile
    vim一个远程文件
  • python -m SimpleHTTPServer
    一句话实现一个HTTP服务,把当前目录设为HTTP服务目录,可以通过http://localhost:8000访问 这也许是这个星球上最简单的HTTP服务器的实现了。
  • history | awk '{CMD[$2]++;count++;} END { for (a in CMD )print CMD[a] " " CMD[a]/count*100 "% " a }' | grep -v "./" | column -c3 -s " " -t | sort -nr | nl | head -n10
    (注:有点复杂了,history|awk ‘{print $2}’|awk ‘BEGIN {FS=”|”} {print $1}’|sort|uniq -c|sort -rn|head -10)
    这行脚本能输出你最常用的十条命令,由此甚至可以洞察你是一个什么类型的程序员。
  • tr -c “[:digit:]” ” ” < /dev/urandom | dd cbs=$COLUMNS conv=unblock | GREP_COLOR=”1;32″ grep –color -e “[^ ]” –
    想看看Marix的屏幕效果吗?(不是很像,但也很Cool!)

Linux下定时器

2013年3月17日
一、 基础知识

1、时间类型。Linux下常用的时间类型有4个:time_t,struct timeval,struct timespec,struct tm。
(1)time_t是一个长整型,一般用来表示用1970年以来的秒数。
(2)Struct timeval有两个成员,一个是秒,一个是微妙。

struct timeval {
               long tv_sec;        /* seconds */
               long tv_usec;  /* microseconds */
       };

(3)struct timespec有两个成员,一个是秒,一个是纳秒。

struct timespec{
                      time_t  tv_sec;         /* seconds */
                      long    tv_nsec;        /* nanoseconds */
              };

(4)struct tm是直观意义上的时间表示方法:

struct tm {
                      int     tm_sec;         /* seconds */
                      int     tm_min;         /* minutes */
                      int     tm_hour;        /* hours */
                      int     tm_mday;        /* day of the month */
                      int     tm_mon;         /* month */
                      int     tm_year;        /* year */
                      int     tm_wday;        /* day of the week */
                      int     tm_yday;        /* day in the year */
                      int     tm_isdst;       /* daylight saving time */
              };

2、 时间操作
(1) 时间格式间的转换函数
主要是 time_t、struct tm、时间的字符串格式之间的转换。看下面的函数参数类型以及返回值类型:

char *asctime(const struct tm *tm);
char *ctime(const time_t *timep);
struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);
time_t mktime(struct tm *tm);

gmtime和localtime的参数以及返回值类型相同,区别是前者返回的格林威治标准时间,后者是当地时间。
(2) 获取时间函数
两个函数,获取的时间类型看原型就知道了:

time_t time(time_t *t);
int gettimeofday(struct timeval *tv, struct timezone *tz);

前者获取time_t类型,后者获取struct timeval类型,因为类型的缘故,前者只能精确到秒,后者可以精确到微秒。
二、 延迟函数
主要的延迟函数有:sleep(),usleep(),nanosleep(),select(),pselect().

unsigned int sleep(unsigned int seconds);
void usleep(unsigned long usec);
int nanosleep(const struct timespec *req, struct timespec *rem);
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout);
int pselect(int   n,   fd_set   *readfds,  fd_set  *writefds,  fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);

alarm函数是信号方式的延迟,这种方式不直观,这里不说了。
仅通过函数原型中时间参数类型,可以猜测sleep可以精确到秒级,usleep/select可以精确到微妙级,nanosleep和pselect可以精确到纳秒级。
而实际实现中,linux上的nanosleep和alarm相同,都是基于内核时钟机制实现,受linux内核时钟实现的影响,并不能达到纳秒级的精度,man nanosleep也可以看到这个说明,man里给出的精度是:Linux/i386上是10 ms ,Linux/Alpha上是1ms。
这里有一篇文章http://blog.csdn.net/zhoujunyi/archive/2007/03/30/1546330.aspx,测试了不同延迟函数之间的精确度。文章给出的结论是linux上精度最高的是select,10ms级别。我在本机器测试select和pselect相当,都达到了1ms级的精度,精度高于文章中给出的10ms,sleep在秒级以上和usleep/nanosleep相当。下面贴下我机器上1ms时候的测试结果,其他不贴了:

sleep           1000          0      -1000
usleep           1000       2974       1974
nanosleep        1000       2990       1990
select           1000        991         -9
pselect           1000        990        -10
gettimeofday           1000       1000          0

而使用gettimeofday循环不停检测时间,可精确微秒级,不过不适宜用来做定时器模块。
因此后面的定时期模块将选择select为延迟函数。
三、 定时器模块需求以及实现概述
1、需求。从实现结果的角度说来,需求就是最终的使用方式。呵呵,不详细描述需求了,先直接给出我实现的CTimer类的三个主要接口:

Class CTimer{
Public:
CTimer(unsigned int vinterval,void (*vfunc)(CTimer *,void *),void *vdata,TimerType vtype);
void start();
void stop();
void reset(unsigned int vinterval);
};

使用定时器模块的步骤如下:
(1) 实例化一个CTimer,参数的含义依次是:vinterval间隔时间(单位ms),vfunc是时间到回调的函数,vdata回调函数使用的参数,vtype定时器的类型,分一次型和循环型两种。
(2) 调用start方法。
(3) 必要的时候调用stop和reset。
2、实现。简单描述下定时器模块的实现,有一个manager单例类保存所有CTimer对象,开启一线程运行延迟函数,每次延迟间隔到,扫描保存CTimer的容器,对每个CTimer对象执行减少时间操作,减少到0则执行回调函数。对一次性CTimer,超时则从容器中删除,循环型的将间隔时间重置,不从容器中移除。
CTimer的start执行将对象插入到manager容器中操作;stop执行将对象从manager容器中删除的操作;reset执行先删除,重置间隔,然后再放到容器中,reset不改变CTimer的定时器类型属性。
四、 定时器模块的数据结构选择
Manager类的容器要频繁进行的操作涉及插入、删除、查询等。
误区:(1)简单看,好象该容器要是有序的,方便插入删除等,貌似红黑树比较合适。其实不然,插入删除操作的频率很低,最频繁的还是每次时延到,对容器的扫描并做时间减少操作,红黑树在做顺序扫描相对链表并没什么优势。
(2) 插入的时候依照顺序链表的方式插入到合适的位置保持排序,以保证超时的对象都在链表的头端。其实这也是没必要的,每次时延到,对每一个对象都要做时间减少操作,因此不管是有序还是无序,都是一次扫描就执行完下面操作:减少时间、判断是否超时,是则执行回调,继续判断是什么类型,一次型的则执行完就移除,循环型则执行完直接重置间隔就可。
因此,只需要能快速插入头、删除结点、遍历就好。我的实现直接使用BSD内核中的数据结构LIST,插入头、删除时间复杂度都是1,遍历就不说了。linux下/usr/include/sys下有头文件queue.h里也有LIST结构以及操作的定义。貌似linux下的少了遍历宏:

#define LIST_FOREACH(var, head, field)     \
 for((var) = LIST_FIRST(head);     \
     (var)!= LIST_END(head);     \
     (var) = LIST_NEXT(var, field))

五、 详细实现
这里帖出主要的代码,请重点关注CTimerManager:: process方法,不再详细说了。需要详细的全部代码,可来信索取,整体代码很简单,就两个类。

class CTimer
{
friend class CTimerManager;
public:
    typedef enum
    {
        TIMER_IDLE=0,  //start前以及手动调用stop后的状态
        TIMER_ALIVE,  //在manager的list里时候的状态
        TIMER_TIMEOUT  //超时后被移除的状态,循环型的没有
    }TimerState;
    typedef enum
    {
        TIMER_ONCE=0,  //一次型
        TIMER_CIRCLE   //循环型
    }TimerType;
    CTimer(unsigned int vinterval,void (*vfunc)(CTimer *,void *),void *vdata,TimerType vtype);
    void start();
    void stop();
    void reset(unsigned int vinterval);
    ~CTimer();
private:
    unsigned int id_;     //测试用
    unsigned int m_interval;  //间隔,不变
    unsigned int m_counter;  //开始设置为interval,随延迟时间到,减少
    TimerState m_state;      //状态
    TimerType m_type;        //类型
    void (*m_func)(CTimer *,void *);//回调函数
    void * m_data;  //回调函数参数
    LIST_ENTRY(CTimer) entry_;  //LIST的使用方式
};
/*构造函数*/
CTimer::CTimer(unsigned int vinterval,void (*vfunc)(CTimer *,void *),void *vdata,TimerType vtype):

    m_interval(vinterval),m_counter(vinterval),
    m_state(TIMER_IDLE),m_type(vtype),
    m_func(vfunc),m_data(vdata)
{}
/*开始定时器*/
void CTimer::start()
{
    CTimerManager::instance()->add_timer(this);
}
/*停止定时器*/
void CTimer::stop()
{
    CTimerManager::instance()->remove_timer(this);
}
/*reset定时器*/
void CTimer::reset(unsigned int vinterval)
{
    CTimerManager::instance()->remove_timer(this);
    m_counter=m_interval=vinterval;
    CTimerManager::instance()->add_timer(this);
}
/*析构函数,stop操作不能省略,避免delete前忘记stop*/
CTimer::~CTimer()
{
    if(m_state==TIMER_ALIVE)
        stop();
}

CTimerManager的:

class CTimerManager
{
public:

    typedef enum
    {
        TIMER_MANAGER_STOP=0,
        TIMER_MANAGER_START
    }TimerManagerState;

    static CTimerManager * instance();
    void add_timer(CTimer * vtimer);//线程安全的add
    void remove_timer(CTimer * vtimer);//线程安全的remove
    void start();  //开始process线程
    void stop();  //停止process线程
    void dump();
protected:
    static void * process(void *); //实际的定时器时间延迟线程
private:
    void add_timer_(CTimer * vtimer);//非线程安全的add
    void remove_timer_(CTimer * vtimer);//非线程安全的remove

    CTimerManager();
    static pthread_mutex_t m_mutex;
    static CTimerManager * m_instance;

    TimerManagerState m_state;
    LIST_HEAD(,CTimer) list_;  //LIST使用方式

    static unsigned int mark;  //测试,配合dump
};
/*singlton的double-check实现*/
CTimerManager * CTimerManager::instance()
{
    if(m_instance==NULL)
    {
        pthread_mutex_lock(&m_mutex);
        if(m_instance==NULL)
        {
            m_instance=new CTimerManager();
        }
        pthread_mutex_unlock(&m_mutex);
    }
    return m_instance;
}
/*process必须static,不能操作非static属性,因此传递this指针*/
void CTimerManager:: start()
{
if(m_state==TIMER_MANAGER_STOP){
    m_state=TIMER_MANAGER_START;
    pthread_t pid;
    pthread_create(&pid,0,process,this);
}
}
/*定时器模块延迟时间线程*/
void * CTimerManager:: process(void * arg)
{
    pthread_detach(pthread_self());

    CTimerManager *manage=(CTimerManager *)arg;

    CTimer *item;
    struct timeval start,end;
    unsigned int delay;

    struct timeval tm;
    gettimeofday(&end,0);
/*使用状态控制线程运行,进而容易实现stop,也可以使用pthread_cancel粗暴的停止,需要考虑暂停点等问题*/
    while(manage->m_state==TIMER_MANAGER_START)
    {
        tm.tv_sec=0;
        tm.tv_usec=DEFULT_INTERVAL*1000;
        start.tv_sec=end.tv_sec;
        start.tv_usec=end.tv_usec;
/*不同系统的延迟函数精度不同,如果需要替换为其他延迟函数,这附近修改下就好*/
        while(select(0,0,0,0,&tm)<0&&errno==EINTR);
        gettimeofday(&end,0);

        delay=(end.tv_sec-start.tv_sec)*1000+(end.tv_usec-start.tv_usec)/1000;
        pthread_mutex_lock(&manage->m_mutex);

        LIST_FOREACH(item, &(manage->list_), entry_)
        {
            item->m_counter<delay?item->m_counter=0:item->m_counter-=delay;
            if(item->m_counter==0)
            {
                if(item->m_func)
                item->m_func(item,item->m_data);

                if(item->m_type==CTimer::TIMER_ONCE)
                {
/*一次型的,超时,移除,并状态CTimer::TIMER_TIMEOUT*/
                    manage->remove_timer_(item);
                    item->m_state=CTimer::TIMER_TIMEOUT;
                }
                else if(item->m_type==CTimer::TIMER_CIRCLE)
                {
/*循环型的,重置counter就好*/
                    item->m_counter=item->m_interval;
                }
            }
        }

        pthread_mutex_unlock(&manage->m_mutex);
    }
}

六、 讨论
(1)精度问题。精度高,实时性高,但要求select等待的时间缩短,进而增加对LIST结构的扫描操作。精度低,实时性差,但会增加定时器线程的睡眠时间,减少对cpu的占用。一般的应用系统,应该尽量降低精度,避免不必要的扫描,对具体系统可考察所用到的所有定时器的实际间隔,在允许的情况下,尽量降低精度,可通过修改代码中的宏实现。为了降低定时器线程对cpu的占有时间,甚有更为粗犷型的定时器模块实现为将延迟时间取list中最小的那个间隔,保证每次延迟时间到都有回调。
(2)加锁区域问题。本文中的定时器模块实现,将定时器对象的时间减少以及函数回调的执行等再同一个临界区内执行,而有的定时器模块实现是在加锁区域执行“时间减少”操作,将减少到0的对象放到另一个超时链表中,解锁后再单独扫描超时链表执行回调操作。很明显,后者缩短了加锁时间,能及时响应其他的线程的定时器对象的start以及stop操作。但是后者对定时器操作的时序性有误差,直观反应就是可能在定时器执行了stop操作以后,仍然会有超时回调发生,特别是回调参数是指针的情况,可能引起难以发现的bug,增加调试困难。在衡量两者的利弊后,本文采用延长加锁时间以保证操作的时序性。因此,在实际的使用,回调函数应尽快返回,另一方面,尽量减少系统内使用的定时器数目,这个主要原因是延迟时间到要扫描LIST,哪种方式都避免不了。

七、使用示例

#include “timer_manager.h”
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void func(CTimer * timer, void *data)
{
    printf(“hi,%d\n”,(int
)(data));
}

/*随便写的,凑合着看吧。没有CTimerManager::instance()->stop();也没new对象。定时器对象可多次start和stop,使用上对暴露的接口没有任何的契约式限制,可随意调用*/
int main()
{
    CTimerManager::instance()->
start();
    CTimer a(1000,func,(void *)1
,CTimer::TIMER_CIRCLE);
    CTimer a1(2000,func,(void *)11
,CTimer::TIMER_ONCE);
    CTimer a2(3000,func,(void *)12
,CTimer::TIMER_ONCE);
    CTimer a3(1000,func,(void *)13
,CTimer::TIMER_ONCE);

    a.start();
    a1.start();
    a2.start();
    a3.start();
    a.start();
    a1.start();
    a2.start();
    a3.start();

    sleep(1
);
    CTimerManager::instance()->
dump();
    sleep(1
);
    CTimerManager::instance()->
dump();
    a.reset(2000
);
    a1.stop();
    a3.stop();

    sleep(10
);
    return 0
;
}
 

经典的GDB调试命令

2013年3月17日
在你调试程序时,当程序被停住时,你可以使用print命令(简写命令为p),或是同义命令inspect来查看当前程序的运行数据。print命令的格式是:

print
print /
是表达式,是你所调试的程序的语言的表达式(GDB可以调试多种编程语言),是输出的格式,比如,如果要把表达式按16进制的格式输出,那么就是/x。

一、表达式
print和许多GDB的命令一样,可以接受一个表达式,GDB会根据当前的程序运行的数据来计算这个表达式,既然是表达式,那么就可以是当前程序运行中的const常量、变量、函数等内容。可惜的是GDB不能使用你在程序中所定义的宏。
表达式的语法应该是当前所调试的语言的语法,由于C/C++是一种大众型的语言,所以,本文中的例子都是关于C/C++的。(而关于用GDB调试其它语言的章节,我将在后面介绍)
在表达式中,有几种GDB所支持的操作符,它们可以用在任何一种语言中。
@
是一个和数组有关的操作符,在后面会有更详细的说明。
::
指定一个在文件或是一个函数中的变量。
{}
表示一个指向内存地址的类型为type的一个对象。

二、程序变量
在GDB中,你可以随时查看以下三种变量的值:
1、全局变量(所有文件可见的)
2、静态全局变量(当前文件可见的)
3、局部变量(当前Scope可见的)
如果你的局部变量和全局变量发生冲突(也就是重名),一般情况下是局部变量会隐藏全局变量,也就是说,如果一个全局变量和一个函数中的局部变量同名时,如果当前停止点在函数中,用print显示出的变量的值会是函数中的局部变量的值。如果此时你想查看全局变量的值时,你可以使用“::”操作符:
file::variable
function::variable
可以通过这种形式指定你所想查看的变量,是哪个文件中的或是哪个函数中的。例如,查看文件f2.c中的全局变量x的值:
gdb) p ‘f2.c’::x
当然,“::”操作符会和C++中的发生冲突,GDB能自动识别“::” 是否C++的操作符,所以你不必担心在调试C++程序时会出现异常。
另外,需要注意的是,如果你的程序编译时开启了优化选项,那么在用GDB调试被优化过的程序时,可能会发生某些变量不能访问,或是取值错误码的情况。这个是很正常的,因为优化程序会删改你的程序,整理你程序的语句顺序,剔除一些无意义的变量等,所以在GDB调试这种程序时,运行时的指令和你所编写指令就有不一样,也就会出现你所想象不到的结果。对付这种情况时,需要在编译程序时关闭编译优化。一般来说,几乎所有的编译器都支持编译优化的开关,例如,GNU 的C/C++编译器GCC,你可以使用“-gstabs”选项来解决这个问题。关于编译器的参数,还请查看编译器的使用说明文档。

三、数组
有时候,你需要查看一段连续的内存空间的值。比如数组的一段,或是动态分配的数据的大小。你可以使用GDB的“@”操作符,“@”的左边是第一个内存的地址的值,“@”的右边则你你想查看内存的长度。例如,你的程序中有这样的语句:
int *array = (int *) malloc (len * sizeof (int));
于是,在GDB调试过程中,你可以以如下命令显示出这个动态数组的取值:
*array@len
@的左边是数组的首地址的值,也就是变量array所指向的内容,右边则是数据的长度,其保存在变量len中,其输出结果,大约是下面这个样子的:
(gdb) p *array@len
$1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}
如果是静态数组的话,可以直接用print数组名,就可以显示数组中所有数据的内容了。

四、输出格式
一般来说,GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。例如,你想输出一个整数的十六进制,或是二进制来查看这个整型变量的中的位的情况。要做到这样,你可以使用GDB的数据显示格式:
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。
(gdb) p i
$21 = 101
(gdb) p/a i
$22 = 0x65
(gdb) p/c i
$23 = 101 ‘e’
(gdb) p/f i
$24 = 1.41531145e-43
(gdb) p/x i
$25 = 0x65
(gdb) p/t i
$26 = 1100101

五、查看内存
你可以使用examine命令(简写是x)来查看内存地址中的值。x命令的语法如下所示:
x/
n、f、u是可选的参数。
n 是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。
f 表示显示的格式,参见上面。如果地址所指的是字符串,那么格式可以是s,如果地十是指令地址,那么格式可以是i。
u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。
表示一个内存地址。
n/f/u三个参数可以一起使用。例如:
命令:x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。

六、自动显示
你可以设置一些自动显示的变量,当程序停住时,或是在你单步跟踪时,这些变量会自动显示。相关的GDB命令是display。
display
display/
display/
expr是一个表达式,fmt表示显示的格式,addr表示内存地址,当你用display设定好了一个或多个表达式后,只要你的程序被停下来,GDB会自动显示你所设置的这些表达式的值。
格式i和s同样被display支持,一个非常有用的命令是:
display/i $pc
$pc是GDB的环境变量,表示着指令的地址,/i则表示输出格式为机器指令码,也就是汇编。于是当程序停下后,就会出现源代码和机器指令码相对应的情形,这是一个很有意思的功能。
下面是一些和display相关的GDB命令:
undisplay
delete display
删除自动显示,dnums意为所设置好了的自动显式的编号。如果要同时删除几个,编号可以用空格分隔,如果要删除一个范围内的编号,可以用减号表示(如:2-5)
disable display
enable display
disable和enalbe不删除自动显示的设置,而只是让其失效和恢复。
info display
查看display设置的自动显示的信息。GDB会打出一张表格,向你报告当然调试中设置了多少个自动显示设置,其中包括,设置的编号,表达式,是否enable。
七、设置显示选项
GDB中关于显示的选项比较多,这里我只例举大多数常用的选项。
set print address
set print address on
打开地址输出,当程序显示函数信息时,GDB会显出函数的参数地址。系统默认为打开的,如:
(gdb) f
#0 set_quotes (lq=0x34c78 “<<“, rq=0x34c88 “>>”)
at input.c:530
530 if (lquote != def_lquote)

set print address off
关闭函数的参数地址显示,如:
(gdb) set print addr off
(gdb) f
#0 set_quotes (lq=”<<“, rq=”>>”) at input.c:530
530 if (lquote != def_lquote)
show print address
查看当前地址显示选项是否打开。
set print array
set print array on
打开数组显示,打开后当数组显示时,每个元素占一行,如果不打开的话,每个元素则以逗号分隔。这个选项默认是关闭的。与之相关的两个命令如下,我就不再多说了。
set print array off
show print array
set print elements
这个选项主要是设置数组的,如果你的数组太大了,那么就可以指定一个来指定数据显示的最大长度,当到达这个长度时,GDB就不再往下显示了。如果设置为0,则表示不限制。
show print elements
查看print elements的选项信息。
set print null-stop
如果打开了这个选项,那么当显示字符串时,遇到结束符则停止显示。这个选项默认为off。
set print pretty on
如果打开printf pretty这个选项,那么当GDB显示结构体时会比较漂亮。如:
$1 = {
next = 0x0,
flags = {
sweet = 1,
sour = 1
},
meat = 0x54 “Pork”
}
set print pretty off
关闭printf pretty这个选项,GDB显示结构体时会如下显示:
$1 = {next = 0x0, flags = {sweet = 1, sour = 1}, meat = 0x54 “Pork”}
show print pretty
查看GDB是如何显示结构体的。

set print sevenbit-strings
设置字符显示,是否按“\nnn”的格式显示,如果打开,则字符串或字符数据按\nnn显示,如“65”。
show print sevenbit-strings
查看字符显示开关是否打开。
set print union
设置显示结构体时,是否显式其内的联合体数据。例如有以下数据结构:
typedef enum {Tree, Bug} Species;
typedef enum {Big_tree, Acorn, Seedling} Tree_forms;
typedef enum {Caterpillar, Cocoon, Butterfly}
Bug_forms;
struct thing {
Species it;
union {
Tree_forms tree;
Bug_forms bug;
} form;
};
struct thing foo = {Tree, {Acorn}};
当打开这个开关时,执行 p foo 命令后,会如下显示:
$1 = {it = Tree, form = {tree = Acorn, bug = Cocoon}}
当关闭这个开关时,执行 p foo 命令后,会如下显示:
$1 = {it = Tree, form = {…}}
show print union
查看联合体数据的显示方式
set print object
在C++中,如果一个对象指针指向其派生类,如果打开这个选项,GDB会自动按照虚方法调用的规则显示输出,如果关闭这个选项的话,GDB就不管虚函数表了。这个选项默认是off。
show print object
查看对象选项的设置。
set print static-members
这个选项表示,当显示一个C++对象中的内容是,是否显示其中的静态数据成员。默认是on。
show print static-members
查看静态数据成员选项设置。
set print vtbl
当此选项打开时,GDB将用比较规整的格式来显示虚函数表时。其默认是关闭的。
show print vtbl
查看虚函数显示格式的选项。

八、历史记录
当你用GDB的print查看程序运行时的数据时,你每一个print都会被GDB记录下来。GDB会以$1, $2, $3 …..这样的方式为你每一个print命令编上号。于是,你可以使用这个编号访问以前的表达式,如$1。这个功能所带来的好处是,如果你先前输入了一个比较长的表达式,如果你还想查看这个表达式的值,你可以使用历史记录来访问,省去了重复输入。

九、GDB环境变量
你可以在GDB的调试环境中定义自己的变量,用来保存一些调试程序中的运行数据。要定义一个GDB的变量很简单只需。使用GDB的set命令。GDB的环境变量和UNIX一样,也是以$起头。如:
set $foo = *object_ptr
使用环境变量时,GDB会在你第一次使用时创建这个变量,而在以后的使用中,则直接对其赋值。环境变量没有类型,你可以给环境变量定义任一的类型。包括结构体和数组。
show convenience
该命令查看当前所设置的所有的环境变量。
这是一个比较强大的功能,环境变量和程序变量的交互使用,将使得程序调试更为灵活便捷。例如:
set $i = 0
print bar[$i++]->contents
于是,当你就不必,print bar[0]->contents, print bar[1]->contents地输入命令了。输入这样的命令后,只用敲回车,重复执行上一条语句,环境变量会自动累加,从而完成逐个输出的功能。

十、查看寄存器
要查看寄存器的值,很简单,可以使用如下命令:
info registers
查看寄存器的情况。(除了浮点寄存器)
info all-registers
查看所有寄存器的情况。(包括浮点寄存器)
info registers
查看所指定的寄存器的情况。
寄存器中放置了程序运行时的数据,比如程序当前运行的指令地址(ip),程序的当前堆栈地址(sp)等等。你同样可以使用print命令来访问寄存器的情况,只需要在寄存器名字前加一个$符号就可以了。如:p $eip。

gdbtui窗口切换

2013年3月17日

使用命令:fs next

则可以进行切换

如果你想查看当前的窗口,则可以使用:
info win
顾名思义:
fs 则是focus
win则是windows
以后有空学学Cgdb,听说比较强大,而且支持高亮显示。

#if、#ifdef、#if defined之间的区别

2013年3月17日

#if的使用说明

#if的后面接的是表达式

#if (MAX==10)||(MAX==20)
 code...
#endif

它的作用是:如果(MAX==10)||(MAX==20)成立,那么编译器就会把其中的#if 与 #endif之间的代码编译进去(注意:是编译进去,不是执行!!)

#if defined的使用

#if后面接的是一个宏。

#if defined (x)
    ...code...
#endif

这个#if defined它不管里面的“x”的逻辑是“真”还是“假”它只管这个程序的前面的宏定义里面有没有定义“x”这个宏,如果定义了x这个宏,那么,编译器会编译中间的…code…否则不直接忽视中间的…code…代码。

另外 #if defined(x)也可以取反,也就用 #if !defined(x)

#ifdef的使用

#ifdef的使用和#if defined()的用法一致
#ifndef又和#if !defined()的用法一致。

最后强调两点:
第一:这几个宏定义只是决定代码块是否被编译!
第二:别忘了#endif

Fedora14安装chrome依赖解决

2013年3月17日

提示:
[root@Creater 下载]# rpm -ivh google-chrome-stable_current_i386.rpm 
warning: google-chrome-stable_current_i386.rpm: Header V4 DSA/SHA1 Signature, key ID 7fac5991: NOKEY
error: Failed dependencies:
lsb >= 4.0 is needed by google-chrome-stable-24.0.1312.52-175374.i386
[root@Creater 下载]# yum install lsb
已加载插件:langpacks, presto, refresh-packagekit
Adding zh_CN to language list
设置安装进程
解决依赖关系
–> 执行事务检查
—> 软件包 redhat-lsb.i686 0:4.0-5.fc14 将被 安装
–> 处理依赖关系 /usr/bin/pax,它被软件包 redhat-lsb-4.0-5.fc14.i686 需要
–> 执行事务检查
—> 软件包 pax.i686 0:3.4-10.fc12 将被 安装
–> 完成依赖关系计算

依赖关系解决

================================================================================
软件包              架构          版本                   仓库             大小
================================================================================
正在安装:
redhat-lsb          i686          4.0-5.fc14             fedora           25 k
为依赖而安装:
pax                 i686          3.4-10.fc12            fedora           67 k

事务概要
================================================================================
Install       2 Package(s)

总下载量:92 k
Installed size: 156 k
确定吗?[y/N]:y

linux下IPTABLES配置详解

2013年3月17日
如果你的IPTABLES基础知识还不了解,建议先去看看.
开始配置
我们来配置一个filter表的防火墙.
(1)查看本机关于IPTABLES的设置情况
[root@tp ~]# iptables -L -n
Chain INPUT (policy ACCEPT)
target       prot opt source                 destination
Chain FORWARD (policy ACCEPT)
target       prot opt source                 destination
Chain OUTPUT (policy ACCEPT)
target       prot opt source                 destination
Chain RH-Firewall-1-INPUT (0 references)
target       prot opt source                 destination
ACCEPT       all    —    0.0.0.0/0              0.0.0.0/0
ACCEPT       icmp —    0.0.0.0/0              0.0.0.0/0             icmp type 255
ACCEPT       esp    —    0.0.0.0/0              0.0.0.0/0
ACCEPT       ah     —    0.0.0.0/0              0.0.0.0/0
ACCEPT       udp    —    0.0.0.0/0              224.0.0.251           udp dpt:5353
ACCEPT       udp    —    0.0.0.0/0              0.0.0.0/0             udp dpt:631
ACCEPT       all    —    0.0.0.0/0              0.0.0.0/0             state RELATED,ESTABLISHED
ACCEPT       tcp    —    0.0.0.0/0              0.0.0.0/0             state NEW tcp dpt:22
ACCEPT       tcp    —    0.0.0.0/0              0.0.0.0/0             state NEW tcp dpt:80
ACCEPT       tcp    —    0.0.0.0/0              0.0.0.0/0             state NEW tcp dpt:25
REJECT       all    —    0.0.0.0/0              0.0.0.0/0             reject-with icmp-host-prohibited
可以看出我在安装linux时,选择了有防火墙,并且开放了22,80,25端口.
如果你在安装linux时没有选择启动防火墙,是这样的
[root@tp ~]# iptables -L -n
Chain INPUT (policy ACCEPT)
target       prot opt source                 destination
Chain FORWARD (policy ACCEPT)
target       prot opt source                 destination
Chain OUTPUT (policy ACCEPT)
target       prot opt source                 destination
什么规则都没有.
(2)清除原有规则.
不管你在安装linux时是否启动了防火墙,如果你想配置属于自己的防火墙,那就清除现在filter的所有规则.
[root@tp ~]# iptables -F        清除预设表filter中的所有规则链的规则
[root@tp ~]# iptables -X        清除预设表filter中使用者自定链中的规则
我们在来看一下
[root@tp ~]# iptables -L -n
Chain INPUT (policy ACCEPT)
target       prot opt source                 destination
Chain FORWARD (policy ACCEPT)
target       prot opt source                 destination
Chain OUTPUT (policy ACCEPT)
target       prot opt source                 destination
什么都没有了吧,和我们在安装linux时没有启动防火墙是一样的.(提前说一句,这些配置就像用命令配置IP一样,重起就会失去作用),怎么保存.
[root@tp ~]# /etc/rc.d/init.d/iptables save
这样就可以写到/etc/sysconfig/iptables文件里了.写入后记得把防火墙重起一下,才能起作用.
[root@tp ~]# service iptables restart
现在IPTABLES配置表里什么配置都没有了,那我们开始我们的配置吧
(3)设定预设规则
[root@tp ~]# iptables -p INPUT DROP
[root@tp ~]# iptables -p OUTPUT ACCEPT
[root@tp ~]# iptables -p FORWARD DROP
上面的意思是,当超出了IPTABLES里filter表里的两个链规则(INPUT,FORWARD)时,不在这两个规则里的数据包怎么处理呢,那就是DROP(放弃).应该说这样配置是很安全的.我们要控制流入数据包
而对于OUTPUT链,也就是流出的包我们不用做太多限制,而是采取ACCEPT,也就是说,不在着个规则里的包怎么办呢,那就是通过.
可以看出INPUT,FORWARD两个链采用的是允许什么包通过,而OUTPUT链采用的是不允许什么包通过.
这样设置还是挺合理的,当然你也可以三个链都DROP,但这样做我认为是没有必要的,而且要写的规则就会增加.但如果你只想要有限的几个规则是,如只做WEB服务器.还是推荐三个链都是DROP.
注:如果你是远程SSH登陆的话,当你输入第一个命令回车的时候就应该掉了.因为你没有设置任何规则.
怎么办,去本机操作呗!
(4)添加规则.
首先添加INPUT链,INPUT链的默认规则是DROP,所以我们就写需要ACCETP(通过)的链
为了能采用远程SSH登陆,我们要开启22端口.
[root@tp ~]# iptables -A INPUT -p tcp –dport 22 -j ACCEPT
[root@tp ~]# iptables -A OUTPUT -p tcp –sport 22 -j ACCEPT (注:这个规则,如果你把OUTPUT 设置成DROP的就要写上这一部,好多人都是望了写这一部规则导致,始终无法SSH.在远程一下,是不是好了.
其他的端口也一样,如果开启了web服务器,OUTPUT设置成DROP的话,同样也要添加一条链:
[root@tp ~]# iptables -A OUTPUT -p tcp –sport 80 -j ACCEPT ,其他同理.)
如果做了WEB服务器,开启80端口.
[root@tp ~]# iptables -A INPUT -p tcp –dport 80 -j ACCEPT
如果做了邮件服务器,开启25,110端口.
[root@tp ~]# iptables -A INPUT -p tcp –dport 110 -j ACCEPT
[root@tp ~]# iptables -A INPUT -p tcp –dport 25 -j ACCEPT

如果做了FTP服务器,开启21端口
[root@tp ~]# iptables -A INPUT -p tcp –dport 21 -j ACCEPT
[root@tp ~]# iptables -A INPUT -p tcp –dport 20 -j ACCEPT
如果做了DNS服务器,开启53端口
[root@tp ~]# iptables -A INPUT -p tcp –dport 53 -j ACCEPT
如果你还做了其他的服务器,需要开启哪个端口,照写就行了.
上面主要写的都是INPUT链,凡是不在上面的规则里的,都DROP
允许icmp包通过,也就是允许ping,
[root@tp ~]# iptables -A OUTPUT -p icmp -j ACCEPT (OUTPUT设置成DROP的话)
[root@tp ~]# iptables -A INPUT -p icmp -j ACCEPT    (INPUT设置成DROP的话)
允许loopback!(不然会导致DNS无法正常关闭等问题)
IPTABLES -A INPUT -i lo -p all -j ACCEPT (如果是INPUT DROP)
IPTABLES -A OUTPUT -o lo -p all -j ACCEPT(如果是OUTPUT DROP)
下面写OUTPUT链,OUTPUT链默认规则是ACCEPT,所以我们就写需要DROP(放弃)的链.
减少不安全的端口连接
[root@tp ~]# iptables -A OUTPUT -p tcp –sport 31337 -j DROP
[root@tp ~]# iptables -A OUTPUT -p tcp –dport 31337 -j DROP
有些些特洛伊木马会扫描端口31337到31340(即黑客语言中的 elite 端口)上的服务。既然合法服务都不使用这些非标准端口来通信,阻塞这些端口能够有效地减少你的网络上可能被感染的机器和它们的远程主服务器进行独立通信的机会
还有其他端口也一样,像:31335、27444、27665、20034 NetBus、9704、137-139(smb),2049(NFS)端口也应被禁止,我在这写的也不全,有兴趣的朋友应该去查一下相关资料.
当然出入更安全的考虑你也可以包OUTPUT链设置成DROP,那你添加的规则就多一些,就像上边添加
允许SSH登陆一样.照着写就行了.
下面写一下更加细致的规则,就是限制到某台机器
如:我们只允许192.168.0.3的机器进行SSH连接
[root@tp ~]# iptables -A INPUT -s 192.168.0.3 -p tcp –dport 22 -j ACCEPT
如果要允许,或限制一段IP地址可用 192.168.0.0/24 表示192.168.0.1-255端的所有IP.
24表示子网掩码数.但要记得把 /etc/sysconfig/iptables 里的这一行删了.
-A INPUT -p tcp -m tcp –dport 22 -j ACCEPT 因为它表示所有地址都可以登陆.
或采用命令方式:
[root@tp ~]# iptables -D INPUT -p tcp –dport 22 -j ACCEPT
然后保存,我再说一边,反是采用命令的方式,只在当时生效,如果想要重起后也起作用,那就要保存.写入到/etc/sysconfig/iptables文件里.
[root@tp ~]# /etc/rc.d/init.d/iptables save
这样写 !192.168.0.3 表示除了192.168.0.3的ip地址
其他的规则连接也一样这么设置.
在下面就是FORWARD链,FORWARD链的默认规则是DROP,所以我们就写需要ACCETP(通过)的链,对正在转发链的监控.
开启转发功能,(在做NAT时,FORWARD默认规则是DROP时,必须做)
[root@tp ~]# iptables -A FORWARD -i eth0 -o eth1 -m state –state RELATED,ESTABLISHED -j ACCEPT
[root@tp ~]# iptables -A FORWARD -i eth1 -o eh0 -j ACCEPT
丢弃坏的TCP包
[root@tp ~]#iptables -A FORWARD -p TCP ! –syn -m state –state NEW -j DROP
处理IP碎片数量,防止攻击,允许每秒100个
[root@tp ~]#iptables -A FORWARD -f -m limit –limit 100/s –limit-burst 100 -j ACCEPT
设置ICMP包过滤,允许每秒1个包,限制触发条件是10个包.
[root@tp ~]#iptables -A FORWARD -p icmp -m limit –limit 1/s –limit-burst 10 -j ACCEPT
我在前面只所以允许ICMP包通过,就是因为我在这里有限制.
二,配置一个NAT表放火墙
1,查看本机关于NAT的设置情况
[root@tp rc.d]# iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target       prot opt source                 destination
Chain POSTROUTING (policy ACCEPT)
target       prot opt source                 destination
SNAT         all    —    192.168.0.0/24         anywhere              to:211.101.46.235
Chain OUTPUT (policy ACCEPT)
target       prot opt source                 destination
我的NAT已经配置好了的(只是提供最简单的代理上网功能,还没有添加防火墙规则).关于怎么配置NAT,参考我的另一篇文章
当然你如果还没有配置NAT的话,你也不用清除规则,因为NAT在默认情况下是什么都没有的
如果你想清除,命令是
[root@tp ~]# iptables -F -t nat
[root@tp ~]# iptables -X -t nat
[root@tp ~]# iptables –Z -t nat
2,添加规则
添加基本的NAT地址转换,(关于如何配置NAT可以看我的另一篇文章),
添加规则,我们只添加DROP链.因为默认链全是ACCEPT.
防止外网用内网IP欺骗
[root@tp sysconfig]# iptables -t nat -A PREROUTING -i eth0 -s 10.0.0.0/8 -j DROP
[root@tp sysconfig]# iptables -t nat -A PREROUTING -i eth0 -s 172.16.0.0/12 -j DROP
[root@tp sysconfig]# iptables -t nat -A PREROUTING -i eth0 -s 192.168.0.0/16 -j DROP

如果我们想,比如阻止MSN,QQ,BT等的话,需要找到它们所用的端口或者IP,(个人认为没有太大必要)
例:
禁止与211.101.46.253的所有连接

[root@tp ~]# iptables -t nat -A PREROUTING    -d 211.101.46.253 -j DROP

禁用FTP(21)端口
[root@tp ~]# iptables -t nat -A PREROUTING -p tcp –dport 21 -j DROP
这样写范围太大了,我们可以更精确的定义.
[root@tp ~]# iptables -t nat -A PREROUTING    -p tcp –dport 21 -d 211.101.46.253 -j DROP
这样只禁用211.101.46.253地址的FTP连接,其他连接还可以.如web(80端口)连接.
按照我写的,你只要找到QQ,MSN等其他软件的IP地址,和端口,以及基于什么协议,只要照着写就行了.
最后:
drop非法连接
[root@tp ~]# iptables -A INPUT     -m state –state INVALID -j DROP
[root@tp ~]# iptables -A OUTPUT    -m state –state INVALID -j DROP
[root@tp ~]# iptables-A FORWARD -m state –state INVALID -j DROP

允许所有已经建立的和相关的连接
[root@tp ~]# iptables-A INPUT -m state –state ESTABLISHED,RELATED -j ACCEPT
[root@tp ~]# iptables-A OUTPUT -m state –state ESTABLISHED,RELATED -j ACCEPT

[root@tp ~]# /etc/rc.d/init.d/iptables save

这样就可以写到/etc/sysconfig/iptables文件里了.写入后记得把防火墙重起一下,才能起作用.

[root@tp ~]# service iptables restart
别忘了保存,不行就写一部保存一次.你可以一边保存,一边做实验,看看是否达到你的要求,

上面的所有规则我都试过,没有问题.
写这篇文章,用了我将近1个月的时间.查找资料,自己做实验,希望对大家有所帮助.如有不全及不完善的地方还请提出.
因为本篇文章以配置为主.关于IPTABLES的基础知识及指令命令说明等我会尽快传上,当然你可以去网上搜索一下,还是很多的.

怎样自动登陆Linux系统

2013年3月17日

下面我将详细解说mingetty程序的参数意义:Software:mingetty-1.07

参数 意义
-a 自动登陆,后面参数为用户名
-d 延时,后面参数为延时时间
-l 所使用的登陆程序,后面参数为所使用的登陆程序名
-n 优先级,设定优先级
-r 根文件系统
-w 更改当前工作目录

修改/etc/inittab文件,在相应行行尾,增加:
1:2345:respawn:/sbin/mingetty tty1 -a root
2:2345:respawn:/sbin/mingetty tty2 -a root
3:2345:respawn:/sbin/mingetty tty3 -a root
4:2345:respawn:/sbin/mingetty tty4-a root
5:2345:respawn:/sbin/mingetty tty5 -a root
6:2345:respawn:/sbin/mingetty tty6 -a root

重新启动机器测试