bakey的小世界

· 所有网志 (156) · 胡思乱想 (3) · 学习生活 (7) · 未分类 (146) ·
bakey @ 2008-07-08 15:48

摘要:一直以来都觉得printf似乎是c语言库中功能最强大的函数之一,不仅因为它能格式化输出,更在于它的参数个数没有限制,要几个就给几个,来者不拒。printf这种对参数个数和参数类型的强大适应性,让人产生了对它进行探索的浓厚兴趣。

关键字:printf, 可变参数

1. 使用情形

int a =10;
double b = 20.0;
char *str = "Hello world";
printf("begin print\n");
printf("a=%d, b=%.3f, str=%s\n", a, b, str);
...
  从printf的使用情况来看,我们不难发现一个规律,就是无论其可变的参数有多少个,printf的第一个参数总是一个字符串。而正是这第一个参数,使得它可以确认后面还有有多少个参数尾随。而尾随的每个参数占用的栈空间大小又是通过第一个格式字符串确定的。然而printf到底是怎样取第一个参数后面的参数值的呢,请看如下代码

2. printf 函数的实现

//acenv.h
typedef char *va_list;

#define  _AUPBND        (sizeof (acpi_native_int) - 1)
#define  _ADNBND        (sizeof (acpi_native_int) - 1)
                        
#define _bnd(X, bnd)    (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T)   (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap)      (void) 0
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

//start.c
static char sprint_buf[1024];
int printf(char *fmt, ...)
{
 va_list args;
 int n;
 va_start(args, fmt);
 n = vsprintf(sprint_buf, fmt, args);
 va_end(args);
 write(stdout, sprint_buf, n);
 return n;
}

//unistd.h
static inline long write(int fd, const char *buf, off_t count)
{
 return sys_write(fd, buf, count);
}
3. 分析

  从上面的代码来看,printf似乎并不复杂,它通过一个宏va_start把所有的可变参数放到了由args指向的一块内存中,然后再调用vsprintf. 真正的参数个数以及格式的确定是在vsprintf搞定的了。由于vsprintf的代码比较复杂,也不是我们这里要讨论的重点,所以下面就不再列出了。我们这里要讨论的重点是va_start(ap, A)宏的实现,它对定位从参数A后面的参数有重大的制导意义。现在把 #define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND)))) 的含义解释一下如下:

va_start(ap, A)
    {
         char *ap =  ((char *)(&A)) + sizeof(A)并int类型大小地址对齐
    }
  在printf的va_start(args, fmt)中,fmt的类型为char *, 因此对于一个32为系统 sizeof(char *) = 4, 如果int大小也是32,则va_start(args, fmt);相当于 char *args = (char *)(&fmt) + 4; 此时args的值正好为fmt后第一个参数的地址。对于如下的可变参数函数
void fun(double d,...)
    {
        va_list args;
     int n;
     va_start(args, d);
    }
则 va_start(args, d);相当于
char *args = (char *)&d + sizeof(double);
  此时args正好指向d后面的第一个参数。

  可变参数函数的实现与函数调用的栈结构有关,正常情况下c/c++的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈。对于函数

void fun(int a, int b, int c)
    {
        int d;
        ...
    }
其栈结构为
0x1ffc-->d
    0x2000-->a
    0x2004-->b
    0x2008-->c
  对于任何编译器,每个栈单元的大小都是sizeof(int), 而函数的每个参数都至少要占一个栈单元大小,如函数 void fun1(char a, int b, double c, short d) 对一个32的系统其栈的结构就是
0x1ffc-->a  (4字节)
    0x2000-->b  (4字节)
    0x2004-->c  (8字节)
    0x200c-->d  (4字节)
  对于函数void fun1(char a, int b, double c, short d)

  如果知道了参数a的地址,则要取后续参数的值则可以通过a的地址计算a后面参数的地址,然后取对应的值,而后面参数的个数可以直接由变量a指定,当然也可以像printf一样根据第一个参数中的%模式个数来决定后续参数的个数和类型。如果参数的个数由第一个参数a直接决定,则后续参数的类型如果没有变化并且是已知的,则我们可以这样来取后续参数, 假定后续参数的类型都是double;

void fun1(int num, ...)
{
    double *p = (double *)((&num)+1);
    double Param1 = *p;
    double Param2 = *(p+1);
    ...
    double Paramn  *(p+num);
}
  如果后续参数的类型是变化而且是未知的,则必须通过一个参数中设定模式来匹配后续参数的个数和类型,就像printf一样,当然我们可以定义自己的模式,如可以用i表示int参数,d表示double参数,为了简单,我们用一个字符表示一个参数,并由该字符的名称决定参数的类型而字符的出现的顺序也表示后续参数的顺序。 我们可以这样定义字符和参数类型的映射表,
i---int
s---signed short
l---long
c---char
"ild"模式用于表示后续有三个参数,按顺序分别为int, long, double类型的三个参数那么这样我们可以定义自己版本的printf 如下
void printf(char *fmt, ...)
{
    char s[80] = "";
    int paramCount = strlen(fmt);
    write(stdout, "paramCount = " , strlen(paramCount = ));
    itoa(paramCount,s,10);
    write(stdout, s, strlen(s));
    char *p = (char *)(&fmt) + sizeof(char *);
    int *pi = (int *)p;
    for (int i=0; i<paramCount; i++)
    {
        char line[80] = "";
        strcpy(line, "param");
        itoa(i+1, s, 10);
        strcat(line, s);
        strcat(line, "=");
        switch(fmt[i])
        {
            case 'i':
            case 's':
                itoa((*pi),s,10);
                strcat(line, s);
                pi++;
                break;
            case 'c':
                {
                    int len = strlen(line);
                    line[len] = (char)(*pi);
                    line[len+1] = '{post.abstract}';
                }
                break;
            case 'l':
                ltoa((*(long *)pi),s,10);
                strcat(line, s);
                pi++;
                break;
            default:
                break;
        }
    }
}
也可以这样定义我们的Max函数,它返回多个输入整型参数的最大值
int Max(int n, ...)
{
    int *p = &n + 1;
    int ret = *p;
    for (int i=0; i<n; i++)
    {
        if (ret < *(p + i))
            ret = *(p + i);
    }
    return ret;
}
可以这样调用, 后续参数的个数由第一个参数指定
int m = Max(3, 45, 12, 56);
int m = Max(1, 3);
int m = Max(2, 23, 45);

int first = 34, second = 45, third=5;
int m = Max(5, first, second, third, 100, 4);
结论

  对于可变参数函数的调用有一点需要注意,实际的可变参数的个数必须比前面模式指定的个数要多,或者不小于, 也即后续参数多一点不要紧,但不能少, 如果少了则会访问到函数参数以外的堆栈区域,这可能会把程序搞崩掉。前面模式的类型和后面实际参数的类型不匹配也有可能造成把程序搞崩溃,只要模式指定的数据长度大于后续参数长度,则这种情况就会发生。如:

printf("%.3f, %.3f, %.6e", 1, 2, 3, 4);
  参数1,2,3,4的默认类型为整型,而模式指定的需要为double型,其数据长度比int大,这种情况就有可能访问函数参数堆栈以外的区域,从而造成危险。但是printf("%d, %d, %d", 1.0, 20., 3.0);这种情况虽然结果可能不正确,但是确不会造成灾难性后果。因为实际指定的参数长度比要求的参数长度长,堆栈不会越界。


 
bakey @ 2008-07-08 11:39

到新公司上班已经2个多星期了,所见所想所得自然也并不少,日子过得也还算滋润,但是偶尔浮躁的思想也会像野草一样疯长。原本感觉自己毕业才一年就要换公司已经是很浮躁的表现了,虽然对原公司有种种不满,也算借个机会给自己逃离吧。但是我必须告诫自己,我只是一个普通本科的毕业生,工作经验也只有短短的一年时间,并没有太多资本在职场随心所欲地跳转,自己有几斤几两还是应该掂量得出来才对。现在应该做的应该是韬光养晦,安心努力地工作,等到把自己积累到一定的厚度,相信一定有厚积薄发的时候。这个社会实在是有太多的诱惑了,自己应该摒弃杂念,好好做好自己的事,找准自己的目标,等到时机到了,才能一击即中


 
bakey @ 2008-07-08 11:37

cin与cout 
一:标准输入函数cin 
    不知道说它是个函数对还是不对,它是代表标准的输入设备--键盘。他是属于流的,他的用法和流的用法是一样的。也就是:cin>>变量;
小小的说明一下,输入多个变量可以写在一行,如:cin>>x>>y>>z;
这样写不是不允许,而是不好看,如果是不同的变量类型,那就更是没头没脑了。除了你,人家是不知道该输入什么的,所以,一般在输入语句的前面,我们一般都
要做一个提示,请输入×××,让人家心里有个底,知道这个变量是做什么的。 
    另外,这个函数是不用带地址符号"&"的,也不用写明变量类型,千万不要跟scanf混淆。当然他就也不检查变量输入是否合法。如: 
int i; 
cout<<"please input a number:" 
cin>>i; 
cout<<"i="<<i<<endl; 
如果你输入的是一个字符如’a’那么他也不检查,但你输出的结果不是正确的,这要是手工进行检查。当然他也跟scanf一样,如果在循环内部输入不合法的变量值,那么也将陷入死循环。如下: 
/*一个输入不合法变量陷入死循环的例子*/ 
#include <iostream.h> 
main() 

     int i; 
     while(i!=-1) 
     { 
         cout<<"i=" 
         cin>>i;    /*请输入不是一个字符如’a’试试*/ 
         cout<<endl; 
     } 

    如上一个程序,如果你输入的不合法,那就将陷入死循环。解决的办法有个一,把cin>>i;语句移到判断循环的语句中,那么,你输入的如果是不合法的变量,他将跳出循环。 
    cin是用空格来分隔输入的。请看看如下的例子: 
/*一个空格分隔使输入的变量达不到希望的值*/ 
#include <iostream.h> 
main() 

     char str[20]; 
     cout<<"please input a string:"; 
     cin>>str;    /*你试着输入"hello word"*/ 
     cout<<endl<<"str="<<str; 

看得到是什么结果呢?得到的仅仅是str=hello,为什么呢?因为cin是以空格为分隔的,当你输入一个空格时,那他就认为后面的输入不属于这里了,
认为应该给后面的变量了。另外,当你输入的字符串大于分配的空间时,还会出现溢出现象。当然,还有整行输入的函数,包括空格也一起输入了,以后也会学到。 
二、标准输出函数cout 
    说cout是函数,也跟cin一样,不知道对不对。他代表的是标准输出设备--显示器。其实前面已经用过很多次这个函数了。我们就通过一个例子来进行格式化的输出就是了,大家就体会体会这个例子就行了,比printf灵活了很多。 
    首先,我们可以按16进制,8进制和10进制来显示我们的数据,如下: 
/*一个按进制输出的例子*/ 
#include<iostream.h> 
void main() 

    int x=30, y=300, z=1024; 
    cout<<x<<’ ’<<y<<’ ’<<z<<endl;                  //按十进制输出 
    cout.setf(ios::showbase | ios::uppercase);   //设置基指示符输出和数值中的字母大写输出 
    cout<<x<<’ ’<<y<<’ ’<<z<<endl; 
    cout.unsetf(ios::showbase | ios::uppercase); //取消基指示符输出和数值中的字母大写输出 
    cout.setf(ios::oct);                                    //设置为八进制输出,此设置不取消一直有效 
    cout<<x<<’ ’<<y<<’ ’<<z<<endl;                   //按八进制输出 
    cout.setf(ios::showbase | ios::uppercase);    //设置基指示符输出和数值中的字母大写输出 
    cout<<x<<’ ’<<y<<’ ’<<z<<endl; 
    cout.unsetf(ios::showbase | ios::uppercase); //取消基指示符输出和数值中的字母大写输出 
    cout.unsetf(ios::oct);                                 //取消八进制输出设置,恢复按十进制输出 
    cout.setf(ios::hex);                                    //设置为十六进制输出 
    cout<<x<<’ ’<<y<<’ ’<<z<<endl; 
    cout.setf(ios::showbase | ios::uppercase); //设置基指示符输出和数值中的字母大写输出 
    cout<<x<<’ ’<<y<<’ ’<<z<<endl; 
    cout.unsetf(ios::showbase | ios::uppercase); //取消基指示符输出和数值中的字母大写输出 
    cout.unsetf(ios::hex);                       //取消十六进制输出设置,恢复按十进制输出 
    cout<<x<<’ ’<<y<<’ ’<<z<<endl; 

我们用cout.setf()设置输出的格式,用cout.unsetf()取消格式。可以看出10进制在输出的时候不管有没有设置基指示符ios::
showbase,都没用,8进制再输出的时候在前面加0,而16进制是在前面加0X。而对于数值中字母大写输出,只对16进制有用,以后我们就应该看情
况使用了。当然,我们前面已经说了,还有一种方法也可以实现格式化输出,那就是使用操纵算子,如下, 
/*一个按进制输出的例子*/ 
#include<iomanip.h> 
void main() 

    int x=30, y=300, z=1024; 
    cout<<x<<’ ’<<y<<’ ’<<z<<endl;       //按十进制输出 
    cout<<oct<<x<<’ ’<<y<<’ ’<<z<<endl; //按八进制输出 
    cout<<setiosflags(ios::showbase);    //设置基指示符 
    cout<<x<<’ ’<<y<<’ ’<<z<<endl;       //仍按八进制输出 
    cout<<resetiosflags(ios::showbase); //取消基指示符 
    cout<<hex<<x<<’ ’<<y<<’ ’<<z<<endl; //按十六进制输出 
    cout<<setiosflags(ios::showbase | ios::uppercase); 
                  //设置基指示符和数值中的字母大写输出, 
    cout<<x<<’ ’<<y<<’ ’<<z<<endl; //仍按十六进制输出 
    cout<<resetiosflags(ios::showbase | ios::uppercase); 
                  //取消基指示符和数值中的字母大写输出 
    cout<<x<<’ ’<<y<<’ ’<<z<<endl; //仍按十六进制输出 
    cout<<dec<<x<<’ ’<<y<<’ ’<<z<<endl; //按十进制输出 



 
bakey @ 2008-04-08 17:26

由于经常要和汉字处理打交道,因此,我经常受到汉字编码问题的困扰。在不断的打击与坚持中,也积累了一点汉字编码方面的经验,想和大家一起分享。
一、汉字编码的种类
汉字编码中现在主要用到的有三类,包括GBK,GB2312和Big5。
1、GB2312又称国标码,由国家标准总局发布,1981年5月1日实施,通行于大陆。新加坡等地也使用此编码。它是一个简化字的编码规范,当然也包括其他的符号、字母、日文假名等,共7445个图形字符,其中汉字占6763个。我们平时说6768个汉字,实际上里边有5个编码为空白,所以总共有6763个汉字。
GB2312规定“对任意一个图形字符都采用两个字节表示,每个字节均采用七位编码表示”,习惯上称第一个字节为“高字节”,第二个字节为“低字节”。GB2312中汉字的编码范围为,第一字节0xB0-0xF7(对应十进制为176-247),第二个字节0xA0-0xFE(对应十进制为160-254)。

GB2312将代码表分为94个区,对应第一字节(0xa1-0xfe);每个区94个位(0xa1-0xfe),对应第二字节,两个字节的值分别为区号值和位号值加32(2OH),因此也称为区位码。01-09区为符号、数字区,16-87区为汉字区(0xb0-0xf7),10-15区、88-94区是有待进一步标准化的空白区。
2、Big5又称大五码,主要为香港与台湾使用,即是一个繁体字编码。每个汉字由两个字节构成,第一个字节的范围从0X81-0XFE(即129-255),共126种。第二个字节的范围不连续,分别为0X40-0X7E(即64-126),0XA1-0XFE(即161-254),共157种。

3、GBK是GB2312的扩展,是向上兼容的,因此GB2312中的汉字的编码与GBK中汉字的相同。另外,GBK中还包含繁体字的编码,它与Big5编码之间的关系我还没有弄明白,似乎是不一致的。GBK中每个汉字仍然包含两个字节,第一个字节的范围是0x81-0xFE(即129-254),第二个字节的范围是0x40-0xFE(即64-254)。GBK中有码位23940个,包含汉字21003个。

表1 汉字编码范围

名称     ¦ 第一字节                 ¦ 第二字节
-------- ¦------------------------- ¦------------------------

GB2312   ¦ 0xB0-0xF7(176-247)       ¦ 0xA0-0xFE(160-254)
-------- ¦------------------------- ¦-------------------------
GBK0     ¦ x81-0xFE(129-254)      ¦ 0x40-0xFE(64-254)
-------- ¦------------------------- ¦-------------------------
Big5     ¦ 0x81-0xFE(129-255)     ¦ 0x40-0x7E(64-126), ¦ ¦ 0xA1-0xFE(161-254)
-------- ¦------------------------- ¦------------------------


二、对汉字进行hash
为了处理汉字的方便,在查找汉字的时候,我们通常会用到hash的方法,那怎么来确定一个汉字位置呢?这就和每种编码的排列有关了,这里主要给出一种hash函数的策略。
对于GB2312编码,设输入的汉字为GBword,我们可以采用公式(C1-176)*94 (C2-161)确定GBindex。其中,C1表示第一字节,C2表示第二字节。具体如下:


GBindex = ((unsigned char)GBword.at(0)-176)*94 (unsigned char)GBword.at(1) - 161;
之所以用unsigned char类型,是因为char是一个字节,假如用unsigend int,因为int是4个字节的,所以会造成扩展,导致错误。
对于GBK编码,设输入的汉字为GBKword,则可以采用公式 index=(ch1-0x81)*190 (ch2-0x40)-(ch2/128),其中ch1是第一字节,ch2是第二字节。
具体的,
GBKindex = ((unsigned char)GBKword[0]-129)*190
((unsigned char)GBKword[1]-64) - (unsigned char)GBKword[1]/128;  

三、怎样判定一个汉字的是什么编码
直接根据汉字的编码范围判定,对于GB2312和GBK可用下面两个程序实现。
1、判定是否是GB2312
bool isGBCode(const string& strIn)
{
 unsigned char ch1;
 unsigned char ch2;

 if (strIn.size() > = 2)
 {
  ch1 = (unsigned char)strIn.at(0);
  ch2 = (unsigned char)strIn.at(1);
  if (ch1 >=176 && ch1 <=247 &&ch2 >=160 && ch2 <=254)


  return true;
  else return false;
 }
 else return false;
}
2、判定是否是GBK编码
bool isGBKCode(const string& strIn)
{
 unsigned char ch1;
 unsigned char ch2;

 if (strIn.size() > = 2)
 {
  ch1 = (unsigned char)strIn.at(0);
  ch2 = (unsigned char)strIn.at(1);
  if (ch1 >=129 && ch1 <=254 &&ch2 >=64 && ch2 <= 254)
  return true;
  else return false;
 } 
 else return false;
}

3、对于Big5
它的范围为:高字节从0xA0到0xFE,低字节从0x40到0x7E,和0xA1到0xFE两部分。判定一个汉字是否是BIG5编码,可以如上对字符的编码范围判定即可。如何定位呢?那么也想象所有编码排列为一个二维坐标,纵坐标是高字节,横坐标是低字节。这样一行上的汉字个数:(0x7E-0x40 1) (0xFE-0xA1 1)=157。那么定位算法分两块,为:
if 0x40 <=ch2 <=0x7E: #is big5 char
index=((ch1-0xA1)*157 (ch2-0x40))*2
elif 0xA1 <=ch2 <=0xFE: #is big5 char
index=((ch1-0xA1)*157 (ch2-0xA1 63))*2

对于第二块,计算偏移量时因为有两块数值,所以在计算后面一段值时,不要忘了前面还有一段值。0x7E-0x40 1=63。
四、假如判定一个字符是西文字符还是中文字符
大家知道西文字符主要是指ASCII码,它用一个字节表示。且这个字符转换成数字之后,该数字是大于0的,而汉字是两个字节的,第一个字节的转化为数字之后应该是小于0的,因此可以根据每个字节转化为数字之后是否小于0,判定它是否是汉字。
例如,设输入字为strin,则,
If (strin.at(0) < 0)
cout < < ”是汉字” < < endl;
else cout < < ”不是汉字” < < endl;

 

 


 
bakey @ 2008-04-08 15:56

Valgrind是一个GPL的软件,用于Linux(For x86, amd64 and ppc32)程序的内存调试和代码剖析。你可以在它的环境中运行你的程序来监视内存的使用情况,比如C 语言中的malloc和free或者 C++中的new和 delete。使用Valgrind的工具包,你可以自动的检测许多内存管理和线程的bug,避免花费太多的时间在bug寻找上,使得你的程序更加稳固。

Valgrind的主要功能

Valgrind工具包包含多个工具,如Memcheck,Cachegrind,Helgrind, Callgrind,Massif。下面分别介绍个工具的作用:

Memcheck 工具主要检查下面的程序错误:

  • 使用未初始化的内存 (Use of uninitialised memory)
  • 使用已经释放了的内存 (Reading/writing memory after it has been free’d)
  • 使用超过 malloc分配的内存空间(Reading/writing off the end of malloc’d blocks)
  • 对堆栈的非法访问 (Reading/writing inappropriate areas on the stack)
  • 申请的空间是否有释放 (Memory leaks – where pointers to malloc’d blocks are lost forever)
  • malloc/free/new/delete申请和释放内存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])
  • src和dst的重叠(Overlapping src and dst pointers in memcpy() and related functions)

Callgrind

Callgrind收集程序运行时的一些数据,函数调用关系等信息,还可以有选择地进行cache 模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate可以把这个文件的内容转化成可读的形式。

Cachegrind

它模拟 CPU中的一级缓存I1,D1和L2二级缓存,能够精确地指出程序中 cache的丢失和命中。如果需要,它还能够为我们提供cache丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。

Helgrind

它主要用来检查多线程程序中出现的竞争问题。Helgrind 寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。Helgrind实现了名为” Eraser” 的竞争检测算法,并做了进一步改进,减少了报告错误的次数。

Massif

堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。Massif能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。

Valgrind 安装

1、 到www.valgrind.org下载最新版valgrind-3.2.3.tar.bz2
2、 解压安装包:tar –jxvf valgrind-3.2.3.tar.bz2
3、 解压后生成目录valgrind-3.2.3
4、 cd valgrind-3.2.3
5、 ./configure
6、 Make;make install

Valgrind 使用

用法: valgrind [options] prog-and-args [options]: 常用选项,适用于所有Valgrind工具

  1. -tool=<name> 最常用的选项。运行 valgrind中名为toolname的工具。默认memcheck。
  2. h –help 显示帮助信息。
  3. -version 显示valgrind内核的版本,每个工具都有各自的版本。
  4. q –quiet 安静地运行,只打印错误信息。
  5. v –verbose 更详细的信息, 增加错误数统计。
  6. -trace-children=no|yes 跟踪子线程? [no]
  7. -track-fds=no|yes 跟踪打开的文件描述?[no]
  8. -time-stamp=no|yes 增加时间戳到LOG信息? [no]
  9. -log-fd=<number> 输出LOG到描述符文件 [2=stderr]
  10. -log-file=<file> 将输出的信息写入到filename.PID的文件里,PID是运行程序的进行ID
  11. -log-file-exactly=<file> 输出LOG信息到 file
  12. -log-file-qualifier=<VAR> 取得环境变量的值来做为输出信息的文件名。 [none]
  13. -log-socket=ipaddr:port 输出LOG到socket ,ipaddr:port

LOG信息输出

  1. -xml=yes 将信息以xml格式输出,只有memcheck可用
  2. -num-callers=<number> show <number> callers in stack traces [12]
  3. -error-limit=no|yes 如果太多错误,则停止显示新错误? [yes]
  4. -error-exitcode=<number> 如果发现错误则返回错误代码 [0=disable]
  5. -db-attach=no|yes 当出现错误,valgrind会自动启动调试器gdb。[no]
  6. -db-command=<command> 启动调试器的命令行选项[gdb -nw %f %p]

适用于Memcheck工具的相关选项:

  1. -leak-check=no|summary|full 要求对leak给出详细信息? [summary]
  2. -leak-resolution=low|med|high how much bt merging in leak check [low]
  3. -show-reachable=no|yes show reachable blocks in leak check? [no]

Valgrind 使用举例

下面是一段有问题的C程序代码test.c

#include <stdlib.h>
void f(void)
{
int* x = malloc(10 * sizeof(int));
x[10] = 0; //问题1: 数组下标越界
} //问题2: 内存没有释放
int main(void)
{
f();
return 0;
}
1、 编译程序test.c
gcc -Wall test.c -g -o test
2、 使用Valgrind检查程序BUG
valgrind --tool=memcheck --leak-check=full ./test
3、 分析输出的调试信息
==3908== Memcheck, a memory error detector.
==3908== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==3908== Using LibVEX rev 1732, a library for dynamic binary translation.
==3908== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==3908== Using valgrind-3.2.3, a dynamic binary instrumentation framework.
==3908== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==3908== For more details, rerun with: -v
==3908==
--3908-- DWARF2 CFI reader: unhandled CFI instruction 0:50
--3908-- DWARF2 CFI reader: unhandled CFI instruction 0:50
/*数组越界错误*/
==3908== Invalid write of size 4
==3908== at 0x8048384: f (test.c:6)
==3908== by 0x80483AC: main (test.c:11)
==3908== Address 0x400C050 is 0 bytes after a block of size 40 alloc'd
==3908== at 0x40046F2: malloc (vg_replace_malloc.c:149)
==3908== by 0x8048377: f (test.c:5)
==3908== by 0x80483AC: main (test.c:11)
==3908==
==3908== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 14 from 1)
==3908== malloc/free: in use at exit: 40 bytes in 1 blocks.
==3908== malloc/free: 1 allocs, 0 frees, 40 bytes allocated.
==3908== For counts of detected errors, rerun with: -v
==3908== searching for pointers to 1 not-freed blocks.
==3908== checked 59,124 bytes.
==3908==
==3908==
/*有内存空间没有释放*/
==3908== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==3908== at 0x40046F2: malloc (vg_replace_malloc.c:149)
==3908== by 0x8048377: f (test.c:5)
==3908== by 0x80483AC: main (test.c:11)
==3908==
==3908== LEAK SUMMARY:
==3908== definitely lost: 40 bytes in 1 blocks.
==3908== possibly lost: 0 bytes in 0 blocks.
==3908== still reachable: 0 bytes in 0 blocks.
==3908== suppressed: 0 bytes in 0 blocks.
 


 
bakey @ 2008-03-31 23:29

首先使用cronolog来格式化apache的log(变成这种形式:access_log.20080331)。
下载地址:http://cronolog.org/download/index.html
2.解压缩
gzip -d cronolog-1.6.2.tar.gz
tar xf cronolog-1.6.2.tar
2.进入相应的目录 ./configure
3.make
4.make install
5.修改apache配置文件
自定义日志格式
CustomLog "|/usr/local/sbin/cronolog /usr/local/apache2/logs/access_log.%Y%m%d" combined
安装awstats(将awstats安装在/var/lib/awstats目录)
mkdir -p /var/lib/awstats
cd /opt
wget http://www.awstats.cn/files/awstats-6.6.tar.gz
tar zxvf awstats-6.6.tar.gz
mv awstats-6.6 awstats
cd awstats/tools/
perl awstats_configure.pl

安装的过程会要求输入一些信息,按照提示来作就可以了。安装结束后要配置一下

修改awstats.bakey.conf配置
vi /etc/awstats/awstats.sina.conf

按?,在之后输入要搜索的内容LogFile="
然后按Ins键,找到LogFile="/var/log/httpd/access_log"
改为要分析的Apache日志路径与文件名。

(1)多日志合并分析(例:新浪播客其中两台服务器2月6日的日志30.0206.vblog.log与31.0206.vblog.log)
LogFile="/opt/awstats/tools/logresolvemerge.pl /var/apachelogs/30.0206.vblog.log /var/apachelogs/31.0206.vblog.log|"

LogFile="/opt/awstats/tools/logresolvemerge.pl /var/apachelogs/*.0206.vblog.log|"

(2)分析使用gzip压缩过的日志文件
LogFile="gzip -d </var/log/apache/access.log.gz|"

更新分析报告
perl /opt/awstats/wwwroot/cgi-bin/awstats.pl -config=bakey -update

查看分析报告

awstats的脚本和静态文件缺省都在wwwroot目录下: 将cgi-bin目录下的文件都部署awstats.pl程序到/home/apache/cgi-bin/awstats/ 下
mv awstats-version/wwwroot/cgi-bin /usr/local/apache2/cgi-bin/awstats
#把图标等文件目录复制到WEB的HTML文件发布目录下:/home/apache/htdocs/ 下发布
更多的批量更新脚本等在tools 目录下,可以一并放到cgi-bin/awstats/ 目录下AWStats的主程序awstats.pl会自动根据站点名调用相应站点的配置文件:awstats.sitename.conf
比如:运行./awstats.pl -config=chedong 调用的就是同目录下的 awstats.chedong.conf 配置文件;
如果没有指定-config,还会找当前目录下的awstats.conf或者/etc/awstats.conf作为缺省配置文件。
所以最好把缺省的awstats.model.conf 重命名成 awstats.yoursite.conf;比如:awstats.chedong.conf
./awstats.pl -update -config=sitename -lang=cn

统计输出

GNU/Linux    http://localhost/cgi-bin/awstats/awstats.pl?config=chedong
自动定时更新的话修改crontab就可以了哦

为了提高安全性,可以设置访问认证:

在httpd.conf文件中配置需要认证的目录:
< Directory "/opt/apache/www.eygle.com/cgi-bin/awstats/" > 
AuthType Basic
AuthName "Restricted Files"
AuthUserFile /opt/apache/passwd/passwords
Require user awsuser
< /Directory >

其中在 /opt/apache/passwd/下产生passwords 的密码文件
运行如下代码产生用户名为awsuser用户及其密码

/opt/apache/bin/htpasswd -c /opt/apache/passwd/passwords awsuser
注意:先要在 /opt/apache/下建passwd目录


 
bakey @ 2008-03-29 09:10

第一種︰

shell> mysql -u root mysql mysql> UPDATE user SET Password=PASSWORD(’new_password’) WHERE user=’root’; mysql> FLUSH PRIVILEGES;

第二種︰使用 set password 語法

shell> mysql -u root mysql mysql> SET PASSWORD FOR root=PASSWORD(’new_password’);

第三種︰使用 mysqladmin命令

shell>mysqladmin -u root password new_password



 
bakey @ 2008-03-18 10:58

安装版本mysql-5.0.40 需编译

  下载编译需要的一些工具,这些工具在默认安装的ubuntu上可能没有,需要手动安装

  sudo apt-get install g++ gcc make automake perl

  安装好所有需要的工具

  下载需要的libncurses5-dev、kdelibs_dev 和 kdelib

  sudo apt-get update

  apt-get install build-essential

  sudo apt-get install libncurses5-dev

  sudo apt-get install kdelibs4-dev

  sudo apt-get install kdelibs4c2a

  安装mysql,使用root权限

  shell> groupadd mysql

  shell> useradd -g mysql mysql

  shell> gunzip < mysql-VERSION.tar.gz | tar -xvf -

  shell> cd mysql-VERSION

  shell> ./configure --prefix=/usr/local/mysql

  shell> make

  shell> make install

  shell> cp support-files/my-medium.cnf /etc/my.cnf

  shell> cd /usr/local/mysql

  shell> chown -R mysql .

  shell> chgrp -R mysql .

  shell> bin/mysql_install_db --user=mysql

  shell> chown -R root .

  shell> chown -R mysql var

  shell> bin/mysqld_safe --user=mysql &




 
日历
最新的评论
· 07/11 互相学习 /握...
· 07/09 我刚工作,以后...
站内搜索
友情链接
· 我的歪酷 非非共享界
· polly
· 高性能网络编程maillist

订阅 RSS

0018327

歪酷博客