目录
4.5 调试器gdb
Page:95 - 104
/* 启动 */
$ gcc -g test.c -o test //-g参数必须加
$ gdb test //gdb 程序文件名 加上-q参数不输出版权说明
$ gdb -q //也可以直接输入gdb,用file命令装入需要调试的程序
(gdb) file test //(gdb)是提示符
/* 退出 */
(gdb) quit
/* 显示 */
(gdb) list //显示十行代码
(gdb) list 5, 10 //显示5-10行代码
(gdb) list test.c:5, 10 //显示源文件test.c的5-10行
(gdb) list <函数名> //显示该函数周围代码
/* shell */
(gdb) shell <命令> //调试时使用shell命令
/* 执行 */
(gdb) run //运行程序
/* help */
(gdb) help <命令> //了解某命令使用方法
/* 断点 */
(gdb) break <行号>
(gdb) break <函数名>
(gdb) break <行号/函数名> if <条件> //条件表达式成真时设置断点
(gdb) info breakpoints //查看所有断点信息
(gdb) disable <断点编号> //使断点失效
(gdb) enable <断点编号> //使断点有效
(gdb) delete <断点编号> //删除该断点
(gdb) clear //删除所有断点
(gdb) clear <函数名/行号> //删除该断点
/* 查看和设置变量 */
(gdb) print <变量/表达式> //打印该值
(gdb) print <变量=值> //对变量进行赋值
(gdb) print <表达式@要打印的值的个数n> //打印以表达式值开始的n个数
(gdb) whatis <变量名/表达式> //显示数据类型
(gdb) set variable <变量=值> //相当于print <变量=值>
/* watch */
(gdb) watch <条件表达式> //条件表达式值改变时暂停
//watch i==99 需在i定义后输出,所以在i定义时先设置断点
/* 控制执行 */
(gdb) continue //执行到下一次暂停或程序结束
(gdb) next //执行一条语句但不进入函数
(gdb) step //执行一条语句并可以进入函数
(gdb) finish //执行到当前函数结束
(gdb) until //执行到循环体结束
(gdb) kill //询问是否结束调试
(gdb) nexti //执行一条机器指令(类似next)
(gdb) stepi //执行一条机器指令(类似step)
Tips:
1.直接敲回车则执行上一次的命令
2.一些命令可以简写,如list可以写成li
6 文件操作
- shell命令是操作系统提供给普通用户的接口,系统调用时操作系统提供给程序员使用的接口
- C语言的库函数也是通过系统调用实现,它封装了系统调用,并再次基础上为方便程序员使用增加了一些功能
- 对于gcc不会自动链接的库,使用-l <库名> -L <库所在的目录> 选项
- Linux文件系统模型:
- 对物理磁盘的访问都是通过设备驱动程序来进行的
- 对设备驱动访问有两种方式:
- 通过设备驱动本身提供的接口:让用户进程绕过文件系统直接读写磁盘内容,带来不稳定性
- 通过虚拟文件系统(Virtual File System)提供给上层应用程序的接口(API)
- VFS是虚拟的,不存在的,只存在于内存中,只有在系统运行起来才存在
- fchmod/chmod:
#include <sys/types.h>
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);//文件名
int fchmod(int files, mode_t mode);//文件描述符
- 权限更改成功返回0,失败返回1,错误代码存于errno
- mode参数的宏用 |(按位或)进行运算
- 编写跨平台程序最好使用C语言的标准库函数
- Linux下,文件描述符取值在0~256,所以每个程序最多只能打开256个文件,其中,0:stdin,1:stdout,2:stderr
- open/create:打开或创建文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
- 新文件的实际存取权限时mode与umask按照mode&~umask运算的结果
- creat()相当于open()中的flag参数为O_CREAT|O_WRONLY|O_TRUNC
- close:
#include <unisted.h>
int close(int fd);
- close函数调用成功时不保证数据能全部写回硬盘
- 进程结束时内核会自动关闭所有已打开的文件,但这不是一个好习惯
- read:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
- 含义:从文件描述符fd所指向的文件中读取count个字节到buf中,返回实际读取的字节数,因此可用返回值跟count比较
- write:
#include <unistd.h>
ssize_t write(int fd, void *buf, size_t count);
- 同样的,返回成功写入的字节数
- lseek:移动读写指针
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
- whence:SEEK_SET、SEEK_CUR、SEEK_END
- 成功则返回读写位置(字节数),失败则返回-1
- lseek允许文件指针的值设置到EOF后,若对EOF后的位置写入数据,EOF和写入数据直接用0填充
- 预编译期内置宏:__LINE__、__FILE__
- dup/dup2:
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
- 返回的新文件描述符和参数old指向同一文件,共享所有的锁定、读写指针和权限或标志位
- dup用于复制oldfd所指的文件描述符,复制成功返回最小的未使用的文件描述符
- dup可用参数newfd指定新文件描述符的值,若newfd被使用,系统会将其关闭以释放该文件描述符,成功返回新文件描述符,失败返回-1
- fcntl:
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
- 可对已打开的文件描述符进行各种控制操作以改变已打开文件属性
- F_DUPFD:与dup类似
- F_GETFD:获取文件描述符的close-on-exec标志
- F_SETFL:设置文件打开方式为第三个参数arg指定的方式
- F_GETFL:获得文件打开方式,成功返回标志值,同mode一致
- O_ACCMODE是取得文件打开方式的掩码,实际值是3,按位与后取得后两位
- fcntl用于管理文件记录锁操作时,第三个参数指向一个struct flock *lock结构:
struct flock {
short l_type;/*F_RDLCK, F_WRLCK, or F_UNLCK */
off_t l_start;/*offset in bytes, relative to l_whence */
short l_whence;/*SEEK_SET, SEEK_CUR, or SEEK_END */
off_t l_len;/*length, in bytes; 0 means lock to EOF */
pid_t l_pid;/*returned with F_GETLK */
7 };
第一个成员是加锁的类型:只读锁,读写锁,或是解锁。l_start和l_whence用来指明加锁部分的开始位置,l_len是加锁的长度,l_pid是加锁进程的进程id。
- 多个进程在给定的字节上可以有一把共享的读锁,但是给定字节上的写锁只能由一个进程单独使用
- 锁的不兼容规则:
- 给定字节已经有读锁,则不能加写锁
- 给定字节有独占性的写锁,则不能加读锁
- 一个进程只能设置某一文件区域上的一种锁,如果某一文件区域已经存在文件记录锁
- 文件记录锁:
- 有多个进程对某一文件操作时,就有可能发生数据的不同步,该文件的最后状态取决于写该文件的最后一个程序,Linux系统提供记录锁来确保进程在单独写一个文件
- rename:修改文件名或文件位置
#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
- 若newpath指定文件已存在,原文件会被删除
- 成功返回0,失败返回-1
- 文件的删除:
#include <unistd.h>
int unlink(const char *pathname);
int remove(const char *pathname);
- rmdir系统调用用于删除目录,unlink调用用于删除文件,remove实际上封装了unlink和rmdir,所以目录和文件都可以删除
- unlink:从文件系统删除一个文件,如果连接数为0且没有进程调用,则文件被删除且占用的磁盘空间被释放,若有进程打开则等到进程结束才删除
- mkdir:
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
- 目录权限由mode&~umask指定
- 新建目录的uid与该进程uid一致,如果父目录有SGID,则新建目录也有SGID
- rmdir:只能删除空目录
#include <unisted.h>
int rmdir(const char *pathname);
- getcwd:获取进程当前工作目录
#include <unistd.h>
char *getcwd(char *buf, size_t size);
- getcwd会将当前工作目录的绝对路径复制到buf所指内存,size为buf空间大小
- 若绝对路径过长则返回NULL,若buf为NULL则系统自动malloc出内存,若size也为0,系统会根据绝对路径长度来决定配置的内存大小
- 进程使用完可利用free释放内存
- 成功返回配置的指针,失败返回NULL
- chdir:更改当前工作目录
#include <unistd.h>
int chdir(const char *path);
int fchdir(int fd);
- chdir和fchdir无法改变父进程工作目录,只影响当前进程(执行后shell中工作目录不变)
- 获取目录信息
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
struct dirent *readdir(DIR *dir);
int closedir(DIR *dir);
- opendir返回DIR*形态的目录,类似文件描述符,失败返回NULL
- readdir:从参数dir指向目录读取目录项信息,返回struct dirent结构指针
struct dirent
{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}
- 获取文件属性:stat/lstat/fstat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
struct stat {
dev_t st_dev; /* ID of device containing file */ // 文件的设备编号
ino_t st_ino; /* inode number */ // 结点
mode_t st_mode; /* protection */ // 文件的类型和存取的权限
nlink_t st_nlink; /* number of hard links */ // 连到该文件的硬链接数目,新建的文件则硬连接数为 1
uid_t st_uid; /* user ID of owner */ // 用户ID
gid_t st_gid; /* group ID of owner */ // 组ID
dev_t st_rdev; /* device ID (if special file) */ // 若此文件为设备文件,则为其设备的编号
off_t st_size; /* total size, in bytes */ // 文件字节数(文件大小)
blksize_t st_blksize; /* blocksize for filesystem I/O */ // 块大小
blkcnt_t st_blocks; /* number of 512B blocks allocated */ // 块数
time_t st_atime; /* time of last access */ // 最后一次访问时间
time_t st_mtime; /* time of last modification */ // 最后一次修改时间
time_t st_ctime; /* time of last status change */ // 最后一次改变时间
};
- stat 函数与 lstat 函数的区别: 当一个文件是符号链接时,lstat 函数返回的是该符号链接本身的信息;而 stat 函数返回的是该链接指向文件的信息。
具体请参考:获取文件属性—stat、lstat、fstat - ctime: 返回一个表示当地时间的字符串,当地时间是基于参数 timer。
#include <time.h>
char *ctime(const time_t *timer);
- 返回的字符串格式如下: Www Mmm dd hh:mm:ss yyyy 其中,Www 表示星期几,Mmm 是以字母表示的月份,dd 表示一月中的第几天,hh:mm:ss 表示时间,yyyy 表示年份。
- getpwuid:知道用户uid(user id),用getpwuid获取用户相关信息
#include <pwd.h>
struct passwd * getpwuid(uid_t uid);
#include <sys/types.h>
#include <pwd.h>
struct passwd
{
char *pw_name; /* 用户登录名 */
char *pw_passwd; /* 密码(加密后) */
__uid_t pw_uid; /* 用户ID */
__gid_t pw_gid; /* 组ID */
char *pw_gecos; /* 详细用户名 */
char *pw_dir; /* 用户目录 */
char *pw_shell; /* Shell程序名 */
};
用法:char* fileUser = getpwuid(st.st_uid)->pw_name;
- getgrgid:同理,获取所在组相关信息