Linux介绍与文件IO学习
一、Linux目录结构以及文件操作
1.Linux命令操作的目的
Windows采用的命令:DOS命令
Linux采用的命令:Shell命令
我们采用命令,是为了操作文件。比如访问某个目录,访问某个文件,查找某个文件等等。
2.Linux文件目录分布
Linux文件目录是呈现一个树状的数据结构。
"/“根目录下有"bin”、“boot”、“dev”、"home"等目录,这些目录下又有很多目录。
根目录:/ 就是目录的一个源头。用户目录:~或者/home/username
3.常用Linux命令
cd+文件名:访问某个文件夹
ls:显示当前所在目录的文件
touch+文件名:创建文件
rm+文件名:删除文件
mkdir+目录名:创建目录
rm -rf +目录名:删除目录
TAB键:补全
sudo+命令:用管理员权限执行命令
pwd:现实当前目录绝对路径
4.vi编辑器的简单使用
vi和vim的区别:vim是vi的升级版,基础功能两者一致,不过在嵌入式开发板中只有vi没有vim,但是在ubuntu上我们可以使用vim编辑器,他的功能更加丰富。
vi+文件名 用vi打开/创建某个文本文件
5.vi常用两种模式
1.命令行模式:按ESC进入,在这个状态下可以输入常用命令:
:行号 跳转到某一行
G 跳转到文本末尾
yy 复制该行
yx 复制若干行,x代表行数
p 粘贴
:wq 保存文本并退出
:q 正常退出文本
:q! 强制退出文本
:set nu 程序显示行号
:dd 删除一行
gg = G 自动整理代码
2.文本输入模式:按i或a进入
二、Linux文件
open函数
高频使用的Linux系统调用:open write read close
Linux自带的工具:man手册
man 1是普通的shell命令
man 2是系统调用函数,如open,write说明。man 2 open
查看open函数说明
在Linux系统库的定义:
int open(const char *pathname,intflags);//常用
int open(const char *pathname,intflags,mode_tmode);
//需包含的头文件
#include <sys/types.h>//这里提供类型pid_t和size_t的定义
#include <sys/stat.h>
#include <fcnt1.h>c
返回值:
成功,返回文件句柄,我们后面对于文件的读写和关闭等 都通过文件句柄来错做。
失败,返回-1
参数说明:
pathname:文件的路径名,如果只写文件名,就默认当前目录,如果在文件名加上目录,就按照绝对路径来打开文件。
flags:表示打开文件后用的操作。O_RDONLY只读模式、O_WRONLY只写模式 和 O_RDWR可读可写。
文件权限
Linux系统中采用三位十进制数表示权限,如0755,0644
ABCD:A-0表示十进制,B-用户,C-组用户,D-其他用户。
7表示1+2+4可读可写可执行,5表示1+4可读可执行。
close函数
int close(int fd);
write函数
write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。
ssize_t write(int fd, const void *buf, size_t count);
fd:文件描述符
*buf:写入的数据的首地址
count:写入数据个数
返回值:如果顺利write()会返回实际写入的字节数(len)。错误则返回-1,错误代码存入errno中。
read函数
从打开的fd设备或文件中读取count个字节到buf中
ssize_t read(int fd, void *buf, size_t count);
fd:文件描述符
*buf:读数据的首地址
count:读数据的个数
返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调用read之前已经到达文件末尾,则这次read返回0。
lseek函数
off_t lseek(int fd, off_t offset, int whence);
fd:文件描述符
offset:偏移量
whence:
SEEK_SET:参数offset即为新的读写位置
SEEK_CUR:以目前的读写位置往后增加offset个偏移量
SEEK_END:将读写位置指向文件尾后再增加offset个偏移量,当whence值为CUR或END时,参数offset允许负值的出现。
返回值:文件读写距离文件开头的字节大小,出错返回-1
main函数参数
int main(int argc,char *argv[])
{
return 0;
}
C语言规定了main函数的参数只能有两个,一个是argc,一个是argv,并且argc只能是整数,第二个必须是指向字符串的指针数组。
argc:表示行中参数的个数,argc的值是在输入命令行时由系统按实际参数个数自动赋值的。
argv:参数是字符串指针数组,各元素值为命令行中各字符串
cp指令的实现
执行流程:打开源文件,读取文件缓存倒数组,打开目标文件,写入目标文件,关闭文件句柄。
#include <stdio.h>
#incldue <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int src_fd;
int dev_fd;
char readBuff[128]={0};
int nRet = 0;
if(argc!=3){
printf("error\n");
return -1;
}
src_fd = open(argv[1],O_RDWR);
if(src_fd<0){
printf("open file %s failed\n",argv[1]);
return -2;
}
des_fd = open(argv[2],O_RDWR|O_CREAT,0755);
if(des_fd<0){
printf("open file %s failed\n",argv[2]);
return -3;
}
while(1)
{
nRet = read(src_fd,&readBuff[0],128);
if(nRet<128)
break;
write(des_fd,readBuff,nRet);
memset(readBuff,0,128);//清零读缓存区
}
write(des_fd,readBuff,nRet);
close(src_fd);
close(des_fd);
return 0;
}
这里介绍以下memset函数
void *memset(void *buffer,int c,int count);
/*
buffer:为指针或数组
c:是赋给buffer的值
count:是buffer的长度
*/
memset(void *buff,0,sizeof(buff));//用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为''或'\0'
fopen函数
fopen fwrite fread fclose等 属于标准C库函数
open close write read属于Linux系统调用。
可移植性:fopen强于open
fopen在用户态是缓存的,open在用户态没有缓存的
FILE *fopen(const char *pathname, const char *mode);
第一个参数为想打开文件的文件路径及文件名,第二个参数表示打开方式
mode:
r:只读,文件必须存在
r+:可读写,文件必须存在
rb+:打开二进制文件,可读写
w:只写,文件存在则文件长度清0,文件不存在则建立该文件
w+:可读写
返回值:成功,返回文件指针;失败返回NULL
fread
从文件中读入数据到指定的地址中
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
第一个参数为接收数据的指针buf,也即数据存储的地址。
第二个参数为单个元素的大小,即由指针写入地址的数据大小,注意单位是字节
第三个参数为元素个数,即要读取的数据大小为size的元素个数
第四个参数为提供数据的文件指针,该指针指向文件内部数据
返回值:读取的总数据元素个数
fwrite
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
fseek
重定位文件内部的指针
int fseek(FILE *stream, long offset, int whence);
参数:第一个为文件指针,第二个是指针偏移量,第三个是指针偏移起始位置。
返回值:重定位成功返回0,否则返回非零值。
常用3个宏:
SEEK_SET 0 文件开头
SEEK_CUR 1 文件当前位置
SEEK_END 2 文件末尾
例如:
fseek(fp,100L,SEEK_SET);
把fp指针移动到离文件开头100字节处。
fseek(fp,100L,SEEK_CUR);
把fp指针移动到离文件当前位置100字节处。
fclose
关闭一个文件流,使用fclose可以把缓冲区内最后剩余的数据输出到磁盘文件中,并释放文件指针和有关的缓冲区。
int fclose(FILE* stream);
成功返回0,失败返回EOF
示例:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp = NULL;
int nRet = 0;
char readBuff[12];
memset(readBuff,0,12);//初始化数组为0
char *writeBuff = "hello world";
fp = fopen("mm","r+");
if(fp == NULL){
printf("fopen failed\n");
return -1;
}
printf("open succeed\n");
nRet = fread(readBuff,4,2,fp);
if(nRet<=0){
printf("fread failed\n");
return -2;
}
printf("read %s\n",readBuff);
nRet = fseek(fp,1,SEEK_SET);
if(nRet){
printf("fseek failed\n");
return -3;
}
nRet = fwrite(writeBuff,4,1,fp);
if(nRet <= 0){
printf("fwrite failed\n");
return -4;
}
nRet = fclose(fp);
if(nRet){
printf("close failed\n");
return -5;
}
printf("close succeed\n");
return 0;
}
文件IO和标准IO的区别
linux标准文件描述符
文件描述符 | 缩写 | 描述 |
---|---|---|
0 | STDIO | 标准输入 |
1 | STDOUT | 标准输出 |
2 | STDERR | 标准错误输出 |
文件IO:是指调用内核提供的系统调用函数,头文件是unistd.h
标准IO:是间接调用系统调用函数,头文件是stdio.h
缓存的概念
1.我们程序中的缓存,局势你想从内核读写的缓存(数组)—用户空间的缓存
2.每打开一个文件,内核在内核空间中也会开辟一块缓存,这个叫内核空间的缓存
文件IO中的写 即是将用户空间中的缓存写到内核空间的缓存中。
文件IO中的读 即是将内核空间中的缓存写到用户空间的缓存中。
3.标准IO的库函数也有一个缓存,这个缓存称为 库缓存。
C库缓存的特点:
1.遇到 \n 时,会将库缓存的内容写到内核缓存中,即调用了系统调用函数。
2.库缓存写满时,会调用系统调用函数,将库缓存内容写到内核缓存中。1024
//例如
int main()
{
char buf[20]="hello world";
printf("%s",buf);//此时是不会有标准输出的,因为没有\n字符结束
while(1);
return 0;
}
三类读写函数
1.行缓存,遇到换行符(\n),或者写满用户缓存时,即调用系统调用函数
读:fgets,gets,printf,fprintf,sprintf
写:fputs,puts,scanf
一个字符的读写,是否也有行缓存
读:fgetc,getc,getchar
写:fputc,putc,putchar
2.无缓存,只要用户调用这个函数,就会写到内核中
stderr
这个可以用在fputs里,fputs(buf,stderr);
3.全缓存,只有写满缓存再调用系统调用函数
读:fread
写:fwrite
行缓存的读写函数fgets和fputs
char *fgets(char *s,int size,FILE *stream);
int fputs(const char *s,FILE *stream);
fgets示例:
#include <stdio.h>
int main(int argc,char *argv[])
{
FILE *fp;
char writeBuff[] = "hello world";
fp = fopen("mm.c","w+");
if(fp == NULL){
printf("fopen failed\n");
return -1;
}
fputs(writeBuff,fp);
fseek(fp,0,SEEK_SET);//将光标指向第一个位置,因为fputs执行完毕后光标会移动到字符串的末尾,会导致下面的fgets函数输出为空。
fgets(readBuff,128,fp);
printf("readBuff is %s\n",readBuff);
fclose(fp);
return 0;
}
fputs示例:
#include <stdio.h>
int main(int argc,char *argv[])
{
char writeBuff[] = "hello world";
FILE *fp;
fp = fopen("a.c","w+");
if(fp == NULL){
printf("fopen failed\n");
return -1;
}
fputs(writeBuff,fp);//因为fputs是行缓存的输出函数,所以运行此代码时,shell界面并看不到输出writeBuff的字符串。
fclose(fp);//运行到这里才会输出writeBuff,因为fclose关闭文件指针后,会把缓冲区内最后剩余的数据输出到磁盘文件中。
return 0;
}
刷新缓存函数
fflush(FILE *fp);
一般使用方法:fflush(stdout);
调整读写位置指针函数
fseek():
fseek()参数与lseek是一样的但是返回值不一样
lseek()函数返回值是:当前文件的位置指针值;
fseek()返回值:成功返回0,失败返回-1;
rewind(FILE *fp):用于设定流的文件位置为文件开头,该函数调用成功无返回值
实例:
#include <stdio.h>
int main(int argc, char *argv)
{
char writeBuff[] = "hello world";
char readBuff[128] = {0};
FILE *fp;
fp = fopen("a.c","w+");
if(fp == NULL){
printf("fopen failed\n");
return -1;
}
//fputs(fp,0,SEEK_SET);
rewind(fp);
fgets(readBuff,128,fp);
printf("readBuff is %s\n",readBuff);
fclose(fp);
return 0;
}
gets和puts
行缓存读写函数
char *gets(char *s);
int puts(const char *s);
gets与fgets区别:
1.gets()不能指定缓存长度,这样会造成缓存越界
2.gets()只能从标准输入中读
3.gets()不将新行符存入缓存中,fgets将新行符存入缓存中
puts与fputs区别:
1.puts()只能向标准输出中写
2.puts()输出时会添加一个新行符,fputs不会添加。
fprintf、sprintf
行缓存函数
#include <stdio.h>
int main()
{
char buf[128]={0};
int i = 10;
sprintf(buf,"i = %d\n",i);
printf("buf is %s\n",buf);
return 0;
}
fputc和fgetc
行缓存函数
fputc写一个字符到文件中和fgetc从文件中读一个字符。
返回值都是成功则返回输入或读取的字符,出错返回EOF。-1
例:
#include <stdio.h>
int main(int argc,char *argv[])
{
FILE *fp;
int nRet;
char writeBuff[] = "hello world";
fp = fopen("a.c","w+");
if(fp == NULL){
printf("open file failed\n");
return -1;
}
fputc('a',fp);
rewind(fp);
nRet = fgetc(fp);
printf("nRet is %c\n",nRet);
fclose(fp);
return 0;
}
其他feof、ferror、clearerr
int feof(FILE *stream);//判断是否已经到文件结束,若是则返回非0,没有返回0
int ferror(FILE *stream);//判断是否读写错误,若是读写错误返回非0,无错误返回0
void clearerr(FILE *stream);//清除流错误
cat指令实现
#include <stdio.h>
int main(int argc,char *argv[])
{
FILE *fp;
int nRet = 0;
if(argc!=2){
printf("failed");
return -1;
}
fp = fopen(argv[1],"r");
if(fp == NULL){
printf("fopen failed\n");
return -2;
}
while(1)
{
nRet = fgetc(fp);
if(feof(fp))//到文件末尾则返回非0
break;
fputc(nRet,stdout);
}
fclose(fp);
return 0;
}
Linux下静态库和动态库制作与使用
Linux操作系统支持的函数库分为:
静态库,libxxx.a,在编译时就将库编译进可执行程序中。优点:程序的运行环境中不需要外部的函数库。缺点:可执行程序大
动态库,libxxx.so,在运行时将库加载到可执行程序中。优点:可执行程序小。缺点:程序的运行环境中必须提供响应的库。
静态库的制作
1.生成目标文件:gcc -c file.c
2.静态函数库创建命令ar
ar -cr libfile.a file.o
例如写一个main.c和sub.c两个c文件:
gcc sub.c -c -o sub.o
生成sub.o文件
ar -cr -o libsub.a sub.o
生成libsub.a静态函数库文件
gcc main.c -L. -lsub
静态库的编译,就会把静态函数库整合进main。-L.表示在本目录下查找静态函数库。-l指定了静态函数库名,由于静态函数库命名方式是“lib***.a”,其中的lib和.a忽略。
动态函数库的制作
1.生成目标文件:gcc -c file.c
2.gcc -shared -fpic -o libfile.so file.o
-fpic:产生位置无关代码
-shared:生成共享库
用上述命令生成libaddsub.so动态函数库
gcc -o out main.c -L. -lfile
此时还不能立即./out,因为在动态函数库使用时,会查找/usr/lib,/lib目录下的动态函数库,而我们的动态库不在里面
3.解决方法:
1.libaddsub.so放到/usr/lib或/lib中去;
2.假设libfile.so在/home/linux/file 环境变量方法
export LD_LIBRARY_PATH=/home/linux/file
3.在/etc/ld.so.conf文件里加如我们生成的库的目录,然后执行/sbin/ldconfig。
/etc/ld.so.conf是非常重要的一个目录,里面存放的是链接器和加载器搜索共享库时要检查的目录。