linux系统中在打开描述符时,默认打开的都是最小的空闲描述符。第一个被打开的文件描述符一般是3,默认0/1/2 为输入输出和标准错误描述符。
那么如果我们想打开某一个特定的描述符时要如何操作呢?。
更进一步说我们需要打开一个指定范围的描述符,用于给不同的模块保留不同范围的描述符,以免不同的模块之间描述符相互交织。那么如何操作呢?。
这里主要需要解决的是两个问题,一个是如何打开特定的文件描述符,另一个是如何保证这个特定的描述符是可以使用的。前者使用dup2将文件描述符进行复制。后者则使用fcntl对文件描述符的状态进行判断。
dup2 实现打开特定的文件描述符
man dup可以查看到dup2的定义如下
int dup2(int oldfd, int newfd);
dup2()
The dup2() system call performs the same task as dup(), but instead of using the lowest-numbered unused file descriptor, it uses the descriptor number specified in newfd. If
the descriptor newfd was previously open, it is silently closed before being reused.
执行类似dup的操作,dup函数创建当前fd的拷贝,使用最小的未使用的文件描述符返回作为新描述符。dup2使用指定的描述符newfd作为拷贝的目标。如果newfd之前是打开的,就会直接关闭掉然后重用。
使用dup2 以指定的描述符打开文件的形式如下,这样path就是以target_fd来打开了。
int open_target_fd(char * path,int target_fd){
int fd = 0;
fd = open(path,O_RDWR|O_CREAT,0777);
if (fd < 0 ){
perror("open");
return -1;
}else{
dup2(fd,target_fd);
close(fd);
return target_fd;
}
}
fcntl判断文件描述符是否在被使用
fcntl主要是通过各种命令来对fd的状态进行查询和修改。
int fcntl(int fd, int cmd, ... /* arg */ );
fcntl() performs one of the operations described below on the open file descriptor fd. The operation is determined by cmd.
F_GETFL (void)
Get the file access mode and the file status flags; arg is ignored.
RETURN VALUE
F_GETFL Value of file status flags.
通过如下的check_fd_inuse函数可以判断fd是否在被使用,如果返回1就是正在被使用,如果返回0就是没有被使用。
int check_fd_inuse(int fd){
struct stat _stat;
int ret = -1;
if(0 <= fcntl(fd, F_GETFL,0)) {
if(0 == fstat(fd, &_stat)) {
if(_stat.st_nlink >= 1){
ret = 0;
return 1 ;
}
else{
printf("File was deleted!\n");
return 0 ;
}
}else{
perror("fstat error\n");
return 0;
}
}else{
printf("fcntl error %d errono{%d}\n",fd,errno);
return 0;
}
}
实现打开指定范围的描述符
将上述两个主要环节进行组合就可以实现打开指定范围的描述的功能。
int open_specified_fd(int value){
int fd = 0;
fd = open("/dev/null",O_RDONLY);
dup2(fd,value);
close(fd);
}
int check_fd_inuse(int fd){
struct stat _stat;
int ret = -1;
if(0 <= fcntl(fd, F_GETFL,0)) {
if(0 == fstat(fd, &_stat)) {
if(_stat.st_nlink >= 1){
ret = 0;
return 1 ;
}
else{
printf("File was deleted!\n");
return 0 ;
}
}else{
perror("fstat error\n");
return 0;
}
}else{
printf("fcntl error %d errono{%d}\n",fd,errno);
return 0;
}
}
/*start from 500 end to 600*/
int open_fd_in_range(char * path){
int start = 500;
int fd = 0;
while(1){
if (check_fd_inuse(start) == 0){
break;
}else{
start++;
if (start > 600){
return -1;
}
}
}
fd = open(path,O_RDWR|O_CREAT,0777);
if (fd < 0 ){
perror("open");
return -1;
}else{
dup2(fd,start);
close(fd);
return fd;
}
}
int main(){
open_specified_fd(500);
open_specified_fd(501);
open_fd_in_range("./123");
open_fd_in_range("./456");
while(1){
sleep(1000);
}
}
查看对应进程的proc的结果如下,可以看到打开了指定范围的描述符且:
lrwx------ 1 pi pi 64 Feb 26 18:14 0 -> /dev/pts/0
lrwx------ 1 pi pi 64 Feb 26 18:14 1 -> /dev/pts/0
lrwx------ 1 pi pi 64 Feb 26 18:14 2 -> /dev/pts/0
lr-x------ 1 pi pi 64 Feb 26 18:14 500 -> /dev/null
lr-x------ 1 pi pi 64 Feb 26 18:14 501 -> /dev/null
lrwx------ 1 pi pi 64 Feb 26 18:14 502 -> /path/to/test/123
lrwx------ 1 pi pi 64 Feb 26 18:14 503 -> /path/to/test/456
参考:
https://www.man7.org/linux/man-pages/man2/fcntl.2.html]
https://blog.csdn.net/kangear/article/details/42805393 (该博客中描述的判断文件描述符有效的办法,个人认为存在少许的问题)