VMware虚拟机的三种网络模式
GCC工作流程
源代码(.h.c.cpp)
| 预处理
| (头文件展开、删除注释、宏替换等)
↓ gcc test.c -E -o test.i
预处理后源代码(.i)
| 编译器
|
↓ gcc test.i -S -o test.s
汇编代码(.s)
| 汇编器
|
↓ gcc test.s -c -o test.o
目标代码(.o) +启动代码、库代码、其他目标代码
| 链接器
|
↓
可执行程序(.exe.out)
GCC和G++
.c的,gcc当作c程序,g++当作c++程序
.cpp的,都当作c++程序
编译阶段,g++调用gcc,对于c++代码二者等价
但链接时,c++程序需要用g++或者gcc -lstdc++
库
代码保密
方便部署和分发
静态库优点:
被打包到应用程序中,加载速度快
发布程序无需提供静态库,移植方便
缺点:
消耗系统资源,浪费内存
更新、部署、发布麻烦
动态库优点:
可以实现进程间资源共享(共享库)
更新、部署、发布简单
可以控制何时加载动态库
缺点:
加载速度比静态库慢
发布程序时需要提供依赖的动态库
静态库:
GCC链接阶段被复制到可执行文件中。
动态库:
GCC链接阶段没有被复制到可执行文件中,只放了信息,而在程序运行时,使用到动态库的api时,根据定位动态库的绝对路径,由系统动态加载到内存中。可通过ldd(list dynamic dependencies)命令检查动态库依赖关系(ldd 可执行文件)
定位动态库文件:
当系统加载可执行代码时,能够知道其所依赖的库的名字,但还需要知道绝对路径。
此时就需要系统的动态载入器来获取该绝对路径。
对于elf格式(绝大部分)的可执行程序,由 ld-linux.so动态载入器(专门用来负责定位、加载程序所需的所有动态库文件)来完成。
它先后搜索elf文件的 DT_RPATH段 (改不了,不考虑) --→ 环境变量LD_LIBRARY_PATH --→ /etc/ld.so.cache文件列表 --→ /lib 或 /user/lib目录(内含系统自带库文件,不建议)找到库文件后将其载入到内存
静态库的制作和使用
-
命名规则
Linux:libxxx.a
lib:前缀
xxx:库名
.a:后缀
Windows:libxxx.lib -
制作
gcc -c 获得 .o文件
将 .o文件打包,使用ar工具(archive)
ar rcs libxxx.a xxx.o xxx.o
r - 将文件插入备存文件中
c -建立备存文件
s -索引 -
使用
分发库文件时需要将库文件的头文件同时分发使用-I(大i)去查找头文件所在的路径
gcc main.c -o app -I ./include
然后使用-l(小L)指定使用的库的名称
gcc main.c -o app -I ./include -l calc
再使用-L去查找库文件所在的路径
gcc main.c -o app -I ./include -l calc -L ./lib
动态库的制作和使用
-
命名规则
Linux:libxxx.so
lib:前缀
xxx:库名
.so:后缀
Windows:libxxx.dll -
制作
使用gcc得到.o文件,得到和位置无关的文件
gcc -c -fPIC a.c b.c
或
gcc -c -fpic a.c b.c
使用gcc得到动态库
gcc -shared a.o b.o -o libcalc.so
-
使用
分发库文件时需要将库文件的头文件同时分发使用-I(大i)去查找头文件所在的路径
gcc main.c -o app -I ./include
然后使用-l(小L)指定使用的库的名称
gcc main.c -o app -I ./include -l calc
再使用-L去查找库文件所在的路径
gcc main.c -o app -I ./include -l calc -L ./lib
配置动态库绝对路径:
1.1.临时配置环境变量
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库绝对路径
1.2.永久用户级别配置
进入到home目录下
vim .bashrc
到最后一行
插入export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库绝对路径
保存并退出
. .bashrc
第一个.是source的意思
或
source .bashrc
使之生效
1.3.永久系统级别配置
sudo vim /etc/profile
到最后一行
插入export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库绝对路径
保存并退出
. /etc/profile
或
source /etc/profile
使之生效
2.更改/etc/ld.so.cache文件列表
通过sudo vim /etc/ld.so.conf
间接修改
插入动态库绝对路径
保存并退出
sudo ldconfig
更新
Makefile
Makefile文件定义了一系列规则来指定文件的编译顺序、是否重新编译或者更复杂的功能操作。Makefile一旦写好,只需要make命令,整个工程就能完全自动化编译。
- 文件命名
makefile 或者Makefile - Makefile规则
一个Makefile文件中可以有一个或多个规则
目标...:依赖...
命令(Shell命令)
...
目标:最终要生成的文件(伪目标除外)
依赖:生成目标所需要的文件或目标
命令:通过执行命令对依赖操作生成目标(命令前必须Tab缩进)
Makefile中的其它规则一般都是为第一条规则服务的
例子:
app:sub.o add.o mult.o div.o main.o
gcc sub.o add.o mult.o div.o main.o -o app
sub.o:sub.c
gcc -c sub.c -o sub.o
add.o:add.c
gcc -c add.c -o add.o
mult.o:mult.c
gcc -c mult.c -o mult.o
div.o:div.c
gcc -c div.c -o div.o
main.o:main.c
gcc -c main.c -o main.o
- 工作原理
命令执行前,检查规则中的依赖是否存在
若存在,执行命令
若不存在,向下检查其它的规则,有没有一个规则是用来生成这个依赖的,
若找到,则执行该规则中的命令
检测更新,在执行规则中的命令时,会比较目标和依赖文件的时间
若依赖的更新时间比目标的时间晚,需要重新生成目标
若依赖的更新时间比目标的时间早,目标不需要更新,对应规则中的命令不需要被执行 - 变量
自定义变量
变量名=变量值 var=hello
预定义变量
AR:归档维护程序的名称,默认值为ar
CC:C编译器的名称,默认值为cc
CXX:C++编译器的名称,默认值为g++
自动变量(只能在规则的命令中使用)
$@:获取目标的完整名称
$<:获取第一个依赖文件的名称
$^:获取所有的依赖文件
获取变量的值
$(变量名) - 模式匹配
%.o:%.c
%:通配符,匹配一个字符串
两个%匹配的时同一个字符串
例子(优化上个例子):
#定义变量
src=sub.o add.o mult.o div.o main.o
target=app
$(target):$(src)
$(CC) $(src) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
- 函数
$(wildcard PATTERN…)
功能:获取指定目录下指定类型的文件列表
参数:PATTERN指的是某个或多个目录下的对应的某种类型的文件,如果有多个目录,一般使用空格间隔
返回:得到的若干个文件的文件列表,文件名之间使用空格间隔
示例:
$(wildcard *.c ./sub/*.c)
返回值格式:a.c b.c c.c d.c e.c f.c
$(patsubst <PATTERN>,<replacement>,<text>)
功能:查找text中的单词是否符合模式pattern,如果匹配,则以replacement替换
示例:
$(patsubst %.c, %.o, x.c bar.c)
返回值格式:x.o bar.o
例子(优化上个例子):
#定义变量
#文件中有sub.c add.c mult.c div.c main.c
src=$(wildcard ./*.c)
objs=$(patsubst %.c, %.o, $(src))
target=app
$(target):$(objs)
$(CC) $(objs) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
#删除依赖,.o文件
#将clean变成伪目标,不生成特定文件
.PHONY:clean
clean:
rm $(objs) -f
make
再clean
make clean
GDB调试
为调试而编译时,关闭编译器优化选项,打开调试选项(-g),在尽量不影响程序行为的情况下打开所有warning(-Wall)
gcc -g -Wall program.c -o program
-g选项的作用是在可执行文件中加入源代码的信息,
比如可执行文件中第几条机器指令对应源代码的第几行。
所以在调试时必须保证gdb能找到源代码文件。
启动:
gdb 可执行程序
退出:
quit/q
给程序设置参数:
set args 10 20
获取设置参数:
show args
GDB使用帮助:
help
查看当前文件代码:
从默认位置(main)显示:
list/l
指定行上下文显示:
list/l 行号
指定的函数上下文显示:
list/l 函数名
查看非当前文件代码:
list/l 文件名:行号
list/l 文件名:函数名
设置显示的行数:
show list/listsize
set list/listsize 行数
设置断点:
b/break 行号
b/break 函数名
b/break 文件名:行号
b/break 文件名:函数
查看断点:
i/info b/break
删除断点:
d/del/delete 断点编号
设置断点无效:
dis/disable 断点编号
设置断点生效:
ena/enable 断点编号
设置条件断点(一般用在循环的位置):
b/break 10 if i==5
运行GDB程序:
start
程序停在第一行
run
遇到断点才停
继续运行,到下一个断点停:
c/continue
向下执行一行代码(不会进入函数体):
n/next
向下单步调试(遇到函数进入函数体):
s/step
finish
跳出函数体(内无断点)
变量操作:
p/print 变量名
打印变量值
ptype 变量名
打印变量类型
自动变量操作:
display 变量名
自动打印指定变量的值
i/info display
undisplay 编号
其它操作:
set var 变量名=变量值
until
跳出循环(内无断点)
标准C库IO函数和Linux系统IO函数对比
标准c库io有缓冲区,适合磁盘读写
在man 3中查找
Linux的io无缓冲区,适合网络通信
在man 2中查找
标准c库io调用Linux的io
虚拟地址空间
每个进程有一个虚拟地址空间
CPU中的MMU(Memory Management Unit),即内存管理单元,将虚拟地址空间映射到物理内存中
文件描述符
open函数
//需要头文件
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
//函数原型:
//打开一个已经存在的文件
int open(const char *pathname, int flags);
/*
参数:
-pathname:要打开的文件路径
-flags:对文件的操作权限设置还有其他的设置
-必选项:O_RDONLY, O_WRONLY, O_RDWR 这三个设置是互斥的
-可选项:O_APPEND(追加),O_ASYNC(同步)。。。
返回值:
返回一个新的文件描述符,如果调用失败,返回-1(并改errno)
errno:属于Linux系统函数库,库里面的一个全局变量,记录的是最近的错误号
#include<stdio.h>
void perror(const char *s);
s参数:用户描述,比如hello,最终输出内容:hello:xxx(实际的错误描述)
作用:打印errno对应的错误描述
*/
//创建一个新的文件
int open(const char *pathname, int flags, mode_t mode);
/*
参数:
-pathname:要创建的文件路径
-flags:对文件的操作权限设置还有其他的设置
-必选项:O_RDONLY, O_WRONLY, O_RDWR 这三个设置是互斥的
-可选项:O_CREAT(若文件不存在,则创建新文件)
-mode:八进制的数,表示创建出的的新文件的操作权限,rwxrwxrwx
最终的权限是:mode & ~umask
umask的作用是抹去某些权限,使之更合理
比如mode为0777,umask为002,则最终0775,抹去了其他组的写权限
flags参数是一个int类型的数据,占4个字节,32位。
flags 32个位,每一位就是一个标志位,所有用 |
*/
//此处不是重载,而是使用了可变参数
打开文件
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>//close函数需要
int main()
{
//打开文件
int fd = open("a.txt", O_RDONLY);
if(fd == -1)
{
perror("open");
}
//关闭文件
close(fd);
return 0;
}
创建文件
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>//close函数需要
int main()
{
//创建文件
int fd = open("create.txt", O_RDWR | O_CREAT, 0777);
if(fd == -1)
{
perror("create");
}
//关闭文件
close(fd);
return 0;
}
read、write函数
//需要头文件
#include<unistd.h>
//函数原型
ssize_t read(int fd, void *buf, size_t count);
/*
参数:
-fd:文件描述符,open得到,通过这个文件描述符操作文件
-buf:需要读取数据存放的地方,数组的地址(传出参数)
-count:指定的数组的大小
返回值:
-成功:
>0:返回实际的读取到的字节数
=0:文件已经读取完了
-失败:
-1,并设置errno
*/
ssize_t write(int fd, const void *buf, size_t count);
/*
参数:
-fd:文件描述符,open得到,通过这个文件描述符操作文件
-buf:要往磁盘写入的数据,数组
-count:要写的数据的实际的大小
返回值:
-成功:
>0:实际写入的字节数
=0:表示没有写任何东西
-失败:
-1,并设置errno
*/
实例拷贝文件:
#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
//1.通过open打开english.txt文件
int srcfd = open("english.txt", O_RDONLY);
if(srcfd == -1)
{
perror("open");
return -1;
}
//2.创建一个新的文件(拷贝文件)
int destfd = open("cpy.txt", O_WRONLY|O_CREAT, 0664);
if(destfd == -1)
{
perror("create");
return -1;
}
//3.频繁的读写操作
char buf[1024]={0};
int len = 0;
while((len = read(srcfd, buf, sizeof(buf))) > 0)
{
write(destfd, buf, len);
}
//4.关闭文件
close(srcfd);
close(destfd);
return 0;
}
lseek函数
//需要头文件
#include<sys/types.h>
#include<unistd.h>
off_t lseek(int fd, off_t offset, int whence);
/*
参数:
-fd:文件描述符,open得到,通过这个文件描述符操作文件
-offset:偏移量
-whence:
SEEK_SET:
设置偏移量:文件头+第二个参数offset的值
SEEK_CUR:
设置偏移量:当前位置+第二个参数offset的值
SEEK_END:
设置偏移量:文件大小+第二个参数offset的值
返回值:
返回文件指针的位置
作用:
1.移动文件指针到文件头
lseek(fd,0,SEEK_SET);
2.获取当前文件指针的位置
lseek(fd,0,SEEK_CUR);
3.获取文件长度
lseek(fd,0,SEEK_END);
4.扩展文件的长度,当前文件10b,希望110b,要增加100个字节
lseek(fd,100,SEEK_END);
作用:在下载时,先占用磁盘大小,再往里写入
*/
示例扩展文件长度:
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
int main()
{
int fd = open("hello.txt", O_RDWR);
if(fd == -1)
{
perror("open");
return -1;
}
//扩展文件长度
int ret = lseek(fd, 100, SEEK_END);
if(ret == -1)
{
perror("lseek");
return -1;
}
//写入一个空数据
write(fd, " ", 1);
//关闭文件
close(fd);
return 0;
}
stat、lstat函数
//需要头文件
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
//函数原型
int stat(const char *pathname, struct stat *statbuf);
/*
作用:获取一个具体文件的相关信息
参数:
-pathname:文件路径
-statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
返回值:
成功:0
失败:-1,并设置errno
*/
int lstat(const char *pathname, struct stat *statbuf);
/*
作用:获取一个软链接文件的相关信息
参数:
-pathname:文件路径
-statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
返回值:
成功:0
失败:-1,并设置errno
*/
示例用法:
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdio.h>
int main()
{
struct stat statbuf;
int ret = stat("a.txt", &statbuf);
if(ret == -1)
{
perror("stat");
return -1;
}
printf("size: %ld\n",statbuf.st_size);
return 0;
}
实现ls -l命令
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
#include<string.h>
//模拟实现ls -l指令
//输入: ./ls a.txt
//输出: -rw-rw-r-- 1 nowcoder nowcoder 12 12月 3 15:48 a.txt
//文件类型 访问权限 硬连接数 所有者 所在组 文件大小 修改时间 文件名称
int main(int argc,char* argv[])
{
//判断输入参数是否正确
if(argc<2)
{
printf("%s filename\n",argv[0]);
return -1;
}
//通过stat函数获取用户传入的文件信息
struct stat st;
int ret=stat(argv[1],&st);
if(ret==-1)
{
perror("stat");
return -1;
}
//获取文件类型和文件权限
char perms[11]={0}; //用于保存文件类型和文件权限
switch(st.st_mode & __S_IFMT)
{
case __S_IFLNK:
perms[0]='l';
break;
case __S_IFDIR:
perms[0]='d';
break;
case __S_IFREG:
perms[0]='-';
break;
case __S_IFBLK:
perms[0]='b';
break;
case __S_IFCHR:
perms[0]='c';
break;
case __S_IFSOCK:
perms[0]='s';
break;
case __S_IFIFO:
perms[0]='p';
break;
default:
perms[0]='?';
break;
}
//判断文件的访问权限
//文件所有者
perms[1]=(st.st_mode&S_IRUSR)?'r':'-';
perms[2]=(st.st_mode&S_IWUSR)?'w':'-';
perms[3]=(st.st_mode&S_IXUSR)?'x':'-';
//文件所在组
perms[4]=(st.st_mode&S_IRGRP)?'r':'-';
perms[5]=(st.st_mode&S_IWGRP)?'w':'-';
perms[6]=(st.st_mode&S_IXGRP)?'x':'-';
//其他人
perms[7]=(st.st_mode&S_IROTH)?'r':'-';
perms[8]=(st.st_mode&S_IWOTH)?'w':'-';
perms[9]=(st.st_mode&S_IXOTH)?'x':'-';
//硬连接数
int linkNum=st.st_nlink;
//文件所有者
char* fileUser=getpwuid(st.st_uid)->pw_name;
//文件所在组
char* fileGrp=getgrgid(st.st_gid)->gr_name;
//文件大小
long int fileSize=st.st_size;
//获取修改时间
char* time=ctime(&st.st_mtime);
char mtime[512]={0};
strncpy(mtime,time,strlen(time)-1);//去掉回车换行
char buf[1024];
sprintf(buf,"%s %d %s %s %ld %s %s",perms,linkNum,fileUser,fileGrp,fileSize,mtime,argv[1]);
printf("%s\n",buf);
return 0;
}
文件属性操作函数
int access(const char *pathname, int mode);
int chmod(const char *filename, int mode);
int chown(const char *path, uid_t owner, gid_t group);
int truncate(const char *path, off_t length);
access
//需要头文件
#include<unistd.h>
//函数原型
int access(const char *pathname, int mode);
/*
作用:判断某个文件是否有某个权限,或者判断文件是否存在
参数:
-pathname:判断的文件路径
-mode:
R_OK:判断是否有读权限
W_OK:判断是否有写权限
X_OK:判断是否有执行权限
F_OK:判断文件是否存在
返回值:
成功:0
失败:-1并改errno
*/
示例:
#include<unistd.h>
#include<stdio.h>
int main()
{
int ret=access("a.txt",F_OK);
if(ret==-1)
{
perror("access");
return -1;
}
printf("文件存在!!\n");
return 0;
}
chmod
//需要头文件
#include<sys/stat.h>
//函数原型
int chmod(const char *pathname,mode_t mode);
/*
作用:修改文件权限
参数:
-pathname:修改的文件路径
-mode:需要修改的权限值,8进制的数(会&掩码)
返回值:
成功:0
失败:-1并改errno
*/
示例
#include<sys/stat.h>
#include<stdio.h>
int main()
{
int ret = chmod("a.txt",0775);
if(ret == -1)
{
perror("chmod");
return -1;
}
return 0;
}
chown
修改所有者或所在组
略
truncate
//需要头文件
#include<sys/types.h>
#include<unistd.h>
//函数原型
int truncate(const char *path, off_t length);
/*
作用:缩减或者扩展文件的尺寸至指定大小
参数:
-path:需要修改的文件的路径
-length:需要最终文件变成的大小
返回值:
成功:0
失败:-1并改errno
*/
示例:
#include<sys/types.h>
#include<unistd.h>
int main()
{
int ret = truncate("b.txt",20);
if(ret == -1)
{
perror("truncate");
return -1;
}
return 0;
}
目录操作函数
int mkdir(const char *pathname, mode_t mode);
int rmdir(const char *pathname);
int rename(const char *oldpath, const char *newpath);
int chdir(const char *path);
char *getcwd(char *buf, size_t size);
mkdir
//需要头文件
#include<sys/stat.h>
#include<sys/types.h>
//函数原型
int mkdir(const char *pathname, mode_t mode);
/*
作用:创建一个目录
参数:
-pathname:创建的目录的路径
-mode:权限,八进制的数(会&掩码)
返回值:
成功:0
失败:-1并改errno
*/
示例
#include<sys/stat.h>
#include<sys/types.h>
#include<stdio.h>
int main()
{
int ret=mkdir("aaa",0777);
if(ret == -1)
{
perror("mkdir");
return -1;
}
return 0;
}
rmdir
删除目录函数
略
rename
重命名函数
略
chdir
//需要头文件
#include<unistd.h>
//函数原型
int chdir(const char *path);
/*
作用:修改进程的工作目录
比如在/home/jeon 启动了一个可执行程序a.out,进程的工作目录为/home/jeon
参数:
path:需要修改的工作目录
*/
getcwd
//需要头文件
#include<unistd.h>
//函数原型
char *getcwd(char *buf, size_t size);
/*
作用:获取当前的工作目录
参数:
-buf:存储的路径,指向的时一个数组,传出参数
-size:数组的大小
返回值:
返回的指向的一块内存,这个数据就是第一个参数
*/
示例:
#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
//获取当前的工作目录
char buf[128];
getcwd(buf,sizeof(buf));
printf("当前的工作目录是:%s\n",buf);
//修改工作目录
int ret = chdir("/home/jeon/Linux/lesson13");
if(ret == -1)
{
perror("chdir");
return -1;
}
//创建一个新文件
int fd = open("chdir.txt",O_CREAT | O_RDWR,0664);
if(fd == -1)
{
perror("open");
return -1;
}
close(fd);
//获取当前的工作目录
char buf1[128];
getcwd(buf1,sizeof(buf1));
printf("当前的工作目录是:%s\n",buf1);
return 0;
}
目录遍历函数
DIR *opendir(const char *name);
struct dirent *readdir(DIR *dirp);
int closedir(DIR *dirp);
opendir
//需要头文件
#include<sys/types.h>
#include<dirent.h>
//函数原型
DIR *opendir(const char *name);
/*
作用:打开一个目录
参数:
name:需要打开的目录的名称
返回值:
DIR * 类型,理解为目录流
失败则返回NULL
*/
readdir
//需要头文件
#include<dirent.h>
//函数原型
struct dirent *readdir(DIR *dirp);
/*
作用:读取目录流中的数据
参数:
dirp:opendir()返回的结果
返回值:
struct dirent *:代表读取到的文件的信息
读取到了末尾或者失败了,返回NULL,并设置errno
*/
struct dirent
{
//此目录进入点的inode
ino_t d_ino;
//目录文件开头至此目录进入点的位移
off_t d_off;
//d_name的长度,不包含NULL字符
unsigned short int d_reclen;
//d_name所指的文件类型
unsigned char d_type;
//文件名
char d_name[256];
};
d_type:
DT_BLK -块设备
DT_CHR -字符设备
DT_DIR -目录
DT_LNK -软连接
DT_FIFO -管道
DT_REG -普通文件
DT_SOCK -套接字
DT_UNKNOWN -未知
closedir
//需要头文件
#include<sys/types.h>
#include<dirent.h>
//函数原型
int closedir(DIR *dirp);
/*
作用:关闭目录
*/
示例
统计指定目录下普通文件的个数
#define _DEFAULT_SOURCE //使用DT_DIR等宏需要
#include<sys/types.h>
#include<dirent.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>//exit(0);需要
//输入: ./a.out lesson15
//输出: 此目录下所有普通文件的个数
int getFileNum(const char *path);
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("%s path\n",argv[0]);
return -1;
}
int num = getFileNum(argv[1]);
printf("普通文件的个数为:%d\n",num);
return 0;
}
//用于获取目录下所有普通文件的个数
int getFileNum(const char *path)
{
//1.打开目录
DIR* dir = opendir(path);
if(dir == NULL)
{
perror("opendir");
exit(0);
}
struct dirent *ptr;
//记录普通文件的个数
int total=0;
while((ptr = readdir(dir)) != NULL)
{
//获取名称
char* dname = ptr->d_name;
//忽略掉 .和 ..目录
if(strcmp(dname, ".") == 0 | strcmp(dname, "..") == 0)
{
continue;
}
//判断是否是普通文件还是目录
if(ptr->d_type == DT_DIR)
{
//目录,需要继续读取这个目录
char newpath[256];
sprintf(newpath,"%s/%s",path,dname);
total += getFileNum(newpath);
}
if(ptr->d_type == DT_REG)
{
//普通文件,统计个数
total++;
}
}
//关闭目录
closedir(dir);
return total;
}
dup、dup2函数
int dup(int oldfd);
复制文件描述符
int dup2(int oldfd, int newfd);
重定向文件描述符
dup
//需要头文件
#include<unistd.h>
//函数原型
int dup(int oldfd);
/*
作用:
复制一个新的文件描述符
fd=3,int fd1=dup(fd)
则fd指向a.txt,fd1也指向a.txt
从空闲的文件描述符表中找一个最小的,作为新的拷贝的文件描述符
*/
示例:
#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd = open("a.txt",O_RDWR | O_CREAT,0664);
int fd1 = dup(fd);
if(fd1 == -1)
{
perror("dup");
return -1;
}
printf("fd:%d ,fd1:%d\n",fd,fd1);
close(fd);
char*str = "hello world";
int ret = write(fd1,str,strlen(str));
if(ret == -1)
{
perror("write");
return -1;
}
close(fd1);
return 0;
}
dup2
//需要头文件
#include<unistd.h>
//函数原型
int dup2(int oldfd, int newfd);
/*
作用:
重定向文件描述符
oldfd指向a.txt,newfd指向b.txt
调用函数成功后:newfd 和b.txt做close, newfd指向a.txt
oldfd必须是一个有效的文件描述符
oldfd和newfd值相同则相当于什么都没做
*/
示例:
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
int fd = open("1.txt",O_RDWR | O_CREAT,0664);
if(fd == -1)
{
perror("open");
return -1;
}
int fd1 = open("2.txt",O_RDWR | O_CREAT,0664);
if(fd1 == -1)
{
perror("open");
return -1;
}
printf("fd:%d,fd1:%d\n",fd,fd1);
int fd2 = dup2(fd,fd1);
if(fd2 == -1)
{
perror("dup2");
return -1;
}
//通过fd1去写数据,实际操作的是1.txt
char* str="hello world";
int len = write(fd1,str,strlrn(str));
if(len == -1)
{
perror("write");
return -1;
}
printf("fd:%d,fd1:%d,fd2:%d\n",fd,fd1,fd2);
//fd2与fd1相同
close(fd);
close(fd1);
return 0;
}
fcntl函数
int fcntl(int fd, int cmd, .../*arg*/);
复制文件描述符
设置/获取文件的状态标志
//需要头文件
#include<unistd.h>
#include<fcntl.h>
//函数原型
int fcntl(int fd, int cmd, .../*arg*/);
/*
参数:
-fd:表示需要操作的文件描述符
-cmd:表示对文件描述符进行何种操作
-F_DUPFD:复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符(返回值)
int ret = fcntl(fd,F_DUPFD);
-F_GETFL:获取指定的文件描述符文件状态flag
获取的flag和通过open函数传递的flag是一个东西
-F_SETFL:设置文件描述符文件状态flag
必选项:O_RDONLY, O_WRONLY, O_RDWR不可以被修改
可选项:O_APPEND(追加数据), O_NONBLOCK(非阻塞)等可修改
*/
示例:
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
int main()
{
//1.复制文件描述符
int fd0 = open("2.txt",O_RDONLY);
int ret0 = fcntl(fd0,F_DUPFD);
//2.修改或获取文件状态flag
int fd = open("1.txt",O_RDWR);
if(fd == -1)
{
perror("open");
return -1;
}
//获取文件描述符状态flag
int flag=fcntl(fd, F_GETFL);
if(flag == -1)
{
perror("fcntl");
return -1;
}
//修改文件描述符状态的flag,给flag加入O_APPEND这个标记
//原先的必选项不可修改,所以得|
flag |= O_APPEND; //flag = flag | O_APPEND
int ret = fcntl(fd, F_SETFL, flag);
if(ret == -1)
{
perror("fcntl");
return -1;
}
char* str="nihao";
write(fd,str,strlen(str));
close(fd);
return 0;
}