Day4
lseek
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
- 设置文件位置指针
- 返回值 文件指针的位置 功能类似于ftell
练习1:实现一个Linux系统下计算文件大小的函数使用系统调用
homework
练习2:实现一个带覆盖检查的cp命令
homework
dup/dup2
#include <unistd.h>
int dup(int oldfd);
- 复制文件描述符,操作系统会从未使用的文件描述符中选择一个小的返回
- oldfd 被复制的文件描述符
int dup2(int oldfd, int newfd);
- 复制指定的文件描述符,如果newfd已经被使用,则先关闭再复制
- oldfd
- newfd
- 返回
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
void init(void){
int fd = open("log.txt",O_RDWR|O_CREAT|O_APPEND,0644);
dup2(fd,1);//如果是0,可以使用gets从日志中读取一行
}
int main(){
init();//init后printf()打印的内容会直接写入log.txt
int fd = dup(2);//fd = 1/2时(STDOUT/STDERR)向fd写入的数据会直接打印
write(fd,"hello",5);
}
标准IO与系统IO比较
练习3 分别使用标准IO与系统IO随机写入100,0000个整数到文件,比较哪一种方法更快,为什么?homework
标准IO使用了缓冲区技术,而每次调用系统IO都要切换用户态和内核态
文件同步
sync/fsync/fdatasync
硬盘上一般会有一些缓冲区以此来提高数据写入效率,操作系统写入数据其实只是写入缓冲区,直到缓冲区满,才排队写入硬盘中
这种操作降低了写入的次数,但是提高了数据写入的延时,导致缓冲区中的数据与磁盘中的内容不同步
注意 此处所讲的缓冲区都指硬盘缓存
void sync(void);
- 把所有缓冲区中的数据全部同步到磁盘
- 注意 只是将数据同步到磁盘的命令,并不等等执行完后才返回,而是命令发布后立即返回
int fsync(int fd);
- 指定df文件的缓冲区数据同步到磁盘,只针对一个文件,数据同步到磁盘后才返回
int fdatasync(int fd);
- 指定df文件的缓冲区数据同步到磁盘,但仅是文件的数据并不同步文件的属性
fcntl
int fcntl(int fd,int cmd,.../* arg */);
- cmd 操作指令,不同的操作指令决定后续参数的个数和类型
- 注意 这是个变长参数的函数
int fcntl(int fd,int cmd, long newfd);
- cmd F_DUPFD
- 复制文件描述符,与fd操作同一个文件
- 返回值 如果newfd没有使用则返回newfd;如果nrefd已经被占用,则返回一个不小于newfd的文件描述符
练习1:利用fcntl实现dup,dup2的功能
homework
int fcntl(int fd, int cmd, void/long);
-
设置或获取文件描述符标志
-
cmd
- F_GETFD
- F_SETFD
目前只能设置FD_CLOEXEC标志位
-
返回值 0 新进程保持打开状态,1 新进程中关闭该文件描述符
int fcntl(int fd, int cmd, void/long);
- 获取文件状态标志(当前文件打开的权限以及打开的方式)
- cmd:
F_GETFL
voidO_CREAT,O_EXCL,O_NOCTTY,O_TRUNC
不能获取到
- 返回值 带有文件状态标志的int类型状态,需要与各标志相与得到
F_SETFL
long- 仅能设置的有
O_APPEND,O_ASYNC,O_DIRECT,O_NOATIME,O_NONBLOCK
- 仅能设置的有
- 返回值 成功0 失败-1
#include <...>
void file_flags(int fd){
struct Flags{
int flag;
char* name;
}flags[] = {
{O_RDONLY,"O_RDONLY"},
{O_WRONLY,"O_WRONLY"},
{O_RDWR,"O_RDWR"},
{O_APPEND,"O_APPEND"},
{O_ASYNC,"O_ASYNC"},
{O_CLOEXEC,"O_CLOEXEC"},
{O_SYNC,"O_SYNC"}
};
int flags = fcntl(fd,F_GETFL);
for(int i=0;i<sizeof(flags)/sizeof(flags[0]);i++){
if(flags[i].flag&flag)
printf("%s\n",flags[i].name);
}
}
int main(){
int fd = open("A.TXT",O_WRONLY|O_RDONLY|O_SYNC|O_CLOEXEC);
fcntl(fd,F_SETFL,O_ASYNC);
file_flags(fd);
close(fd);
}
int fcntl(int fd, int cmd, struct* flock);
- 为文件加锁,能所整个文件,或其中的一部分内容
- cmd
F_GETLK
获取锁的信息F_SETLK
设置锁F_SETLKW
测试锁
- 读锁和读锁不冲突,读锁与写锁冲突,写锁与写锁冲突
- 加锁并不能让其他进程打不开文件或不能操作,而是使用者都要遵守锁的约定,确保文件不混了(劝诫锁)
struct flock {
...
short l_type; /* Type of lock: F_RDLCK,
F_WRLCK, F_UNLCK */
short l_whence; /* How to interpret l_start:
SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* Starting offset for lock */
off_t l_len; /* Number of bytes to lock */
pid_t l_pid; /* PID of process blocking our lock
(set by F_GETLK and F_OFD_GETLK) */
...
};
#include <...>
int rlock(int fd){
struct flock lock = {F_RDLOCK,SEEK_SET,0,0,-1};
return fcntl(fd,f_SETLK,&lock);
}
int wlock(int fd){
struct flock lock = {F_WRLOCK,SEEK_SET,0,0,-1};
return fcntl(fd,f_SETLK,&lock);
}
void show_lock(int fd){
struct flock lock = {};
printf("获取锁%s\n",fcntl(fd,F_GETLK,&lock)?"失败":"成功");
if(lock.l_type == F_UNLCK){
printf("无锁\n");
}else{
printf("加锁进程:%d\n",lock.l_pid);
printf("锁类型:%s\n",lock.l_type == F_RDLOCK?"读锁":"写锁");
printf("从文件");
switch(lock.l_whence){
case SEEK_SET:printf("从文件开头");break;
case SEEK_CUR:printf("从当前位置");break;
case SEEK_END:printf("从文件末尾");break;
}
if(lock.l_start > 0){
printf("右移%lu个字节开始",lock.l_start);
}else if(lock.l_start < 0){
printf("左移%lu个字节开始",lock.l_start);
}
if(lock.len){
printf("加锁%lu个字节\n",lock.l_len);
}
else{
printf("加锁到文件末尾\n");
}
}
}
int main(){
int fd = open("a.txt",O_RDWR);
printf("%d\n",rlock(fd));
printf("%d\n",getpid());
getchar();
}