LinuxAdvancedProgrammingResearch - juedaiyuer/researchNote GitHub Wiki
#Linux高级程序设计#
工具:sourceinsight
Linux下编辑调试工具 gcc gdb
四个步骤
-
预处理 去掉注释,进行宏替换(#define相关),头文件(#include)包含
gcc -E ... -
编译 编译成汇编语言,基于不同的平台,X86,ARM...
gcc -S ... -
汇编 将汇编语言翻译成二进制的目标代码
gcc -c ... -
链接 包含各函数库的入口,得到可执行代码
gcc -o ...
Linux的C源代码文件后缀.c
.C C++
优化:目标是使代码性能更优,去掉冗余代码,工具自动完成,-O 0/1/2/3
gcc -g 生成调试信息
gdb
l是list的意思,列出各行编号
b 行号 设置断点
n是下一步
c是continue 下一个断点
p 变量名 打印这个变量的值
库的使用 系统定义的头文件 /usr/include /usr/local/include /usr/target/include
库文件 /lib
ldd file(可执行文件) 可以列出此文件使用了哪些库文件
math库
编译时默认链接c库,如果要使用其它的库,编译时要用-l
系统限制
limit.h float.h(数据类型限制)
本身限制:资源有限,不可能无限制的申请资源
命令行:ulimit来修改和获取
编程:getrlimit函数来获取,setrlimit来设置系统的限制
RLIMIT_CORE :core文件的最大字节数,core文件是系统某个进程出现异常退出时,系统为其保存的上下文信息,在调试程序时经常要用
RLIMIT_CPU:CPU时间的最大值(s)
RLIMIT_DATA:一个进程数据段的最大字节数
RLIMIT_FSIZE:可创建文件的最大值
RLIMIT_NOFILE:每个进程可以打开的文件的个数
RLIMIT_STACK:进程栈空间的最大值
RLIMIT_VMEN:虚拟地址空间的最大值
RLIMIT_AS:系统进程可用内存空间最大值
##命令行参数##
main函数的参数问题
argc:参数的个数
argv[]:指针数组,存放具体的参数列表
测试代码:
#include<stdio.h>
int main(int argc,char* argv[]){
int i;
for(i=0;i<argc;i++)
printf("argv[%d]=%s\n",i,argv[i]);
return 0;
}
main函数提取命令行参数列表
getopt
getlongopt
#include<unistd.h>
int getopt(int argc,char* const argv[],const char* optstring);
##内存管理##
###进程地址空间### 可执行文件
查看文件的结构:size file
三个部分
- 代码段 主要存放指令,操作,只读常量
- 数据段 全局,静态,二者初始化过的
- BSS段 未初始化的变量
程序存在磁盘上(读取慢),运行时加载到内存中(读取快,CPU可直接访问读写)
进程:最小的资源管理单元,一个程序运行,必然为其创建一个进程,进程是有生命周期的,一个进程是执行的程序段.当然一个程序也可有创建多个进程.
内核中的资源:
PCB进程控制块,结构体task struct,负责管理进程的所有资源
成员
mm_struct 指向相关的内存资源
- 代码段,数据段,BSS段,直接从磁盘拷贝到当前内存空间,大小相等
- 动态空间 堆栈,mmap段(映射其它的库的相关信息)
进程所在的目录 /proc
ps -aux | grep file(可执行文件名)
进程的内存地址信息列表
- cat /proc/进程id/maps
- pmap
地址实际并不是真正的物理地址,而是虚拟地址空间,用于对资源的保护. 内存资源是宝贵的,执行一个程序,并不需要将所有内存资源加载到内存中,采用写时申请技术.
好处
- 保护系统,用户程序非法访问不能造成内核崩溃,段错误出现时,非法访问进程自动退出
- 节约资源,采用内存映射,一个程序执行时,不是将所有资源都加载到内存中,而是使用缺页真正申请物理空间.
32位平台,一个进程拥有自己的4G的虚拟地址空间
###虚拟地址空间布局###
进程执行时,如何申请内存空间,申请哪些空间
##Posix磁盘文件内容管理##
- 磁盘文件需要经过系统调用,一个程序(进程)与要操作的文件建立联系
- 操作磁盘文件,要用到内存做到桥梁作用,系统调用磁盘文件,返回一个文件描述符
- Linux操作系统提供了open系统调用,任何的进程访问一个文件,需要使用open,返回一个文件描述符
- 读写文件内容 read/write
- 文件位置的修改 lseek
- 用户程序从用户空间向内核空间提交打开文件申请
- 操作系统会在内核中检查请求的合法性,在内核中申请这个打开文件的信息(读写位置,在磁盘中的位置...用struct files来存储).并且添加到当前进程的PCB块中的打开文件列表数组中,对应的这个数组下标即文件描述符
- 文件描述符返回用户空间,用户空间进行操作
进程控制块-打开文件列表-文件表项(struct file)
系统认为每个进程打开了三个文件
printf/scanf
文件描述符0(标准输入-键盘 STDIN_FILENO) 1(标准输出-显示器 STDOUT_FILENO) 2(标准错误输出-显示器 STDERR_FILENO)
- IO系统调用函数
- 建立与断开联系 open/close
open
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);
flag:
只读 O_READONLY
只写 O_WRITEONLY
读写 O_RDWR
追加 O_APPEND
mode
创建新文件的权限
新创建一个文件的真正权限: mode & ~umask
打开文件成功,返回一个新的文件描述符
#include<fcntl.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char* argv[])
{
int fd;
fd = open(argv[1],O_RDONLY|O_CREAT,0664);
if(-1 == fd)
{
perror("open");
exit(EXIT_FAILURE);
}
printf("fd:%d\n",fd);
close(fd);
}
- 测试程序,功能:打开一个文件返回ID,亦可以创建一个目录下不存在的文件 ./a.out 文件名
简单的拷贝文件实现
#include<fcntl.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
// cp src -> dst
// argv[1] src
// argv[2] dst
int main(int argc,char* argv[])
{
if(argc !=3)
{
printf("please input message format as :%s src_file dst_file\n",argv[0]);
exit(EXIT_FAILURE);
}
int fd_src,fd_dst;
fd_src = open(argv[1],O_RDONLY);
if(-1 == fd_src)
{
perror("open");
exit(EXIT_FAILURE);
}
printf("fd_src:%d\n",fd_src);
fd_dst = open(argv[2],O_WRONLY|O_CREAT,0664);
if(-1 == fd_dst)
{
perror("open");
exit(EXIT_FAILURE);
}
//内存空间,并且初始化
char buf[1024];
int ret = 0;
memset(buf,'\0',1024);
ret = read(fd_src,buf,1024); //src文件读入内存,返回读入的字节数
write(fd_dst,buf,ret);
close(fd_src);
close(fd_dst);
}
-
测试程序,文件大小有限制.
-
文件大小解决办法
char buf[1024]; int ret = 0;
while(1) { memset(buf,'\0',1024);
ret = read(fd_src,buf,1024); //src文件读入内存,返回读入的字节数 if(ret == -1) { perror("read"); exit(EXIT_FAILURE); } else if(ret == 0) break; else write(fd_dst,buf,ret);
}
-
对当前文件的读写位置进行定位,可以在文件中添加空洞 lseek
-
文件头 SEEK_SET ----当前读写位置SEEK_CUR ---- 文件尾 SEEK_END ; 偏移
-
也可以用这个函数来实现文件大小的获取,把文件的读写位置设置为文件结束,因为这个函数返回当前读写位置距离文件头的偏移字节数
文件描述符操作
-
复制 dup,dup2,fcntl,两个文件描述符指向同一表项
close(0) dup(3)
-
这个代码可以实现输入的重定向,默认从0读数据,现在0指向的表被复制为3,这样从0读实际上就是从3指向的文件读,也就实现了输入的重定向
dup(3,0) // 等效于 close(0) dup(3)
锁定
- 并发的环境,除了使用并发的工具来保护共享文件外,也可以使用文件锁,flock
- LOCK_SH 共享锁
- LOCK_EX exclusive lock 排它锁
- LOCK_UN
- LOCK_NB 非阻塞式申请
如果锁定某一部分,可以使用fcntl函数
属性控制,权限状态,拥有者等
- fcntl 当前的读写状态,当前文件描述符所在的进程等系列信息
提高效率,同步磁盘
- mmap函数打开虚拟地址空间,以后操作类似于操作这个文件
##ANSI C文件IO管理##
position : Linux高级程序设计-第四章
- 流是在文件描述符基础上进行的封装,添加了如下的信息,使功能
- 缓冲区
- 读写位置
缓冲区
- 全缓冲区: /usr/include/stdio.h
BUFSIZ - 行缓冲区
换行符,缓冲区满才刷新行缓冲
部分系统默认128个字节
终端即行缓冲 - 不带缓冲区
wirte()系统调用函数,标准出错流stderr
IO操作
FILE *fopen(const char *path, const char *mode); //文件流
w
Truncate file to zero length or create text file for writing. The stream is positioned at the beginning of the file.
int fclose(FILE *fp);
int fflush(FILE *stream); //更新缓存区内容
//字符读写操作
while(ch = fget(fp)!=EOF); //按字符输出到标准输出stdout
fputc(ch,std);
//行读写文件流
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
The function fread() reads nmemb elements of data, each size bytes long, from the stream pointed to by stream, storing them at the location given by ptr.
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
feof() //判断文件流末尾
文件流定位
- ftell 返回当前读写位置
- fseek 修改当前的读写位置
- rewind 重置读写位置
代码示例
- ftell和fseek实现某文件大小-length_example.c
- 实现文件复制操作-cp_examlpe.c
流的格式化输入输出
page:108
- printf/scanf
- fprintf/fscanf
source:
课程讲师的网页地址
pos: title:Linux高级程序设计(evernote)