本文主要是介绍(P7-P8)文件与IO:文件共享、dup、fcntl,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 1.文件共享
- 2.复制文件描述符
1.文件共享
-
打开文件内核数据结构
(1)内核中如何表示打开的文件的?
每个进程都有1个文件描述符表;
refcnt表示文件当前的引用计数;
stat状态返回的信息都保存在v节点信息上面;
i节点信息保存了文件存放在磁盘的哪个位置;
-
一个进程两次打开同一个文件
一个进程两次打开同一个文件,V节点信息是共享的;
文件状态标志是不一样的;
-
eg:代码:P7fileshare.c
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)int main(int argc, char *argv[])
{int fd1;int fd2;char buf1[1024] = {0};char buf2[1024] = {0};fd1 = open("test.txt", O_RDONLY);if (fd1 == -1)ERR_EXIT("open error");read(fd1, buf1, 5);printf("buf1=%s\n", buf1);fd2 = open("test.txt", O_RDWR);if (fd2 == -1)ERR_EXIT("open error");read(fd2, buf2, 5);printf("buf1=%s\n", buf2);write(fd2, "AAAAA", 5);memset(buf1, 0, 5);read(fd1, buf1, 5);printf("buf1=%s\n", buf1);close(fd1);close(fd2);return 0;
}
-
测试:
一个进程两次打开同一个文件,这两次打开文件的文件表的文件状态标志、文件偏移位置的都是独立的,互不影响,相互独立
注意:V节点信息是共享的
文件2向test.txt文件写入了AAAAA,会改变i节点信息所指向磁盘中的数据。
但是偏移量是从5开始的,所以往后读就读到了AAAAA
-
两个进程打开同一个文件
每个进程的文件表都是独立的。
进程打开文件会返回空闲的描述符,所以第一个进程的fd=3,第二个fd=4;
文件打开的状态标志和偏移量都不是共享的;
2.复制文件描述符
- 实现IO重定向
复制文件描述符的含义:得到一个新的文件描述符指向同一个文件
进程2得到一个空闲的描述符4与进程1的文件描述符3指向同一个文件,引用计数refcnt为2
- eg:代码:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)int main(int argc, char *argv[])
{int fd1;fd1 = open("test2.txt", O_RDONLY);if (fd1 == -1)ERR_EXIT("open error");//返回一个新的文件描述符,与fd指向同一个文件//当前新的文件描述符是1,因为将fd=1关闭了close(1);//将fd=1关闭了,所以返回的文件描述符的值为1,所以1和fd指向同一个文件dup(fd);//上面的2行,等价于dup2(fd,1);//当前的标准输出不再指向屏幕,而是指向了text2printf("hello\n");return 0;
}
-
测试:已经重定向到文件中了
-
复制文件描述符有三种方法
(1)dup
搜索空闲的文件描述符(从0开始搜索可用的fd),与要复制的文件描述符指向同一个文件
(2)dup2
强制使用文件描述符,与要复制的文件描述符指向同一个文件(dup与close的结合)
(3)fcntl:也具有复制fd的功能
man 2 fcntl
int fcntl(int fd, int F_DUPFD, fd_start);
fd:要复制的fd
cmd:命令,复制的话使用F_DUPFD
从fd_start开始搜索可用的fdint fcntl(int fd, int cmd, ... /* arg */ );
功能:操作文件描述符,改变已打开的文件的属性
arg是操作选项,依据cmd而定
- fcntl常用操作
(1)复制文件描述符
F_DUPFD (long)
(2)文件描述符标志
F_GETFD(void)
F_SETFD(long)
(3)文件状态标志
F_GETFL(void)
F_SETFL(long)
(4)文件锁
F_GETTLK
F_SETLK,F_SETLKW
文件锁的结构体如下:
struct flock {...文件锁类型:读锁(就是共享锁,读锁可被多个进程增加)、写锁(就是排他锁,只允许一个进程增加锁)、清除锁short l_type; /* Type of lock: F_RDLCK,F_WRLCK, F_UNLCK */锁的范围:whence和start决定加锁的起始点、len决定锁的长度和范围 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(F_GETLK only) */当前哪个进程阻塞当前的锁...};
- eg:代码:P8fcntl.c
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)int main(int argc, char *argv[])
{int fd1;fd1 = open("test.txt", O_RDONLY);if (fd1 == -1)ERR_EXIT("open error");// close(1);//将fd=1关闭了,所以返回的文件描述符的值为1,所以1和fd指向同一个文件// dup(fd);// //上面的2行,等价于dup2(fd,1);//fcntl模拟复制文件描述符close(1);//关闭标准输出,因为默认0,1,2已经被打开了//因为fcntl是从0搜索的,所以当前得到可以用的fd=1if (fcntl(fd, F_DUPFD, 0) <0)ERR_EXIT("fcntl with error");printf("hello\n");//输出到标准输出,标准输出已经重定位到test.txtreturn 0;
}
-
测试:已经重定向到文件中了
-
eg:代码:P8fcntl2.c
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)void set_flag(int fd, int flags);
void clr_flag(int fd, int flags);
int main(int argc, char *argv[])
{char buf[1024] = {0};int ret;int flags;//先获取fd的状态flags = fcntl(0, F_GETFL, 0);if (flags == -1)ERR_EXIT("fcntl get flag error");//再设置fd的状态,因为若直接获取,会移除以前fd的其它状态ret = fcntl(0, F_SETTFL, flags | O_NONBLOCK);if (flags == -1)ERR_EXIT("fcntl set flag error");//set_flag(fd, O_NONBLOCK);//clr_flag(fd, O_NONBLOCK);ret =read(0, buf, 1024);if (ret == -1)ERR_EXIT("read error");printf("buf=%s\n", buf);return 0;
}void set_flag(int fd, int flags)
{//先获取fd的状态int val;val = fcntl(fd, F_GETFL, 0);if (val == -1)ERR_EXIT("fcntl get flag error");val | =flags;//再设置fd的状态,因为若直接获取,会移除以前fd的其它状态ret = fcntl(fd, F_SETFL, val);if (flags == -1)ERR_EXIT("fcntl set flag error");
}//清除fd的状态
void clr_flag(int fd, int flags)
{//先获取fd的状态int val;val = fcntl(fd, F_GETFL, 0);if (val == -1)ERR_EXIT("fcntl get flag error");val & = ~flags;//再设置fd的状态,因为若直接获取,会移除以前fd的其它状态ret = fcntl(fd, F_SETFL, val);if (flags == -1)ERR_EXIT("fcntl set flag error");
}
-
测试:fd文件状态被改成了非阻塞模式。所以产生了EAGAIN的错误
使用set_flag(fd, O_NONBLOCK);
clr_flag(fd, O_NONBLOCK);的结果
因为read从非阻塞变成阻塞的,read设置成阻塞的,只要获取到输出就会退出了
-
eg:代码:P8fcntl3.c
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)int main(int argc, int argv*[])
{//要对文件加读锁,那么要对文件有读的权限//要对文件加写锁,那么要对文件有写的权限 int fd;fd = open("test2.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);if (fd == -1)ERR_EXIT("open error");struct flock lock;memset(&lock, 0, sizeof(lock));lock.l_type = F_WRLCK;lock.l_whence = SEEK_SET;//基准位置lock.l_start = 0;//偏移位置lock.len = 0;//为0表示锁定整个文件if (fcntl(fd, F_SETLK, &lock) == 0){printf("lock success\n");printf("press any key to unlock\n");getchar();//按下任意键lock.l_type = F_UNLOCK;if (fcntl(fd, F_SETLK, &lock) == 0)printf("unlock success\n");elseprintf("unlock fail\n");}else{ERR_EXIT("lock fail\n");}//就算没解锁,当进程退出的时候也会释放锁return 0;}
-
测试:
进程1
进程2加锁失败
-
eg:代码:P8fcntl4.c
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)int main(int argc, int argv*[])
{//要对文件加读锁,那么要对文件有读的权限//要对文件加写锁,那么要对文件有写的权限 int fd;fd = open("test2.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);if (fd == -1)ERR_EXIT("open error");struct flock lock;memset(&lock, 0, sizeof(lock));lock.l_type = F_WRLCK;lock.l_whence = SEEK_SET;//基准位置lock.l_start = 0;//偏移位置lock.len = 0;//为0表示锁定整个文件if (fcntl(fd, F_SETLKW, &lock) == 0){printf("lock success\n");printf("press any key to unlock\n");getchar();//按下任意键lock.l_type = F_UNLOCK;if (fcntl(fd, F_SETLK, &lock) == 0)printf("unlock success\n");elseprintf("unlock fail\n");}else{ERR_EXIT("lock fail\n");}//就算没解锁,当进程退出的时候也会释放锁return 0;}
-
测试:
加锁还可以使用F_SETLKW
F_SETLKW区别于F_SETLK的原因是:若锁被另外一个进程锁施加了,那么其它进程试图加锁就会阻塞,直到另外一个进程解锁,才能够返回
其它进程会阻塞等待别的进程释放锁
按下任意键释放锁
-
Makefifile
.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
BIN=01fcntl 02fcntl 03fcntl
all:$(BIN)
%.o:%.c$(CC) $(CFLAGS) -c $< -o $@
clean:rm -f *.o $(BIN)
这篇关于(P7-P8)文件与IO:文件共享、dup、fcntl的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!