GCC,GDB,Makefile和IO复用函数

时间:2023-12-15 23:23:02

2015.1.22

c高级的环境搭建:
GCC编译器:
全称 GNU CC,是GNU工具(tool chain)的一种,源码编译成机器码,gcc的编译依赖于很多小工具
4.3.3和3.4.3版本的比较稳定

GCC编译分为四个步骤:(用WC命令可以分别查看每个阶段代码的大小,可以比较一下,ls -l 也能看出大小)
1.预处理 ->cpp预处理文件*.i gcc -E
2.编译 ->cc1汇编文件*.s gcc -S
3.汇编 ->as汇编文件*.o gcc -c
4.链接 ->ld可执行文件*.exe (将多个.o文件组合成可执行的应用程序,连接器需要知道这种目标格式,以便工作)
gcc -O或者-O2是编译优化的
gcc -I dir 指定添加的头文件目录,默认在/usr/include/
lib + 库名.a

readelf hello -h 显示可执行文件的信息
strip 丢弃目标文件的全部或者特定符号,减小文件体积,发布代码的时候可以用

交叉编译,是在一种计算机环境中运行的编译程序,能编译出在另外一种环境下运行的代码,我们就称这种编译器支持交叉编译。
这个编译过程就叫交叉编译。
比如:arm—cortex-a8,arm-linux-gcc
GDB调试工具:能对执行程序进行源码或者汇编级调试
root@lg-desktop:~/test/gdb# gcc -g e.c -o e 生成调试信息
root@lg-desktop:~/test/gdb# gdb e 进入调试界面
GNU gdb (GDB) 7.1-ubuntu
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/test/gdb/e...done.
(gdb) l (l查看文件,每次只能看10行)
1 #include <stdio.h>
2 #include <string.h>
3 #define N 50
4 int main()
5 {
6 int t;
7 char s1[N];
8 char s2[N];
9
10 printf(">");
(gdb) l
11 scanf("%s%s",s1,s2);
12 t = strcmp(s1,s2);
13 if ( 0 == t)
14 printf("s1 = s2\n");
15 else if(t > 0)
16 printf("s1 > s2\n");
17 else
18 printf("s1 < s2\n");
19 return 0;
20
(gdb) l
21 }
(gdb) b 11 在11行设置断点
Breakpoint 1 at 0x4006b8: file e.c, line 11.
(gdb) info b 查看断点信息
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004006b8 in main
(gdb) r 运行,会停在第一个断点处
Starting program: /root/test/gdb/e

Breakpoint 1, main () at e.c:11
11 scanf("%s%s",s1,s2);
(gdb) n 单步运行
>2 3
12 t = strcmp(s1,s2);
(gdb) s 单步运行
0x00007ffff7adc5a0 in strcmp () from /lib/libc.so.6
at e.c:11
(gdb) p t 查看变量t的值,
$1 = 0
(gdb) c 恢复运行
Continuing.
s1 > s2

Program exited normally.
(gdb) help 获取帮助
List of classes of commands:

aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands

Type "help" followed by a class name for a list of commands in that class.
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) q (q退出)
root@lg-desktop:~/test/gdb#

调试的一段程序,总结下:
数组要特别注意下标的范围 注意!注意!注意!

1 #include<stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 int display1(char *string);
6 int display2(char *string);
7
8 int main(int argc,char **argv)
9 {
10 char string[]="Embedded Linux";
11 display1(string);
12 display2(string);
13
14 }
15 int display1(char *string)
16 {
17 printf("the original string is %s\n",string);
18 }
19
20 int display2(char *string1)
21 {
22 char *string2;
23 int size,i;
24 size = strlen(string1); //数组长度为14
25 string2 = (char *)malloc(size+1);
26 for(i = 0;i < size;i++)
27 string2[size-i] = string1[i]; // 应该是string2[size-1-i] = string1[i];
28 string2[size+1] = '\0'; // 应该是string2[size] = '\0';
29 printf("the string afterward is %s\n",string2);
30 free(string2);
31 }

源程序运行结果:
root@lg-desktop:~/test/gdb# gcc gdb.c -o gdb -g
root@lg-desktop:~/test/gdb# ./gdb
the original string is Embedded Linux
the string afterward is //这里得不到真确的结果
root@lg-desktop:~/test/gdb#

开始调试:
Reading symbols from /root/test/gdb/gdb...done.
(gdb) b 30 设置断点
Breakpoint 1 at 0x400794: file gdb.c, line 30.
(gdb) r
Starting program: /root/test/gdb/gdb
the original string is Embedded Linux
the string afterward is

Breakpoint 1, display2 (
string1=0x7fffffffe5d0 "Embedded Linux") at gdb.c:30
30 free(string2);
(gdb) n
31 }
(gdb) p string2[0]
$1 = 0 '\000' //字符传首地址为的值为\0,不是想要的

修改原函数:
27 string2[size-i] = string1[i]; // 应该是string2[size-1-i] = string1[i];
28 string2[size+1] = '\0'; // 应该是string2[size] = '\0';

调试:

(gdb) b 28
Breakpoint 1 at 0x400768: file gdb.c, line 28.
(gdb) r
Starting program: /root/test/gdb/gdb
the original string is Embedded Linux

Breakpoint 1, display2 (
string1=0x7fffffffe5d0 "Embedded Linux") at gdb.c:28
28 string2[size] = '\0';
(gdb) p string2[0]
$1 = 120 'x'
(gdb) p string2[14]
$2 = 0 '\000'
(gdb

最终程序运行结果:

root@lg-desktop:~/test/gdb# ./gdb
the original string is Embedded Linux
the string afterward is xuniL deddebmE

gdb的另一个重要功能是调试死机和段错误;很重要哦!!!!!
例子:首先编写一个错误的程序 core.c 名字随便写
1 #include<stdio.h>
2 core_function(void)
3 {
4 unsigned char *ptr = 0x00;
5 *ptr = 0x00;
6 }
7
8 int main(void)
9 {
10 core_function();
11 return 0;
12
13 }
~下面是生成core(这个名字是系统固定的)文件的步骤:
root@lg-desktop:~/file# gcc core.c -g //1.编译core.c文件
root@lg-desktop:~/file# ulimit -c unlimited //2.设置core大小为无限
root@lg-desktop:~/file# ulimit unlimited //3.设置文件大小为无限
root@lg-desktop:~/file# ./a.out //4.运行编译后的文件,生成下面的core文件
Segmentation fault (core dumped) //5.生成core文件
root@lg-desktop:~/file# gdb ./a.out core //6.gdb调试,会显示错误在哪一行
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
#0 0x00000000004004d4 in core_function () at core.c:5
5 *ptr = 0x00; 7.表示停在第5行,第4行出错了
(gdb)

软件工程工具:make,cvs,subvision

make 是一个工程管理工具,需要通过Makefile实现(注意是大写的M)
下面是实例演示:包含4个.c文件,main.c,f1.c,f2.c,f3.c,最后编写Makefile脚本
.C文件和Makefile需要放在同一个文件夹下面

main.c****************************
1 #include <stdio.h>
2 void enter_string(char str[]);
3 void delete_string(char str[],char ch);
4 void print_string(char str[]);
5
6 int main(void)
7 {
8 char c,str[50];
9 enter_string(str);
10 printf("the char to be deleted is:");
11 scanf("%c",&c);
12 delete_string(str,c);
13
14 }
f1.c**************************************
1 #include <stdio.h>
2 void enter_string(char str[])
3 {
4 printf("input a string:");
5 gets(str);
6
7 }
f2.c**************************************
1 void delete_string(char str[],char ch)
2 {
3 int i,j;
4 for(j = i = 0;str[i] != '\0';i++)
5 {
6 if(str[i] != ch)
7 str[j++]=str[i];
8 str[j]='\0';
9 }
10
11 }
f3.c****************************************
1 #include <stdio.h>
2 void print_string(char str[])
3 {
4 printf("result:%s\n",str);
5
6 }

Makefile***************************Makefile编写很严格,两边不能有空格,切记!

下面的all是目标文件,后面的是依赖文件
生成目标的命令行,可有多行,到下一组命令开始白哦是结束 //1和2组成一个组
1 all: main.o f1.o f2.o f3.o
2 gcc -o all main.o f1.o f2.o f3.o
3 main.o: main.c
4 gcc -c main.c -o main.o
5 f1.o: f1.c
6 gcc -c f1.c -o f1.o
7 f2.o: f2.c
8 gcc -c f2.c -o f2.o
9 f3.o: f3.c
10 gcc -c f3.c -o f3.o
11 clean:
12 rm main.o f1.o f2.o f3.o
13

四个点C文件一起编译,查看有没有错:

root@lg-desktop:~/file# gcc -o test main.c f1.c f2.c f3.c
/tmp/ccmCZYiO.o: In function `enter_string':
f1.c:(.text+0x26): warning: the `gets' function is dangerous and should not be used.
root@lg-desktop:~/file# ls
a.out f1.c f3.c fifo_read.c test
core.c f2.c fife_write.c main.c write_lock.c

Makefile 编写好之后,直接运行make:
root@lg-desktop:~/file# make //运行make
gcc -c main.c -o main.o
gcc -c f1.c -o f1.o
gcc -c f2.c -o f2.o
gcc -c f3.c -o f3.o
gcc -o all main.o f1.o f2.o f3.o
f1.o: In function `enter_string':
f1.c:(.text+0x26): warning: the `gets' function is dangerous and should not be used.
root@lg-desktop:~/file# ls
all core.c f2.c f3.o main.c test
a.out f1.c f2.o fife_write.c main.o write_lock.c
core f1.o f3.c fifo_read.c Makefile

上面是整体过程,下面是分析:

Makefile中的变量:
1.变量代表的是字符串,在Makefile中执行的时候会自动原样的展开在所有使用的地方
2.变量可以使用在目标,依赖目标,命令或者其他部分中
3.变量可以包含字符,数字,下划线
4.变量对大小写敏感,传统的变量是全大写的
5.命令行里面的变量优先级最高,预定义最低

变量定义的格式:
变量名 赋值符号 变量的值

赋值符号有三种:

= 直接将后面的字符串赋给变量
:= 后面跟变量,将它的内容赋给变量
+= 变量原来的值+空格+后面的字符串=>新的变量值

$* 不包含扩展名的目标名称
$+ 所有依赖文件,并已空格分开,
$< 第一个依赖文件的名称
$? 所有依赖文件,并已空格分开,主要是所有改过的文件
$@ 目标的完整名称
$^ 所有依赖文件,并已空格分开,不包含重复的依赖文件

条件编译:
ifeq
ifneq
ifdef
ifndef
endif //结束符

for k in $(DIRS);do 循环体 done

makefile函数

SRCS = $(wildcard *.c) //将当前目录下的所有点C文件赋给SRCS
SRCS = $(patsubst %.c,%.o,<目录或者文件等>) //将<目录或者文件等>中的所有.c文件替换成.o文件并赋给SRCS

%.o: %.c
gcc -c $< -o $@
上面两句的意思是将任意的.c文件转换成.o文件!

为了避免和文件重名的情况,可以使用一个特殊的标记“.PHONY”来显示的指明一个目标为“伪目标”
.PHONY: all

经过变量替换处理,下面两个Makefile的功能是相同的:

Makefile1:
1 all: main.o f1.o f2.o f3.o
2 gcc -o all main.o f1.o f2.o f3.o
3 main.o: main.c
4 gcc -c main.c -o main.o
5 f1.o: f1.c
6 gcc -c f1.c -o f1.o
7 f2.o: f2.c
8 gcc -c f2.c -o f2.o
9 f3.o: f3.c
10 gcc -c f3.c -o f3.o
11 clean:
12 rm main.o f1.o f2.o f3.o
13
Makefile2:
1 .PHONY: all
2 SRCS = $(wildcard *.c)
3 DD = $(patsubst %.c,%.o,$(SRCS))
4 all: $(DD)
5 gcc -o all $+
6 echo $+
7 %.o: %.c
8 gcc -c $< -o $@
9 clean:
10 rm -rf all $(DD)
11

文件锁又可分为读取锁和写入锁,其中读取锁又称为共享锁,它能够使多个进程能在文件的同一部分建立读取锁。
而写入锁又称为排斥锁,在任何时刻只能有一个进程在文件的某个部分建立写入锁,在文件的同一部分不能同时
建立读取锁和写入锁!!!!

lockf() 函数用于对文件施加建议性锁
fcntl() 函数不仅可以施加建议性锁,还可以施加强制锁,同时,fcntl还能对文件的某一记录上锁,也就是记录锁
同时它还是一个通用的函数,还可以对已打开的文件描述符进行各种操作,不仅包括文件锁,还包括获取和设置文件描述符
标志,文件描述符的复制等很多功能

int fcntl(int fd,int cmd,struct flock *lock)
0:成功 -1:出错

struct flock
{
short l_type;
off_t l_start;
short l_whence;
off_t l_len;
pid_t l_pid;

}

文件记录锁功能的源代码:

int lock_set(int fd,int type)
{
struct flock old_lock,lock;
locd.l_whence = SEEK_SET;
lock.l_start = 0;
lock.1_len = 0;
lock.l_type = type;
lock.l_pid = -1;

/*判断文件是否可以上锁*/
fcntl(fd,F_GETLK,&lock);

if((lock.l_type != F_UNLCK)
{
if(lock.l_type == F_RDLCK) /*该文件*/
{
printf("read lock already set by %d\n",lock.l_pid);
}
else if(lock.l_type == F_WRLCK)
{
printf(" write lock already set by %d\n",lock.l_pid);
}
}
/*l_type可能已被F_GETLK修改过*/
lock.l_type = type;
/*根据不同的type值进行阻塞上锁或解锁*/
if((fcnl(fd,F_SETLKW,&lock)) < 0)
{
printf("lock failed:type = %d\n",lock.l_type);
return 1;

}
switch(lock.l_type)
{
case F_RDLCK:
{
printf("read lock set by %d\n",getpid());
}
break;
case F_WRLCK:
{
printf("write lock set by %d\n",getpid());
}
break;
case F_UNLCK:
{
printf("Release lock by %d\n",getpid());
return 1;
}
break;
default:
break;

}
return 0;

}

下面的实例是文件写入锁的测试用例,首先创建一个hello,之和对其写入锁和释放写入锁
其中包含了上一面的程序lock_set(fd,F_WRLCK);
#include <unistd.h>
2 #include <sys/file.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #inlcude "lock_set.c"
8
9 int main(void)
10 {
11 int fd;
12 fd = open("hello",O_RDWR | O_CREAT,0644);
13 if(fd < 0)
14 {
15 printf("open file error\n");
16 exit(1);
17 }
/*给文件上读取锁*/
18 lock_set(fd,F_WRLCK);
19 getchar();
/*给文件解锁*/
20 lock_set(fd,F_UNLCK);
21 getchar();
22 close(fd);
23 exit(0);
24

select()和poll()的I/O多路转接模型是处理I/O复用的一个高效的方法。
int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set*exeptfds,struct timeval *timeout)
int poll(struct pollfd *fds,int numfds,int timeout)

下面程序是调用select()函数来监听3个终端的输入(分别定向到两个管道文件的虚拟终端以及
主程序所运行的虚拟终端),并分别进行相应的处理,

///multiplex_select.c

1 #include <fcn1.h>
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <time.h>
6 #include <errno.h>
7
8 #define MAX_BUFFER_SEZE 1024 缓冲区大小
9 #define IN_RILES 3 多路复用输入文件数目
10 #define TIME_DELAY 60 超时值秒数
11 #define MAX(a.b) ((a > b) ? (a):(b))
12
13 int main(void)
14 {
15 int fds[IN_FELES];
16 char buf[MAX_BUFFER_SIZE];
17 int i,res,real_read,maxfd;
18 struct timeval tv;
19 fd_set inset,tmp_inset;
20 /*首先以只读非阻塞方式打开两个管道文件*/
21 fds[0] = 0;
22 if((fds[1] = open("in1",O_RDONLY | O_NONBLOCK))<0)
23 {
24 printf("open in1 error\n");
25 return 1;
26 }
27 if((fds[2] = open("in2",O_RDONLY | O_NONBLOCK))<0)
28 {
29 printf("open in2 error\n");
30 return 1;
31 }
32 /*取出两个文件描述符中的较大者*/
33 maxfd = MAX(MAX(fds[0],fds[1]),fd[2]);
/*初始化读集合inset,并在读集合中加入相应的描述集*/
34 FD_ZERO(&inset);
35 for(i = 0;i < IN_FILES;i++)
36 {
37 FD_SET(fds[i],&inset);
38 }
39 FD_SET(0,&inset);
40 tv.tv_sec = TIME_DELAY;
41 tv.tv_usec = 0;
42 /*循环测试该文件描述符是否准备就绪看,并调用select函数对相关文件描述符做对应操作*/
43 while(FD_ISSET(fds[0],&inset) || FD_ISSET(fds[1],&inset)||FD_ISSET(fds[0],&inset))
44 {
/*文件描述符的备份,这样可以避免每次进行初始化*/
45 tmp_inset = inset;
46 res = select(maxfd + 1,&tmp_inset,NULL,NULL,&tv);
47 switch(res)
48 {
49 case -1:
50 {
51 printf("select error\n");
52 return 1;
53 }
54 break;
55 case 0:
56 {
57 printf("time out\n");
58 return 1;
59 }
60 break;
61 default:
62 {
63 for(i = 0;i < IN_FILES;i++)
64 {
65
66 memset(but,0,MAX_BUFFER_SEZE);
67 real_read = read(fds[i],buf,MAX_BUFFER_SEZE);
68 if(real_read < 0)
69 {
70 if(error != EAGAIN)
71 return 1;
72 }
73 else if(!real_read)
74 {
75 close(fds[i]);
76 FD_LCR(fds[i],&inset);
77
78 }
79 else
80 {
81 if(i == 0)
82 {
/*主程序终端控制*/
83 if((buf[0]=='q') ||(buf[0] == 'Q'))
84 return 1;
85 }
86 else
87 {
88 buf[real_read] = '\0';
89 printf("%s",buf);
90 }
91 }
92
93 }
94 }
95 }
96 break;
97 }
98
99 return 0;
100}
**********************************************************************************************************************************************************
**********************************************************************************************************************************************************
**********************************************************************************************************************************************************