文章目录
-
- linux 常用工具简介:
-
- tar打包器---解压缩指令:
- 常用命令:
- 可执行文件查看(代码段,数据段,bss段):
- 堆和栈的区别:
- 常见内存错误说明:
- 内存分配相关理解:
-
- 1.内存分配方式:
- 2.动态分配常用函数及说明:
-
- 2.1 malloc & free:
- 2.2 realloc:
- 2.3 calloc():
- 2.4 alloca ():
- 3 内存常用管理函数:
-
- 3.1 memcpy():
- 3.2 memmove():
- 3.3 memset():
- 3.4 memchr()
- 3.5 memcmp():
- 文件与文件流及相关操作函数:
-
- 1.文件存储及操作分类:
- 2.标准流及流主要功能:
- 3.文件流指针:
- 4.缓冲区类型及指定:
-
- 5 ANSI C文件I/O操作(对系统调用再次封装):
-
- 5.1 fopen()打开文件:
- 5.2 关闭文件fclose() /fcloseall():
- 5.3 更新缓冲区内容fflush:
- 5.4 字符读操作:
- 5.5 字符写操作,标准流中写一个字符:
- 5.6 行读出操作:
- 5.7 行写入操作:
- 5.8 块读出操作:
- 5.9 块写入操作:
- 5.10 feof() 文件末尾检测函数:
- 5.11 ferror() 函数判断给定流是否错误:
- 5.12 clearerr() 清除错误标识位
- 5.13 ftell() 返回当前读写位置:
- 5.14 fseek() 修改当前读写位置。
- 5.15 rewind() 重置当前读写位置
- 5.16 printf/scanf 函数:
- 5.17 sprintf() 函数:
- 5.18 sscanf() 函数:
- 6 POSIX 标准文件及目录管理:
-
- 6.1 基础说明
- 6.2 文件表结构:
- 6.3 文件描述符相关操作函数:
-
- 6.3.1 fileno()
- 6.3.2 fdopen()
- 6.4 POSIX 标准下文件IO管理:
-
- 6.4.1 open() 打开文件:
- 6.4.2 close() 关闭文件:
- 6.4.3 creat() 创建文件
- 6.4.4 fcntl() 修改文件描述符的特殊属性:
- 6.4.5 read() 从指定文件读取指定大小的数据
- 6.4.6 write向文件写数据
- 6.4.7 lseek() 函数文件定位:
- 6.4.8 sync & fsync & fdatasync :
- 6.4.9 mmap()映射文件到内存:
《Linux 高级程序设计》
-
UNIX 操作系统于1969 年在AT&T贝尔实验室实现。收取少量费用供其它开发者使用修改。
-
加州大学伯克利分校计算机系统研究小组CSRG 借助UNIX 对操作系统研究,改进,包括撰写更好的内存管理等,组成完整的BSD-UNIX 系统向外发行。
-
Linux 由操纵系统发展而来,由Linux Torvalds 和网络黑客从0编写。是一套开源代码,但遵循GNU 的GPL.(因为不限制商家对自由软件进一步开发,因此出现ubutu, redhat 等多个linux发行版)
-
GUN 项目开发很多高质量编程工具(如emacs,gcc,g++),所有GNU软件和派生工作均使用GNU 通用公共许可证(GPL)。
-
posix 标准表示可移植操作系统接口,不局限于UNIX 。posix 提供源代码级别的C语言应用编程接口,如read/write. posix 1.0 和 posix2.0 分别定义了兼容操作系统的C语言系统接口以及工具标准。
-
库函数和系统调用:库函数完成常见功能,系统调用通常与操纵系统有关,库函数最终一般还是会调用系统函数。系统函数会从用户模式切换为内核模式,然后使用系统资源。
-
glibc 函数库:c语言并没有为常见的操作,如输入/输出,内存管理等提供内置支持。这些功能一般由标准函数库来提供。GNU 的函数库glibc 是Linxu 最重要的函数库,定义ISOC 标准所有库函数以及posix附加特色及GNU其他扩展。
-
在线文档查询: 命令行man xxx, 工具软件则是用info(m/n/p/q 等指令).
-
获取错误信息方法:当系统调用出错时会返回-1, 错误信息记载在全局errno 中。 有关errno定义在/usr/include/asm/errno.h 中。
ERERM |
0 :没有操作权限 |
ENOENT |
1:文件或目录不存在 |
ESRCH |
3:没有此程序 |
EINTR |
4:系统调用中断 |
EI0 |
5:I/0错误 |
错误打印输出函数,一般使用perror(“hxg”); 后面自动会将错误信息打印输出。也可以使用strerror 将错误打印到标准输出。
linux 常用工具简介:
tar打包器—解压缩指令:
解压缩指令:
- tar -cvf filename.tar xxxfile //将xxxfile 打包成filename.tar
- tar -xvf filename.tar //解压打包文件
- tar -cjvf filename.tar.bz2 xxxfile //将xxx file 用bz2 压缩并打包
- tar -xjvf filename.tar.bz2 //强filename.tar.bz2 解压缩
- tar -czvf filename.tar.gz directory/file 将file用gzip压缩并打包
- tart -xzvf filename.tar.gz //解压一个gzip格式的Tar 文件。
常用命令:
- expand 如expand -t 4 hello.c 将hello.c 的制表符设置为4个空格
- grep 搜索字符串
- -i: 不区分大小写
- -I:只首次匹配
- -n: 输出前加匹配串所在行的行号
- find 查找文件
可执行文件查看(代码段,数据段,bss段):
gcc -o test hello.c
$: ls test -l 查看文件test的属性
$: file test 列出ELF 格式; ELF 格式可执行文件存储时分为代码区(text),数据区(data)和未初始化数据区bss 3个部分。 也可以readelf -a test 读取test 详细的elf 信息。
- 代码区:CPU可执行的指令,通常为只读。被const声明的变量以及字符串常量在代码段中申请空间。
- 数据段:已被初始化的全局变量或已初始化的静态变量(静态全局变量和静态局部变量),或者数据常量。
- BSS 区(未初始化的数据区):未初始化的全局变量和静态变量。
- 局部变量是运行中在栈中分配。
堆和栈的区别:
- 管理方式不同:栈操作由系统自动管理完成,而堆工作由程序员管理控制
- 空间大小不同:系统预先设定栈顶和大小,当申请空间超出栈剩余空间时,将出现栈溢出错误。堆是向搞地质扩展,不连续的内存。
- 产生碎片不同:频繁使用malloc、free 会产生大量碎片,使得程序效率降低。
- 分配方式不同:堆由malloc 和free 分配释放,而栈动态分配有alloca() 完成且其由编译器自动申请和方式,无需手工。
- 分配效率不同: 堆分配效率较低。
常见内存错误说明:
- 拒绝返回局部变量地址。
- 操作系统加载某个应用程序时,分配一定大小的栈空间。避免申请过大局部变量以出现栈溢出。
- 动态内存管理常见错误:
- 申请和释放函数不一致! 使用C申请就是用C释放,使用C++申请就使用C++释放
- 申请和释放大小不一致! 申请多少就释放多少
- 释放后仍然读写! 释放后内存不应再读写。
内存分配相关理解:
1.内存分配方式:
- 静态分配:编译器在编译源代码时分配,如全局和静态变量。程序执行前分配,效率高。可直接使用变量名字操作。
- 动态分配:程序执行时调用malloc() 库函数申请。程序执行时分配,效率低。使用指向内存的指针是使用动态内存的唯一方法。
2.动态分配常用函数及说明:
2.1 malloc & free:
说明:
特点:
- malloc 分配的内存使用完需要使用free释放
- 不要使用free释放非malloc 分配的内存
- 内存首地址返回的指针不要进行加减操作再free,否则会引发问题
- 不能两次释放相同的指针
- 内存释放后,指向内存的指针应该被赋值为NULL。
2.2 realloc:
说明:
- 在堆中更改已经分配的内存空间。extern void *realloc(void *ptr,size_t size);
特点:
当前内存段后面拥有足够内存空间则直接扩展这段内存空间
,返回原指针
- 如果当前内存段后空闲字节不够,就重新寻找满足的内存块,并将目前数据copy到新位置,原始数据块释放(即释放原来指针realloc()的参数指针),返回新内存块地址。
- 如果申请失败,则返回NULL.
2.3 calloc():
说明:
- 是malloc的简单包装,特点是将动态分配的内存初始化为0.
2.4 alloca ():
说明:
- 在栈中分配size个字节的内存空间,函数返回时自动释放掉该空间。
3 内存常用管理函数:
3.1 memcpy():
extern void* memcpy(void* des, const void* src,size_t n)
- 从src copy n 个字节数据到des地址。
- 类似strncpy()只是strncpy() 参数为char* 类型。
- 类似bcopy(), 只是bcopy 多用于网络编程中。
3.2 memmove():
extern void* memmove(void* dest, void* src,size_t n)
- 类似memcpy ,当dest 和src 没有重合时,与memcpy相同
- 当dest与src 有重叠时,先处理再将src copy n 字节到dest.
3.3 memset():
extern void * memset(void *s, int _c, size_t _n)
- 将s开始后面n位的值设置为c,执行成功返回s首地址。
- 类似bzero()函数,bzero(s,n); 将s开始n字节设置为0
3.4 memchr()
extern void* memchr(const void* _s, int _c,size_t _n)
3.5 memcmp():
extern int memcmp(const void* s1, void* s2, size_t _n)
- 比较s1 和 s2 前n个字节是否相同
- s1=s2 返回0, s1s2 返回1
- 类似strncmp() 函数
文件与文件流及相关操作函数:
1.文件存储及操作分类:
文件存储分类:
- 文本文件:ASCLL文件,文本文件存储量大,速度慢。文件以EOF结束
- 二进制文件:存量小,速度快,便于存放中间结果
文件操作分类:
- 缓冲文件操作:在用户空间自动为使用的文件开辟内存缓冲区,通过一次系统调用读取较多数据到缓冲区,这样避免每次读写都要进行系统调用。(系统调用会将CPU从用户态切换至内核态,影响效率)。
- 非缓冲文件操作:最多只能在程序中开启缓存空间,每次读写都是一次系统调用。
2.标准流及流主要功能:
- Linux 系统中,系统默认为每个进程打开3个文件,即每个进程默认可以操作3个流,标准输入流(/dev/stdin),标准输出流(/dev/stdout),标准错误输出流(/dev/stderr).
- 每个进程从标准输入流读数据,向标准输出流写数据,向标准错误输出流写错误信息。
- 可以通过重定向,修改输入流/输出流。
3.文件流指针:
在应用编程层面,程序对流的操作实际就是对文件流指针FILE的操作。操作一个文件前通过fopen()打开一个文件,返回该文件流指针且该指针与该文件关联。该文件流指针是一个文件描述符,对应的结构体可在用户空间进行访问。
4.缓冲区类型及指定:
4.1缓冲区分类:
- 全缓冲区:该缓冲区默认大小与系统定义有关,大小定义在/usr/include/libio.h。 在缓冲区满或者调用刷新函数fflush() 后才进行I.O系统调用操作。普通磁盘文件的流通常使用全缓冲区访问。
- 行缓冲区:行大小根据系统有关,通常默认128字节。当遇到换行符或缓冲区满时,行缓冲才刷新。终端使用行缓冲区古。
- 不带缓冲区:标准I/O库不对字符进行缓存。标准出错流stderr 通常是不带缓冲区的,以便错误信息能够尽快显示出来。
使 用 缓 冲 区 , 不 需 要 每 次 进 行 标 准 I / O 处 理 时 都 使 用 系 统 I / O 调 用 \color{red}{使用缓冲区,不需要每次进行标准I/O处理时都使用系统I/O调用} 使用缓冲区,不需要每次进行标准I/O处理时都使用系统I/O调用
4.2 缓冲区指定函数:
setbuf():
extern void setbuf(FILE* __stream,char* __buf);
- 第一个参数为要操作的流对象
- 第二个参数是指向BUFSIZ长度的缓冲区。如果buf设置为NULL,则关闭缓冲区。
- 执行成功范围0,否则返回非0
setvbuf():
extern int servbuf(FILE* __stream, char* __buf, int __modes, size_t __n);
- 第一个参数为要操作的流对象
- 第二个参数buf指向一个长度为n大小的缓冲区
- 第三个参数为缓冲区类型 ;#define _IOFBF 0 全缓冲;#define _IOLBF 1 行缓冲;#define _IONBF 2 无缓冲。
- 如果指定不带缓冲区的流,则buf和n参数都忽略;如果buf=NULL,则系统默认分配适当大小的缓存区。
5 ANSI C文件I/O操作(对系统调用再次封装):
5.1 fopen()打开文件:
extern FILE* fopen(__const char* __filename,__const char* __modes);
- 第一个参数为打开文件的绝对路径或者相对路径。
- 第二个参数为打开方式:具体如下:
- 函数执行成功则返回文件指针,如果文件打开失败则返回NULL
5.2 关闭文件fclose() /fcloseall():
fclose():
extern int fclose(FILE* __stream)
- 参数为文件流指针
- 当close文件时, 操 作 系 统 会 自 动 将 缓 冲 区 的 内 容 回 写 到 文 件 \color{red}{操作系统会自动将缓冲区的内容回写到文件} 操作系统会自动将缓冲区的内容回写到文件
- 成功返回0,否则返回EOF, 并设置errno 全局变量。
extern int fcloseall(void);
- 关闭所有流对象。如进程关闭时,需要关闭打开的所有流对象。
5.3 更新缓冲区内容fflush:
通过I/O系统调用将缓冲区内容写会磁盘中。
extern int fflush(FILE* __stream);
- 函数执行成功返回0,否则返回EOF,并设置标准错误error
5.4 字符读操作:
extern int fgetc(FILE * __stream);
extern int getc(FILE *__stream);
- 每次系统调用只从流中读出一个字符。
- 返 回 无 符 号 c h a r 类 型 强 制 转 换 为 i n t 型 \color{red}{返回无符号char 类型强制转换为int型} 返回无符号char类型强制转换为int型
- 成功返回读的内容,失败或文件结束则返回EOF(-1)
- feof() 检测是否督导结束
- ferror() 检测是否出错
- 比如fgetc(stdin) 从标准输入流读一个字符。标准输入读一个字符还可以使用getchar() //extern int getchar(void);
5.5 字符写操作,标准流中写一个字符:
extern int fputc(int __c, FILE* __stream)
extern int putc(int __c, FILE* __stream)
- 向标准输出流写一个字符可用putchar()相当于fputc(c,stdout). //extern int putchar(int __c);
- 调用成功返回内容,失败返回-1
5.6 行读出操作:
extern char* fgets(char* __s, int __n, FILE *__stream)
- 从strean 中读取n-1个字符存放到s中。
- 成功返回s,失败(到文件尾部或出错)则返回NULL,
5.7 行写入操作:
extern int fputs(__const char* __s, FILE* __stream)
extern int puts(__const char* __s)
- fputs 将s指向的以空字符结尾的字符串写入stream中
- puts()将s指向的以空字符结尾的字符串写入标准输出
- 成功返回非负,失败返回-1
5.8 块读出操作:
extern size_t fread(void* __ptr,size_t __size, size_t __n, FILE* __stream)
- 从stream 中读取n个大小为size的对象,存放在ptr所指的内存空间。
- 返回实际读取到对象的个数,如果返回值
5.9 块写入操作:
extern size_t fwrite(__const void* __ptr, size_t __size, size_t __n, FILE *__S);
- 从ptr 中读取n个大小为size的对象写入s指向的stream中。
- 执行成功,函数返回实际写入的对象个数。
5.10 feof() 文件末尾检测函数:
extern int feof(FILE* __stream)
- ASCLL文件可以直接根据返回值是否=EOF判断
- 二进制文件需要使用该函数,返回1表示读到文件结束,否则返回0
5.11 ferror() 函数判断给定流是否错误:
extern int ferror(FILE* stream)
5.12 clearerr() 清除错误标识位
extern void clearerr(FILE* __stream)
- feof 或ferror 执行后,如果出现错误,将设置错误标识符,执行错误处理后需要清除错误标识符。
5.13 ftell() 返回当前读写位置:
extern long int ftell(FILE* __stream)
- 成功返回流的当前读写位置距离文件开始的字节数
- 失败返回-1
5.14 fseek() 修改当前读写位置。
extern int fseek(FILE* __stream, long int __off, int__whence)
- 第一个参数为stream 流
- 第二个参数为基于修改基础的偏移量
- 第三个参数为修改位置的基准;SEEK_SET :文件开始;SEEK_CUR:当前位置;SEEK_END:文件结束位置
5.15 rewind() 重置当前读写位置
extern void rewind(FILE * __stream)
将读写指针移动到文件开头
案例:
FILE* fp_src;
fp_src=fopen("/mytest.cpp",r+);
if(fp_src==NULL)
perror("error\n");
do{
num=fread(buf, 1, 128, fp_src);
if(feof(fp_src)==1)
break;
}while(1)
fclose(fp_src);
5.16 printf/scanf 函数:
int printf(const char*, ...)
int scanf(const char*,...)
printf() 参考
5.17 sprintf() 函数:
int sprintf(char *buffer, const char *format[,argument]...)
- 数字字符串操作: sprintf(s, “%d”,123);
- 控制浮点数打印格式:sprintf(s,"%f",3,1415923);
- 连接字符串:char* who; char * whom; sprintf(s, “%s love %s”,who,whom);
sprintf 参考
5.18 sscanf() 函数:
extern int sscanf(__cosnt char* __s, char* foramt_at,...)
- 从s指向的流中按照format格式读取数据到str.
- 提取指定长度的字符串:sscanf(“12345”, “%4s”,str)
- 提取包含1-9和小写字母的字符串:sscanf(“12345abcdABCD”,"%[1-9a-z]",str);
- 取到到大写字母为止的字符串:sscanf(“12345abcdeABCDE”,"%[^]",str);
sscanf() 参考
6 POSIX 标准文件及目录管理:
6.1 基础说明
- ANSIC 的库函数主要方便用户层使用,这些库函数是对直接IO系统调用的封装,访问时根据缓冲区类型,减少直接I/O系统调用的次数。
- POSIX 标准的IO管理是Linux 操作系统自身提供IO的系统调用,直接进行IO系统调用的移植性差。
- ANSIC打开一个文件,在用户层有FILE 流及相关结构,实际调用系统调用时,在LINUX 内核中也会创建struct file 结构(存储挂载信息,读写位置,使用此结构的进程,文件打开模式等)对应已打开文件的信息。
- 对于用户空间而言,任何一个流都对应一个唯一描述符。比如STDIN 对应0,STDOUT 对应1, stderr 对应2.
6.2 文件表结构:
每个进程有进程私有结构体,struct task_struct,其包含管理打开文件信息的表项,如下图(进程打开文件的内核数据结构):
6.3 文件描述符相关操作函数:
每个stream 流对应唯一的文件描述符,以下是提供给用户层的接口。
6.3.1 fileno()
extern int fileno(FILE*_stream)
- 根据stream 流获取对应的文件描述符。
- 成功返回文件描述符值,失败返回-1
6.3.2 fdopen()
extern FILE* fdopen(int __fd, __const char* __modes)
- 通过文件描述符获取流对象
- 第一个参数为文件描述符,第二个参数以何种方式打开fd.
6.4 POSIX 标准下文件IO管理:
根据不同文件类型,相同的read/write 实际上操作的代码是不一样的。
6.4.1 open() 打开文件:
extern int open(_const char* __filepath,int _oflag,...)
- 以某种方式打开某路径文件
- 打开成功返回文件描述符,打开失败返回-1
- 文件打开flag 参数如下, 该 参 数 表 示 当 前 进 程 对 该 文 件 的 访 问 权 限 \color{red}{该参数表示当前进程对该文件的访问权限} 该参数表示当前进程对该文件的访问权限:
- 如果创建新文件有第三个参数,第三个参数决定创建的文件的权限,如777
6.4.2 close() 关闭文件:
extern int close(int __fd);
- 根据打开文件获取的fd 关闭该文件
- 调用成功返回0,否则返回-1
6.4.3 creat() 创建文件
extern int creat(__const char* _file, _mode_t __mode)
- 第一个参数为创建文件的路径
- 第二个参数为创建文件的权限
6.4.4 fcntl() 修改文件描述符的特殊属性:
fcntl() 参考
extern int fcntl(int __fd, int __cmd,...);
- 第一个参数为修改属性的文件描述符
- 第二个参数cmd为相应操作
- 如设置属性,成功返回0,错误返回-1;如读取属性,成功返回属性值,错误返回-1
第二个参数说明:
参数 |
说明 |
F_DUPFD |
复制文件描述符,该动作仅仅在当前进程打开的文件表项新增一项,两者同时指向内核为该文件分配的文件表项 |
F_GETFD |
获取与文件描述符关联的close-on_exc标志 |
F_SERFD |
设置文件描述符关联文件的属性,第三个参数为对应属性 |
F _ G E T F L \color{red}{ F\_GETFL } F_GETFL |
获取文件状态标志和访问模式 |
F_SERFL |
修改文件状态标志及访问模式 |
- F_SETFL支持的标志如下:
- F_SETFD 仅仅修改当前文件描述符信息,而F_SETFL 修改与该文件相关的所有文件描述符。
6.4.5 read() 从指定文件读取指定大小的数据
extern ssize_t read(int __fd, void *_buf, size_t _nbytes)
- 从参数fd 所指的文件读取nbutes 字节到buf中
- 在读数据时,读位置会自动移动
- 实际读取小于预读取数据量:
1. 文件剩余字节小于nbyte
2. read()请求被某个信号中断
3. 文件是有名管道或无名管道
- 读操作行为不同说明:
支持搜索的文件(常规文件,目录文件,链接文件)从关联文件地址偏移量开始读取
不支持搜索的文件,如字符设备文件,中断则总是从当前位置读取。
6.4.6 write向文件写数据
extern ssize_t write(int __fd, __const __buf, size_t n);
- 从buf 读取n个字节写到fd
- 写行为与实际操作的文件有关
6.4.7 lseek() 函数文件定位:
extern __off_t lseek(int _fd, _off_t _offset, int _whence)
#include
void sync(void)
- 函数sync 始终成功,只将所有修改过的块的缓存排入写队列,不等待时间I/O操作。
fsync() 函数介绍:
int fsync(int fildes)
- fsync 多用于数据库相关的应用程序。将修改过的块的缓存排入写队列,并等待I/O结束。
- 成功返回0,否则返回-1
fdatasync() 函数
int fdatasync(int fildes);
- 只更新内容,没有必要不更新元数据。
- 成功返回0,否则返回-1;
6.4.9 mmap()映射文件到内存:
#include <sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
- 在进程的虚拟地址空间起始地址为start 长度为length 与fd 开始offset 起始形成映射。
- 进程无需再调用read 和write 直接可操作映射区文件
- flag 为映射的内存权限,不能与文件打开的权限冲突。
- 修改映射区内容不会立马写回文件,需要使用msync() 实现
- 取消映射时,需要使用munmap() 函数。