文件描述符
文件描述符实际上是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符,进程使用它来标识打开的文件。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
Linux系统为程序中每个打开的文件都分配一个文件描述符,文件IO操作通过文件描述符来完成。
文件描述符在形式上是一个顺序分配的非负整数。从0开始分配,依次递增。比如 0,1,2表示 stdin stdout stderr,一般最大打开的文件描述符数量为1024(0~1023)
fdopen
FILE *fdopen(int fd, const char *mode);
功能:文件IO转换成标准IO
参数;
fd: 文件描述符
mode: 打开方式
返回指:
成功:流指针
失败:NULL
例如:
int main(int argc, const char *argv[])
{
FILE *fp = fdopen(1, “w”);
fprintf(fp,“%d-%d-%d”,2023,2,15);
return 0;
}
open
#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);
参数:
pathname: 文件的路径名
flags: 打开方式
O_RDONLY:只读
O_WRONLY:可写
O_RDWR:读写
O_APPEND:追加
O_TRUNC:清零
O_CREAT:创建
mode: 文件的权限 0666
返回指:
成功:文件描述符fd
失败:-1
标准IO 文件IO
r O_RDONLY
r+ O_RDWR
w O_WRONLY | O_CREAT | O_TRUNC
w+ O_RDWR | O_CREAT | O_TRUNC
a O_WRONLY | O_CREAT | O_APPEND
a+ O_RDWR | O_CREAT | O_APPEND
例如:
int main(int argc, const char *argv[])
{
int fd = open(“./100.txt”, O_RDONLY | O_CREAT, 0666);//以0666权限创建100.txt,并以只读方式打开;
if(fd < 0) //if(fd == -1)
{
perror(“open”);
return -1;
}
printf(“open success!\n”);
close(fd);//关闭文件
return 0;
}
实际创建的文件权限需要经过一个公式计算得到: mode & (~umask)
以上案例为例:
mode = 0666 = 110110110
umask = 2 = 000000010
~umsk = 111111101
mode & ~umask=110110100 = 0664
0664为创建文件的实际权限;
read
ssize_t read(int fd, void *buf, size_t count);
功能:从文件中读取count字节的数据到内存
参数:
fd: 文件描述符
buf: 内存地址
count: 读取的字节数 100个字节
返回值:
成功:
>0 返回实际读取到的字节数
== 0 读到文件的末尾
失败:
<0 读取失败
例如:
int main(int argc, const char *argv[])
{
int fd = open(“./1.txt”, O_RDONLY);//以只读方式打开1.txt
if(fd < 0)
{
perror(“open”);
return -1;
}
printf(“open success!\n”);
char buf[100] = {0};
int num = 0;
while(1)
{
int n = read(fd , buf, 100);//在文本中一次读取100个字节到数组中,若读取成功返回值n为100;
if(n <0)
{
perror(“read”);
return -1;
}
else if(n == 0) //读到文件的末尾
{
break;
}
num += n; //num = num + n;
printf("n=%d\n", n);
printf("%s\n", buf);
}
printf(“num=%d\n”, num);//此时的num为文件的大小(总字节数),因此read可用返回值来求文件大小;
close(fd);
return 0;
}
write
ssize_t write(int fd, const void *buf, size_t count);
功能:把内存中的数据写入到文件中
参数:
fd: 文件描述符
buf: 内存地址
count: 写入的字节数 100个字节
返回值:
成功:返回实际写入的字节数
失败:-1
int main(int argc, const char *argv[])
{
int fd = open(“./99.txt”, O_WRONLY | O_CREAT , 0666);
if(fd < 0) //if(fd == -1)
{
perror(“open”);
return -1;
}
printf(“open success!\n”);
char buf[100] = {0};
gets(buf);
write(fd, buf, strlen(buf));//把数组中的内容写到文本中;
close(fd);
return 0;
}
综合练习:copy.c(把一个文件中的内容复制到另一个文件中)
int main(int argc, const char *argv[])
{
if(argc != 3)
{
printf(“%s file1 file2\n”, argv[0]);
return -1;
}
int fd1 = open( argv[1] , O_RDONLY);
if(fd1 < 0)
{
perror(“open”);
return -1;
}
int fd2 = open( argv[2] , O_WRONLY|O_CREAT|O_TRUNC, 0666);
if(fd2 < 0)
{
perror(“open”);
return -1;
}
printf(“open success!\n”);
char buf[100] = {0};
int num = 0;
while(1)
{
int n = read(fd1 , buf, 100);
if(n <0)
{
perror(“read”);
return -1;
}
else if(n == 0) //读到文件的末尾
{
break;
}
write(fd2, buf, n);
num += n; //num = num + n;
printf("n=%d\n", n);
printf("%s\n", buf);
memset(buf, 0, 100);
}
printf(“num=%d\n”, num);
printf(“copy success!\n”);
close(fd1);
close(fd2);
return 0;
}
lseek
off_t lseek(int fd, off_t offset, int whence);
功能:定位文件指针
参数:
fd: 文件描述符
offset: 偏移量
100:向后偏移100个字节
-100:向前偏移100个字节
whence: 基点
SEEK_SET:文件开头
SEEK_END:文件末尾
SEEK_CUR:文件当前位置
返回值:
成功:0
失败:-1
例如:lseek(fd, 0, SEEK_END);//从文件末尾往后移动100个字节;
综合练习:加密一张图片
int main(int argc, const char *argv[])
{
int fd = open(“./1.jpg”, O_RDWR);
if(fd == -1)
{
perror(“open”);
return -1;
}
//加密图片
char buf[10] = {0};
read(fd, buf,10);//从照片文本中从头读取10个字节到数组中;
int i;
for(i=0; i<5; i++)//在数组中交换第0位和第9位的数字,第1位和第8位,以此类推;
{
buf[i] ^= buf[9-i];
buf[9-i] ^= buf[i];
buf[i] ^= buf[9-i];
}
lseek(fd, 0, SEEK_SET);//把文本指针置为开头
write(fd, buf, 10);//把交换完的数组再写会文本中,完成图片加密,再运行一次则完成照片解密,因为数组中所有的数字已回归原位;
}
【目录操作函数】
opendir
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
功能:打开目录
参数:
name: 目录的路径名
返回值:
成功: DIR *
失败: NULL
readdir
struct dirent *readdir(DIR *dirp);
功能:读取目录信息
参数:
dirp: opendir的返回值
返回值:
成功: struct dirent *
失败: NULL(一直遍历,直到遍历目录完成返回NULL)
struct dirent {
ino_t d_ino; /* inode number /
off_t d_off; / not an offset; see NOTES /
unsigned short d_reclen; / length of this record /
unsigned char d_type; / type of file; not supported
by all filesystem types /
char d_name[256]; / filename */
};
closedir
int closedir(DIR *dirp);
综合案例:查看目录下的文件
int main(int argc, const char *argv[])
{
DIR *dirp = opendir(“./”);//打开根目录
if(dirp == NULL)
{
perror(“opendir”);
return -1;
}
struct dirent *p;
while(1)
{
p = readdir(dirp);
if(p == NULL) //遍历根目录完成
{
break;
}
if(strncmp(p->d_name, ".", 1) == 0)
{
continue; //略过这次循环,继续下一次循环(不打印隐藏文件)
}
printf("%s\n", p->d_name);//打印根目录下的文件名字
}
//puts(“”); //printf(“\n”); putchar(‘\n’);
closedir(dirp);//关闭目录
return 0;
}
【文件信息函数】
stat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
struct stat mybuf;
int stat(const char *path, struct stat *buf);
功能:得到文件的属性
参数:
path:文件路径名
buf: &mybuf
-rw-rw-r-- 1 farsight farsight 593 4月 19 09:26 01picture.c
-rw-rw-r-- 1 farsight farsight 537 4月 19 09:53 02readdir.c
-rw-rw-r-- 1 farsight farsight 161 4月 19 10:23 03stat.c
-rwxrw-rw- 1 farsight farsight 97893 4月 19 09:26 5.jfif
-rwxrwxr-x 1 farsight farsight 7357 4月 19 09:53 a.out
//查看设备号
major minor
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 /
uid_t st_uid; 所属用户ID / user ID of owner /
gid_t st_gid; 所属组ID / group ID of owner /
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 */
};
getpwuid
struct passwd *getpwuid(uid_t uid);
获取用户名
struct passwd {
char pw_name; / username */
char pw_passwd; / user password /
uid_t pw_uid; / user ID /
gid_t pw_gid; / group ID */
char pw_gecos; / user information */
char pw_dir; / home directory */
char pw_shell; / shell program */
};
getgrgid
struct group *getgrgid(gid_t gid);
获取组名
struct group {
char gr_name; / group name */
char gr_passwd; / group password /
gid_t gr_gid; / group ID */
char *gr_mem; / group members */
};
示例:stat.c
文件类型
{
常规文件:S_ISREG ‘-’
目录:S_ISDIR ‘d’
字符设备:S_ISCHR ‘c’
块设备:S_ISBLK ‘b’
管道:S_ISFIFO ‘p’
套接字:S_ISSOCK ‘s’
符号链接:S_ISLNK ‘l’
}
库的制作
1、什么是库
/lib/i386-linux-gnu
/usr/include
- 库是一种加密的二进制文件
- 需要被操作系统载入内存运行
- 相比于可执行程序,它不可以直接运行
- window 和 linux 都有自己的库,但是不兼容
- Linux系统的库有两种,1. 静态库 2. 共享库(又叫动态库)
了解: 静态库 动态库
linux *.a *.so
//window *.lib *.dll
2、静态库的制作和使用
1. 制作
$ gcc -c xxx.c -o xxx.o
$ ar -crs libxxx.a xxx.o
静态库的命名规范:
必须以lib开头,紧跟库的名字,跟扩展名 .a
例如: libxxx.a
-
使用
$ gcc main.c -L路径 -lxxx
-L: 指定静态库所在的目录
-l: 指定静态库的名字 xxx部分 -
运行
$ ./a.out (7258)
优点:a.out 运行后不需要库,可以直接运行
缺点:
每个a.out都要包含库,体积较大, 浪费资源;
对程序更新,部署,发布带来麻烦;
3、动态库的制作和使用
1. 制作
$ gcc -fPIC -c xxx.c -o xxx.o
$ gcc -shared -o libxxx.so xxx.o
动态库的命名规范:
必须以lib开头,紧跟库的名字,跟扩展名 .so
例如: libxxx.so
-
使用
$ gcc main.c -L路径 -lxxx$ ldd a.out # 用于查看可执行程序依赖的动态库有哪些
-
运行
$ ./a.out # 会报错 (7146)动态库的搜索方式(3种,任意选一种):
- 将动态库拷贝到 /lib/ 或者 /usr/lib/
$ sudo cp libxxx.so /usr/lib/
2.export LD_LIBRARY_PATH=.或者so所在的路径 (临时情况)
pwd
cd /etc/ld.so.conf.d
ls
sudo vi my.conf
添加路径
sudo ldconfig 生效 - 将动态库拷贝到 /lib/ 或者 /usr/lib/
特点:在编译时不会链接到可执行文件中,只是再其中保存一个索引,在运行时,才真正的链接(动态),因此可执行程序体积小。
优点:
a.out 体积较小, 节约资源;
只需要修改.so动态库,有利于程序的更新,部署,发布;
缺点:a.out 运行后需要库,不能直接运行。