gdb调试器快速入门

21 7 月, 2010 by edsionte 1 comment »

一直不会使用gdb调试器,主要原因是它不像windows下那些编译环境有良好的图形界面,所以一再的排斥gdb调试器。遇到问题也是盲目寻找。下午花了些时间专门看了gdb调试器的相关资料,其实上手一点都不困难。本文通过FQA的方式让你快速入门gdb调试器。

1.如何启动gdb调试器呢?

在终端输入 gdb 程序文件名 即可。注意gdb调试的是可执行文件,而不是源代码。因此此处的文件名应该是可执行程序文件名。成功进入gdb后会显示一大段文字说明,然后是gdb提示符:(gdb)

请注意在进入gdb之前应该按照如下方式编译源程序:

gcc -g test_gdb.c -o test_gdb

只有加入选项-g才能被gdb调试。

2.除了上述方法,还有其他方法进入gdb吗?

直接在终端输入:gdb,成功进入后,使用file命令装入要调试的程序。输入: file 程序文件名 即可。

3.我成功进入了gdb,如何退出呢?

使用quit命令,输入: quit 即可。

4.进入gdb后,我要查看源代码必须退出才能查看吗?

当然不用。下面的命令可以帮助你快速查看源码。

list: 显示10源代码,再次输入该命令显示接下来的10行。

list 1,10:显示从第一行到第10行的代码。

list 函数名:显示此函数名周围的代码。

5.gdb可以设置断点吗?如何设置?

在gdb中最简单的设置方式是:break 行号 在这一行设置断点。比如break 9 会在代码的第9行设置断点。当程序执行到第9行会自动暂停,此时,第9行代码还未执行。

你也可以使用:break 函数名 的方式在某个函数处设置断点,程序运行到这个函数内的第一条语句处会自动暂停。

你也可以这样设置断点:break 行号或函数名 if 条件 。它很好理解,当满足if条件语句时才会在指定的行号或者函数名处断点。

6.我发现我把断点设置错了,如何消除断点?

使用命令:clear 行号 即可删除。

7.我已经成功设置了断点,可是我如何运行程序以便让它在断点处暂停?

输入:run 即可。程序自动停止在第一个断点处。

8.我的程序运行的时候需要加参数,我还能继续使用run命令运行程序吗?

当然可以,只不过你要在run后加上你的参数,参数间用空格隔开。如:run 参数1 参数2  …..

9.有时候我并不确定程序具体哪一句有问题,我如何一步一步的查看语句?

两种命令:nextstep。两者均可以一句一句的查看语句。但不同的是,next命令将函数调用看作一条语句,而step则会进入函数,一步步的执行函数内的代码。

10.如何让暂停的代码继续运行?

输入命令:continue。它可以让程序继续运行,直到程序运行完毕或者遇到下一个断点为止。

11.当程序在断点处暂停执行时,如何查看当前变量的值?

使用print命令。具体如下:

print 命令或者表达式:显示变量或表达式的值。

print 变量=值:为变量赋值。

以上命令属于基本的调试命令,更多的命令可以参考这本书

熟悉以上命令便可以快速入门gdb,要熟练的使用调试器,当代码遇到问题时,多调试即可。

线程ID的输出格式

20 7 月, 2010 by edsionte 无评论 »

Last Update:8/15

在获取线程ID的时候,常用到pthread_self()函数获取当前线程的ID,它返回的ID是pthread_t类型的。如果要打印出线程ID,用什么输出格式呢?

此书中例题8_1中使用的是%u,即unsigned int,但是编译时候有一个warning:

format ‘%u’ expects type ‘unsigned int’, but argument 2 has type ‘pthread_t’。

其实线程ID的格式应该是unsigned long,所以输出格式为:%lu。再次编译就对了。linux中还有一些ID类的数据类型,比如常见的pid,它的输出格式为%d;

再比如文件状态结构体struct stat *buf中的st_ino字段,它的类型为ino_t,而输出格式为%lu。在内核的inode结构体(include/linux/fs.h)中可以找到这个字段的最终面目:unsigned long i_ino;它便是索引结点号。

my_shell.c代码分析

16 7 月, 2010 by edsionte 无评论 »

大三的时候,那时候因为课程需要,我们调试过my_shell.c程序。当时只是对整个程序的结构了解,在涉及linux系统调用函数的时候就不是很清楚”为什么会这样做?”。my_shell.c程序核心函数便是do_cmd函数,此函数是整个程序的核心,负责对用户输入的命令进行执行。

本文便是分析do_cmd函数,以达到对my_shell.c程序有深入的理解。此函数会涉及文件操作中的相关系统调用函数,如果你和我一样调试并分析过前面所说的my_ls.c程序,那么接下来所述的内容对你来说并不困难。

我们按照如下方式定义do_cmd函数:

void do_cmd(int argcount, char arglist[100][256]);

其中,argcount是统计用户输入命令中选项的个数,arglist数组存储每个选项。比如输入:ls -l /那么此时argcount=3,而arglist[0],arglist[1],arglist[2]中分别存储的是:ls,-l,/。

首先此函数会检查用户输入的命令行参数中是否存在后台运行符。利用for循环,对arglist数组中的每个参数与”&”进行比较。由于arglist中的元素是字符串,那么应该选用strcmp函数,而不是利用==来判断。此外即便找到了后台运行符,还应该检查是否”&”位于参数末尾。如果i==argcount-1为假,那么便出错。实现代码如下:

//check if the command include character of running in the background
	for(i=0;i<\argcount;i++)
	{
		if(strncmp(arg[i],"&",1)==0)
		{
			if(i==argcount-1)
			{
				background=1;
				arg[argcount-1]=NULL;
				break;
			}
			else
			{
				printf("wrong command\n");
				return;
			}
		}
	}//for

接下来,检测命令行参数是否含有>,<,|符号,检测方法与上出代码原理类似。由于本程序要求用户输入的命令行参数只能包含上述符号其中之一,所以当flag大于一时,错误。参数中符号合法,接下来提取文件名,存于字符串file中。程序中分别有三个if语句依次针对出现>,<,|时的情况。我们只要分析出现管道符号的情况。

在分析之前首先弄起出管道符号的作用。比如下面命令:

edsionte@edsionte-laptop:~$ ls -l / | wc -c
1513

管道符号前后分别是两个shell命令,前命令的输出作为后命令的输入。

如果检测出命令行中含有管道符号,即how=have_pipe。然后将命令行中两个shell命令分离出来,前命令依然存于arg数组中,而后命令存于argnext数组中。实现命令如下:

if(how==have_pipe)
	{
		for(i=0;arg[i]!=NULL;i++)
		{
			if(strncmp(arg[i],"|",1)==0)
			{
				arg[i]=NULL;
				int j;
				for(j=i+1;arg[j]!=NULL;j++)
				{
					argnext[j-(i+1)]=arg[j];
				}
				argnext[j-(i+1)]=arg[j];
				break;
			}
		}
	}

接下来就要用到进程控制的内容了。我们fork一个新的进程,让它去执行命令行参数中的命令。当命令行中不包含管道符号时,只需fork一个进程,比如ls -l / > a;我们只需fork一个新进程,让其(if(pid==0))执行ls命令即可;否则,我们必须fork两个信进程,前一个进程执行|前的命令,另外一个进程执行|后的命令。至于另个进程如何”通信”,如何让前一个命令的输出称为后者的输入?不着急,后面会详细说明。

1.如果所输命令中不包含<,>,|,how==normal(0);那么调用exe族函数即可让子进程去执行所输的命令。如下:

			if(pid==0)
			{
				if(!(find_command(arg[0])))
				{
					printf("%s: command not found\n",arg[0]);
					exit(0);
				}
				execvp(arg[0],arg);
				exit(0);
			}

find_command函数是在指定目录下去查找命令arg[0]对应的可执行程序。

2.如果命令行中包括输出重定向符号>,how==out_redirect(1);那么需在上述代码中exe函数前添加下面的代码:

				fd=open(file,O_RDWR|O_CREAT|O_TRUNC,0644);
				dup2(fd,1);//make the file as a standard output stream
				execvp(arg[0],arg);//execute the command

前面我们已经提到file中存储的是输出重定向(或输入重定向)的文件名,因此首先应以可读可写方式打开一个文件,并且档要打开的文件存在时,会清空源文件数据,这些我们在前面的博文中都有解释。我们知道标准输出输入的文件描述符为:1,0,利用dup2函数就可以将fd,1同时指向file文件,实现将标准输出重定向到我们刚已打开的文件。

3.如果命令行中包含输入重定向符号<,how==out_redirect(2);那么添加如下代码:

				fd=open(file,O_RDONLY);
				dup2(fd,0);//make the file as a standard output stream
				execvp(arg[0],arg);//execute the command

这里只涉及到读取文件,因此以只读方式打开文件。

4.如果命令行中包含管道符号,how==have_pipe(3);如同我们在上面所述的,子进程还需再fork一个进程(暂且叫它子子进程,child_child_process)。首先让子子进程去执行管道符号前面的shell命令,并将输出结果暂存于tempfile文件中;让子进程去执行管道符号后面的shell命令,并读取tempfile文件,将其作为第二个命令的输入。这样,子进程和子子进程实现了管道”通信”。具体代码实现其实是上述2和3的结合,具体如下:

			if(pid==0)//child_process
			{
				int pid2;
				int status2;
				int fd2;

				if((pid2=fork())<\0)
				{
					printf("fork2 error\n");
					return;
				}
				else if(pid2==0)//child_child_process
				{
					if(!(find_command(arg[0])))
					{
						printf("%s: command not found\n",arg[0]);
						exit(0);
					}
					fd2=open("/tmp/tempfile",O_WRONLY|O_CREAT|O_TRUNC,0644);
					dup2(fd2,1);
					execvp(arg[0],arg);
					exit(0);
				}

				if(waitpid(pid2,&status2,0)==-1)//waiting for child_child_process
				{
					printf("wait for child_child process error\n");
				}

				if(!(find_command(argnext[0])))
				{
					printf("%s:command not found\n",argnext[0]);
					exit(0);
				}

				fd2=open("/tmp/tempfile",O_RDONLY);
				dup2(fd2,0);
				execvp(argnext[0],argnext);

				if(remove("/tmp/tempfile"))
				{
					printf("remove error\n");
				}
				exit(0);
			}

上出1~4处代码需要一个switch语句来组织,根据how参数来选择相应代码,进行执行。另外,由于上述代码中设计两次fork进程,因此要弄清楚父子进程的关系,其次还需了解代码4中waitpid函数的作用。由于子子进程和子进程同组,因此子进程等待子子进程结束。

同样,父进程需要等待子进程执行完毕。但是如果命令行中包含后台运行父&,那么附近成可直接返回,不用等待子进程。这些功能的实现代码如下:

	if(background==1)
	{
		printf("process id %d\n",pid);
	}

	if(waitpid(pid,&status,0)==-1)
	{
		printf("wait for child process error\n");
	}

至此,我们分析完了do_cmd函数的功能,完整代码可以参考这里。

丰富你的ls(updating……)

3 7 月, 2010 by edsionte 无评论 »

如果我们已经对my_ls.c的结构有了深刻了解,以及对此程序中所涉及系统调用函数都已理解,那么不妨继续增添选选型,让你的ls命令程序不断完整。正如你所理解的那样,前面我们实现的my_ls.c属于一个“插座”,要实现其他选项,基本上都是在my_ls.c源码中添加新代码。我们将不断完善选项功能的那个源码称为my_ls_plus.c。

1.增加-i选项

ls -i可以显示出每个文件的i节点编号。具体的实现办法是利用stat族函数,将文件的属性返回到类型为struct stat的参数buf中,然后在需要的位置打印st_ino字段即可。注意st_ino打印的时候需要长整型。

获取文件属性举例:

if(lstat(pathname,&buf)==-1)
	{
		my_err("lstat",errno,__LINE__);
	}

打印举例:

if(if_i)
	{
		printf("%ld ",buf.st_ino);
	}

2.增加-t选项

ls -t 按每个文件最后一次被修改的时间(st_mtime)排列显示文件。这里对文件属性中三个关于时间的字段做以说明。

st_atime:文件最后一次被访问(access)的时间。这个好理解。

st_mtime:文件最后一次被修改(modification)的时间。

st_ctime:文件字后一次被更改(create)的时间。

mtime和ctime其实都是修改时间,但是前者指文件内容被修改后的时间,而后者更倾向于文件所有者、所属组以及文件权限等属性被更改后的时间。当然如果文件内容修改,两者的时间都会被修改。我们一般用ls -l所显示的时间指mtime,而要显示出ctime则可以用: ls -lc。显示出atime则可以用:ls -lu。

因此,要实现ls -t命令的作用,我们必须对文件按照mtime进行排序。我们可以设置全局变量if_t来标记是否出现了 -t选项。如果出现则调用按照时间排序的函数sort_by_mtime(char filename[][PATH_MAX+1],int count);此函数的大致流程是:首先利用stat族函数获取每个文件的mtime,存于字符串数组mtime[256][20]中;再利用某种排序算法排序即可。如果你对C语言基本知识掌握的还算可以,那么要实现上述过程并不困难。

获取文件的mtime示例代码如下:(由于代码插件问题,请忽略<后面的‘\’)

for(i=0;i<\count;i++)
	{
		if(stat(filename[i],&buf)==-1)
		{
			my_err("stat",errno,__LINE__);
		}
		strcpy(buf_time,ctime(&buf.st_mtime));
		buf_time[strlen(buf_time)-1]='\0';
		convert_time(mtime[i],buf_time);
	}

下面的排序代码采用选择排序法进行排序,注意我们在比较的时候必然是对时间进行比较,而在交换两个数据的时候,必须对文件名和时间一起交换。当初我只交换了文件名,找了好久的”错误”。

//sorting
	for(i=0;i<\count;i++)
	{
		k=i;
		for(j=i;j<\count;j++)
               {
                        if(strcmp(mtime[j],mtime[k])>\0) k=j;
		}
		if(k!=i)
		{
			//exchange mtime
			strcpy(temp,mtime[i]);
			strcpy(mtime[i],mtime[k]);
			strcpy(mtime[k],temp);
			//exchange filename
			strcpy(temp,filename[i]);
			strcpy(filename[i],filename[k]);
			strcpy(filename[k],temp);
		}
	}

这样,你就可以实现-t选项的功能。

3.增加-A选项

这个选项理解比较简单。我们知道ls -a是显示包括隐藏文件在内的所有文件。而ls -A则显示除了.和..之外的所有文件。我们知道在my_ls.c中void display(int flag,char* pathname)是根据flag对单个文件pathname进行处理,根据flag的情况来判断是以什么方式显示所有文件名。因为我们可以在此基础上加入对-A选项的判断:

if((if_A&&!strcmp(pathname,"."))||(if_A&&!strcmp(pathname,"..")))
	{
		//blank sentence,do not list this file
	}
	else
	{
		//省略switch语句
	}

这样就可以实现-A选项了。

4.增加-B选项

如果你完成了-A选项,那么此选项对你来说轻而易举。-B选项是不显示以~结尾的备份文件。那么只要在-A选项的基础上修改源代码,示例如下:

if((if_A&&!strcmp(pathname,"."))||(if_A&&!strcmp(pathname,"..")))
	{
		//blank sentence,do not list this file
	}
	else if(if_B&&pathname[strlen(pathname)-1]=='~')
	{
		//blank sentence,do not list this file
	}
	else
	{
		//switch语句省略
        }

5.增加-d选项

-d选项只会显示当前目录(.),而不是目录下的文件。如果加入-l则会显示当前目录的各种属性。前面的-B等选项我们在添加的时候都是在display函数中添加某些代码,因为最终结果还要与-a和-l选项相结合。比如ls -laA,虽然不显示.和..,但是还是要显示以.开始的其他文件。而-d选项则不用看-a的“脸色‘。因为不论你是否加入-a选项,此选项只会显示当前目录。如下:

edsionte@edsionte-laptop:~/code/file$ ls -laAd
drwxr-xr-x 2 edsionte edsionte 4096 2010-07-06 14:50 .
edsionte@edsionte-laptop:~/code/file$ ls -ld
drwxr-xr-x 2 edsionte edsionte 4096 2010-07-06 14:50 .
edsionte@edsionte-laptop:~/code/file$ ls -d
.

因此关于此选项的代码应添加在display_dir函数中,具体位置如下:

//if the command include '-d'
	if(if_d)
	{
		if(stat(".",&buf)<\0)
		{
			my_err("stat",errno,__LINE__);
		}
		if(flag_param==PARAM_L||flag_param==PARAM_L+PARAM+A)
			display_attribute(buf,".");
		else
			display_single(buf,".");
	}
	else
	{
		for(i=0;i<\count;i++)
        	{
			display(flag_param,filename[i]);
        	}
	}

这样添加即可,通过以上几个选项的添加,你应该大致了解应该在何处添加代码了。

待续。。

creat只能以只写方式打开文件

1 7 月, 2010 by edsionte 无评论 »

在《linuxC编程实战》书中,有一个my_rwl.c的小程序(详见P151):首先利用open函数或者creat函数创建一个文件,利用write函数将数据写入文件,再利用read函数读出文件中数据;用lseek移动文件指针,再写入相同的数据,最终读出所有数据。创建代码如下:

// if((fd=creat("example_63.c",S_IRWXU))==-1)
	if((fd=open("example_63.c",O_CREAT|O_TRUNC|O_RDWR,S_IRWXU))==-1)
	{
		my_err2("creat",errno,__LINE__);
	}

在调试此程序的时候,总是出现这样的情况:利用open函数创建文件的时候,一些正常。利用creat函数创建文件的时候,读出的数据总是乱码,并且提示读写失败的错误信息。在自己寻求结果无望的时候,在chinaUnix论坛得到了帮助:creat只能以只写方式打开文件。其实书上有这句话,可是在我寻求上述错误答案的时候,也没有注意到它的存在。而且我还有有这样的错误概念:即便creat函数只能以只写方式打开文件,可是我创建的时候有S_IRWXU参数,那么它便可以写了。

那么,让我重新理解它:

1.文件的打开方式是指文件以只读、只写、可读写那种方式打开,返回的文件描述符就可以对此文件进行相应的读写操作。比如上述的creat函数对example_63.c以只写方式打开,那么就只能对这个文件进行写,因此后续的读操作都失败。而open创建文件的时候,有O_RDWR参数,因此可以成功对文件读写。

2.文件的操作权限是指系统中各用户对此文件的使用权限,依参数而定。

3.以创建的角度来看,creat和open的作用相同。但以打开文件的方式来看,前者只能以只写方式,后者由于参数的关系,以可读写方式打开。

如果要解决上述的错误,那么可以在creat创建完文件后,关闭文件,再以可读写的方式,open这个文件。这么做是很麻烦,但是这里我们为了搞清楚两者的区别,也无所谓了。

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key