linux(makefile-gdb-IO)03_makefile文件,gdb相关调试命令,pcd和文件描述符,虚拟地址空间,Linux系统IO函数的使用,Linux系统IO函数的使用

01 学习目标

1.熟练使用规则编写简单的makefile文件
2.熟练使用makefile中的变量
3.熟练使用makefile中的函数
4.熟练掌握gdb相关调试命令的使用
5.了解概念:pcd和文件描述符,虚拟地址空间
6.熟练掌握Linux系统IO函数的使用
open
read
write
lseek
7.了解阻塞和非阻塞的概念

02 makefike

1 makefile好处

一次编写,终身受益

2 makefile的命名规则

  • makefile
  • Makefile

3 makefile的三要素

  • 目标
  • 依赖
  • 规则命令

4 makefile的写法

目标:依赖
tab键 规则命令

第一版本makefile:

app:main.c add.c sub.c div.c mul.c
	gcc -o app -I ./include main.c add.c sub.c div.c mul.c

如果更改其中一个文件,所有的源码都重新编译。
可以考虑编译过程分解,先生成.o文件,然后使用.o文件得到结果
在这里插入图片描述
规则是递推的:
依赖文件如果比目标文件新,则重新生成目标。

app:main.o add.o sub.o div.o mul.o
	gcc -o app -I ./include main.o add.o sub.o div.o mul.o
main.o:main.c
	gcc -c main.c -I ./include
sub.o:sub.c
	gcc -c sub.c -I ./include
div.o:div.c
	gcc -c div.c -I ./include
add.o:add.c
	gcc -c add.c -I ./include
mul.o:mul.c
	gcc -c mul.c -I ./include

第二版本makefile
可以定义变量ObjFiles=main.o add.o sub.o div.o …
变量的使用:$ ( ObjFiles ),$ (变量名)

ObjFiles = main.o add.o sub.o div.o mul.o
app:$(ObjFiles)
	gcc -o app -I ./include main.o add.o sub.o div.o mul.o
main.o:main.c
	gcc -c main.c -I ./include
sub.o:sub.c
	gcc -c sub.c -I ./include
div.o:div.c
	gcc -c div.c -I ./include
add.o:add.c
	gcc -c add.c -I ./include
mul.o:mul.c
	gcc -c mul.c -I ./include

makefile的隐含规则:默认处理第一个目标

函数:
wildcard 可以进行文件匹配
patsubst 内容的替换

makefile的变量

  • S@ 代表目标
  • S^ 代表全部依赖
  • S< 第一个依赖
  • S? 第一个变化的依赖

第三版本makefile,使用函数wildcard,patsubst

# get all -c files
SrcFiles=$(wildcard *.c)
# all .c files --> .o files
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles))
#目标文件用法:$(var)
app:$(ObjFiles)
	gcc -o app -I ./include $(ObjFiles)

#模式匹配规则,S@,S<,这些变量,只能在规则中出现
%.o:%.c
	gcc -c $< -I ./include -o $@

@在规则前代表不输出该条规则的命令
规则前的“-”,代表该条规则报错,仍然继续执行

第四版本makefile
可以指定编译目标make test
增加清理目标:clean
使用make clean清理工程
@在规则前代表不输出该条规则的命令
规则前的“-”,代表该条规则报错,仍然继续执行

# get all -c files
SrcFiles=$(wildcard *.c)
# all .c files --> .o files
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles))
#目标文件用法:$(var)
app:$(ObjFiles)
	gcc -o app -I ./include $(ObjFiles)

#模式匹配规则,S@,S<,这些变量,只能在规则中出现
%.o:%.c
	gcc -c $< -I ./include -o $@

test:
	echo $(SrcFiles)
	echo $(ObjFiles)
clean:
	@rm -f *.o
	rm -f app

最终版makefile
定义伪目标:all
使用.PHONY,防止目标有歧义

# get all -c files
SrcFiles=$(wildcard *.c)
# all .c files --> .o files
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles))
all:app app1

#目标文件用法:$(var)
app:$(ObjFiles)
	gcc -o app -I ./include $(ObjFiles)

app1:$(ObjFiles)
	gcc -o app -I ./include $(ObjFiles)

#模式匹配规则,S@,S<,这些变量,只能在规则中出现
%.o:%.c
	gcc -c $< -I ./include -o $@

test:
	echo $(SrcFiles)
	echo $(ObjFiles)

.PHONY clean all

clean:
	@rm -f *.o
	rm -f app

执行指定的makefile

  • make -f makefile1

03 gdb调试

1.使用gdb:编译的时候加-g参数
gcc func.c main.c -o app -I ./ -g

2.启动gdb:gdb app(对应可执行程序名)

3.在gdb启动程序

  • r(un) 启动

  • start 启动 -停留在main函数,分步调试

  • n(ext) 下一条指令

  • s(tep) 下一条指令,可以进入函数内部,库函数不能进

  • q(uit) 退出gdb

  • 设置启动参数 set args 10 6

  • 设置断点

  • 在这里插入图片描述

  • b 行号----主函数所在文件的行

  • b 函数名

  • b 文件名:行号

  • l(ist)查看代码,默认显示10行
    – l – 显示主函数对应的文件
    – I 文件名:行号

  • 删除断点,d(el)编号

  • 查看断点i(nfo) b,得到编号

  • c(ontinue) 跳到下一断点

  • p(rint) 打印变量的值

  • ptype 打印变量的类型

  • set 设置变量的值
    – set argc=4
    – set argv[1]=“12”
    – set argv[2]=“7”

  • display 显示变量的值,用于追踪,查看变量具体什么时候变化

  • undisplay 删除显示变量,查看编号

  • 设置条件断点 b line if i == 1

4.gdb跟踪core
设置生成:core:ulimit -c unlimited
取消生成:core:ulimit -c 0
设置core文件格式:
在这里插入图片描述

04 系统api与库函数的关系(pcd和文件描述符,虚拟地址空间)

系统函数调用:
在这里插入图片描述文件描述符表:
在这里插入图片描述

05 Linux系统IO函数的使用

1.open

查看 man 2 open

int open(const char *pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);
  • pathname 文件名

  • flags
    必选项:
    O_RDONLY 只读
    O_WRONLY只写
    O_RDWR 读写
    可选项:
    O_APPEND 追加
    O_CREAT 创建文件
    – O_EXCL 与O_CREAT一起使用,如果文件存在,则报错
    – mode权限位,最终(mode& ~umask)
    O_NONBLOCK 非阻塞

  • 返回值:返回最小的可用文件描述符,失败返回-1,设置errno

2.close 关闭文件描述符

  • int close(int fd);
  • fd open打开的文件描述符
  • 返回值:成功返回0,失败返回-1,设置errno
    在这里插入图片描述
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<fcnt1.h>
#include<unistd.h>

int main(int argc,char *argv[])
{
	if(argc!=2)
	{
		printf("./a.out filename\n");
		return -1;
	}
	int fd = open(argv[1],O_RDONLY|O_CREAT,0666);
	close(fd);
	return 0;
}

3.read 读

  • ssize_t read(int fd,void *buf,size_t count)
  • fd 文件描述符
  • buf 缓冲区
  • count 缓冲区大小
  • 返回值:
    失败返回-1,设置errno
    成功返回读到的大小
    0代表读到文件末尾
    非阻塞的情况下read返回-1,但是此时需要判断errno的值

4.write 写

  • ssize_t write(int fd,const void *buf,size_t count);
  • fd 文件描述符
  • buf 缓冲区
  • count 缓冲区大小
  • 返回值
    成功,返回写入的字节数
    失败,返回-1,设置errno
    0,代表未写入
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<fcnt1.h>
#include<unistd.h>

int main(int argc,char *argv[])
{
	if(argc!=2)
	{
		printf(“./a.out filename\n”);
		return -1;
	}
	int fd=open(argv[1],O_RDONLY);
	
	//读,输出到屏幕
	char buf[256];
	int ret = read(fd,buf,sizeof(buf));
	
	write(STDOUT_FILENO,buf,ret);
	//改进:循环读取,读到0结束
	close(fd);
	return 0;
}

5 lseek 移动文件读写位置

  • off_t lseek(int fd,off_t offset,int whence)
  • fd 文件描述符
  • offset 偏移量
  • whence
    SEEK_SET 文件开始位置
    SEEK_CUR 当前位置
    SEEK_END 结尾
  • 返回值:
    成功:返回当前位置到开始的长度
    失败:返回-1,设置errno

需求:打开一个文件,写入内容:helloworld,然后读取一下该文件的内容,输出

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<fcnt1.h>
#include<unistd.h>

int main(int argc,char *argv[])
{
	if(argc!=2)
	{
		printf(“./a.out filename\n”);
		return -1;
	}
	int fd=open(argv[1],O_RDWR|O_CREAT,0666);
	
	write(fd,"helloworld",11);
	//文件读写位置此时到末尾
	//需要移动读写位置
	lseek(fd,0,SEEK_SET);
	char buf[256]={0};
	int ret = read(fd,buf,sizeof(buf));
	if(ret)
	{
		write(STDOUT_FILENO,buf,ret); //STDIN_FILEND,STDERR_FILEEND
	}
	close(fd);
	return 0;
}

lseek的作用:

  • 移动文件读写位置
  • 计算文件大小
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<fcnt1.h>
#include<unistd.h>

int main(int argc,char *argv[])
{
	if(argc!=2)
	{
		printf(“./a.out filename\n”);
		return -1;
	}
	//1.open
	int fd=open(argv[1],O_RDONLY);
	//2.lseek,得到返回值
	int ret = lseek(fd,0,SEEK_END);
	printf("file size is %d\n",ret);
	//3.close(fd);
	close(fd);
	return 0;
}
  • 拓展文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<fcnt1.h>
#include<unistd.h>

int main(int argc,char *argv[])
{
	if(argc!=2)
	{
		printf(“./a.out filename\n”);
		return -1;
	}
	//1.open
	int fd=open(argv[1],O_WRONLY|O_CREAT,0666);
	//2.lseek,拓展文件
	int ret = lseek(fd,1024,SEEK_END);
	//需要至少写一次,否则不能保存
	write(fd,"a",1);
	//3.close(fd);
	close(fd);
	return 0;
}

06 阻塞和非阻塞的概念

阻塞的概念:

read函数在读设备或者读管道,或者读网路的时候
输入输出设备对应/dev/tty

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcnt1.h>
#include<string.h>
#include<sys/types.h>
int main(int argc,char *argv[])
{	
	//O_NONBLOC将设备的阻塞状态改成非阻塞
 	int fd = open("/dev/tty",O_RDWR|O_NONBLOCK);
 	char buf[256];
 	int ret = 0;
 	while(1)
 	{
 		ret = read(fd,buf,sizeof(buf));
 		if(ret<0)
 		{
 			perror("read err:");
 			printf("ret is %d\n",ret);
 		}
 		if(ret)
 		{
 			printf("buf id %s\n",buf);
 		}
 		printf("haha\n");
 		sleep(1);
 	}
 	close(fd);
 	return 0;
}

07 fcntl函数设置非阻塞

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcnt1.h>
#include<string.h>
#include<sys/types.h>
int main(int argc,char *argv[])
{	
	//O_NONBLOC将设备的阻塞状态改成非阻塞
 	//int fd = open("/dev/tty",O_RDWR|O_NONBLOCK);
 	int fd = open("/dev/tty",O_RDWR);
	//fcnt1()函数,设置非阻塞
	
	int flags = fcnt1(fd,F_GETFL);
	flags|=O_NONBLOCK;
	fcnt1(fd,F_SETFL,flags);
	
 	char buf[256];
 	int ret = 0;
 	while(1)
 	{
 		ret = read(fd,buf,sizeof(buf));
 		if(ret<0)
 		{
 			perror("read err:");
 			printf("ret is %d\n",ret);
 		}
 		if(ret)
 		{
 			printf("buf id %s\n",buf);
 		}
 		printf("haha\n");
 		sleep(1);
 	}
 	close(fd);
 	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值