【转】open创建文件失败(目录不存在),创建目录

本文详细介绍了fopen函数在文件路径不存在时的行为特征,并提供了如何使用access函数进行路径检查的方法,以及如何使用mkdir函数创建所需目录的具体步骤。
一、问题描述

      fopen/open函数使用过程发现,当fopen/open要打开的文件路径中,包含一个不存在路径时,不管fopen/open函数的第二参数设成什么,都无法建立该文件。实例:
char *filename = "/usr/local/ct/mirror_im_protocol.cfg";
FILE *fp = NULL;
 
if ((fp=fopen(filename,"a"))==NULL)
{
    printf("open file %s error\n",filename);
    return FAIL; 
}

上述实例中,若/usr/local/ct/不存在,fopen将不会建立mirror_im_protocol.cfg文件,导致fopen返回NULL。当/usr/local/ct/路径存在,而mirror_im_protocol.cfg不存在时,函数会自动创建该文件,并往文件中追加内容。

      因此,在使用fopen函数创建一个新文件时,一定要保证文件的路径存在,可以考虑在fopen函数前加一个路径检查。


二、路径检查

      access函数可以用来判断文件夹或者文件是否存在。

函数原型: int access(const char *filename, int mode);

所属头文件:io.h / unistd.h

filename:可以填写文件夹路径或者文件路径

mode:0 (F_OK) 只判断是否存在

               2 (R_OK) 判断写入权限

               4 (W_OK) 判断读取权限

               6 (X_OK) 判断执行权限

      用于判断文件夹是否存在的时候,mode取0,判断文件是否存在的时候,mode可以取0、2、4、6。 若存在或者具有权限,返回值为0;不存在或者无权限,返回值为-1。

错误代码

EACCESS 参数pathname 所指定的文件不符合所要求测试的权限。

EROFS 欲测试写入权限的文件存在于只读文件系统内。

EFAULT 参数pathname指针超出可存取内存空间。

EINVAL 参数mode 不正确。

ENAMETOOLONG 参数pathname太长。

ENOTDIR 参数pathname为一目录。

ENOMEM 核心内存不足

ELOOP 参数pathname有过多符号连接问题。

EIO I/O 存取错误。

特别提醒:使用access()作用户认证方面的判断要特别小心,例如在access()后再做open()的空文件可能会造成系统安全上的问题。

 实例:

#include <stdio.h>
#include <io.h>
#define  AUDI_PROCESS_PID_PATH  " /usr/local/ct/"
 
int main(void)
{
    if ( access(AUDI_PROCESS_PID_PATH, F_OK) == 0) )
         printf("AUDI_PROCESS_PID_PATH EXISITS!");
    else
         printf("AUDI_PROCESS_PID_PATH DOESN'T EXISIT!");
    return 0;
} 

当检查到路径不存在时,可通过下面的函数来进行路径的创建。

三、创建目录

原型:int mkdir (const char *filename, mode_t mode)

返回0表示成功,返回-1表述出错。

使用该函数需要包含头文件sys/stat.h

mode 表示新目录的权限,可以取以下值:

S_IRUSR
S_IREAD
Read permission bit for the owner of the file. On many systems thisbit is 0400. S_IREAD is an obsolete synonym provided for BSDcompatibility.
//这是对文件用户的读权限赋予

S_IWUSR
S_IWRITE
Write permission bit for the owner of the file. Usually 0200.S_IWRITE is an obsolete synonym provided for BSDcompatibility.
//这是对文件用户的写权限赋予

S_IXUSR
S_IEXEC
Execute (for ordinary files) or search (for directories) permissionbit for the owner of the file. Usually 0100. S_IEXEC is an obsoletesynonym provided for BSD compatibility.
//这是对文件用户执行权限赋予

S_IRWXU
This is equivalent to (S_IRUSR | S_IWUSR | S_IXUSR).
//这包含了以上三种用户权限

S_IRGRP
Read permission bit for the group owner of the file. Usually040.
//这是对文件用户组读权限赋予

S_IWGRP
Write permission bit for the group owner of the file. Usually020.
//这是对文件用户组写权限赋予

S_IXGRP
Execute or search permission bit for the group owner of the file.Usually 010.
//这是对文件用户组执行权限赋予

S_IRWXG
This is equivalent to (S_IRGRP | S_IWGRP | S_IXGRP).
//这包含了以上三种组用户权限

S_IROTH
Read permission bit for other users. Usually 04.
//对其他用户的读操作赋予

S_IWOTH
Write permission bit for other users. Usually 02.
//对其他用户的写操作赋予

S_IXOTH
Execute or search permission bit for other users. Usually 01.
//对其他用户的执行操作赋予

S_IRWXO
This is equivalent to (S_IROTH | S_IWOTH | S_IXOTH).
//这包含了以上三种组其他用户权限赋予

S_ISUID
This is the set-user-ID on execute bit, usually 04000. See HowChange Persona.
//这下面就不说明了,因为这又涉及set uid ,set gid,stickybit这三个权限,后面有一篇转贴再说明,不过这也只有我这样的菜鸟不知道的啦.

S_ISGID
This is the set-group-ID on execute bit, usually 02000. See HowChange Persona.


S_ISVTX

实例:
#include <sys/types.h>
#include <sys/stat.h> 
 
int main()
{
    int status; 
    status = mkdir("/home/newdir", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 
 
    return 0;
}

这样就创建了一个newdir目录,权限通过ls -al 查看为
drwxr-xr-x
跟用linux命令mkdir创建的目录权限位一致。

四、fopen函数

函数原型:FILE * fopen(const char * path,const char * mode);

返回值:文件顺利打开后,向该流的文件指针就会被返回。如果文件打开失败则返回NULL,并把错误代码存在errno中。

一般而言,打开文件后会做一些文件读取或写入的动作,若打开文件失败,接下来的读写动作也无法顺利进行,所以一般在fopen()后作错误判断及处理。

参数说明:

参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。


mode有下列几种形态字符串:

“r” 以只读方式打开文件,该文件必须存在。

“r+” 以可读写方式打开文件,该文件必须存在。

”rb+“ 读写打开一个二进制文件,允许读写数据,文件必须存在。

“w” 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。

“w+” 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。

“a” 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)

”a+“ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)

“wb” 只写打开或新建一个二进制文件;只允许写数据。

“wb+” 读写打开或建立一个二进制文件,允许读和写

“wx” 创建文本文件,只允许写入数据.[C11]

“wbx” 创建一个二进制文件,只允许写入数据.[C11]

”w+x“ 创建一个文本文件,允许读写.[C11]

“wb+x” 创建一个二进制文件,允许读写.[C11]

“w+bx” 和"wb+x"相同[C11]

<think>好的,我需要回答用户关于在C语言中使用open函数创建新文时是否需要对目录进行fsync以确保数据一致性的问题。首先,我需要理解用户的问题核心:创建时,目录的元数据是否需要同步到磁盘来保证数据一致性。 根据之前的引用内容,特别是引用[1]和[2],fsync作用于文描述符,确保数据和元数据写入磁盘。但用户现在关心的是目录的同步,因为创建会影响目录的元数据,比如目录中的文列表、大小等。 我需要回忆文系统的工作原理。当创建一个新文时,仅文本身的数据和元数据需要写入,其所在的目录也需要更新,以记录新文存在。这些更新可能缓存在内存中,会立即写入磁盘。如果系统在写入前崩溃,可能导致目录信息完整,文虽然存在数据,但目录中没有记录,导致数据一致。 因此,为了确保目录的元数据也持久化,需要在文创建后对目录执行fsync。但C语言中的open函数默认会同步目录,需要显式操作。用户可能需要使用open创建后,再打开目录描述符,并对目录调用fsync。 需要注意,如何获取目录的文描述符。通常,使用open函数打开目录路径,并指定O_RDONLY标志,因为目录能以写方式打开。然后对该描述符调用fsync。 同时,需要考虑性能影响,因为fsync是阻塞操作,会降低性能,但保证了数据一致性。根据应用场景决定是否需要这样做,比如在数据库或关键事务处理中,这种同步是必要的。 另外,是否有其他函数如fdatasync可用?根据引用[2],fdatasync可能同步所有元数据,但目录的元数据可能仍需要完整同步,所以fsync更合适。 总结步骤:创建后,打开目录,fsync目录,关闭目录描述符。需要注意错误处理,每次系统调用都可能失败,需要检查返回值。 最后,回答用户时要结构清晰,分点说明必要性、操作方法、注意事项,并给出示例代码。同时生成相关问题,帮助用户深入理解。</think>在C语言中使用$open()$函数创建新文时,若需要确保数据一致性,对目录执行$fsync()$是必要的。以下是关键分析步骤: --- ### 一、目录同步的必要性 1. **文系统操作特性** 创建涉及两个操作: - 文本身的创建(数据块和inode写入) - 目录元数据的更新(记录新文名和inode映射) 这两个操作可能被延迟写入磁盘,仅停留在内核缓冲区中[^1]。 2. **系统崩溃风险** 若未同步目录,系统崩溃时可能出现: - 文数据已写入,但目录未记录该文(表现为"文丢失") - 文被其他进程访问时出现一致状态 --- ### 二、实现方法 1. **操作流程** ```c int fd = open("newfile.txt", O_CREAT | O_WRONLY, 0644); write(fd, data, data_size); fsync(fd); // 同步文内容 close(fd); int dir_fd = open(".", O_RDONLY); // 打开当前目录 fsync(dir_fd); // 同步目录元数据 close(dir_fd); ``` 2. **关键点说明** - 目录需通过文描述符操作,使用$O_RDONLY$标志打开即可 - 需先同步文再同步目录,确保操作顺序正确 - 调用$fsync()$会阻塞直到磁盘确认写入完成[^2] --- ### 三、性能与一致性权衡 | 方法 | 一致性级别 | 性能影响 | |------|------------|----------| | 仅文$fsync()$ | 文数据安全,目录可能丢失记录 | 低延迟 | | 文+目录$fsync()$ | 完整一致性 | 高延迟 | | 无同步 | 数据可能丢失 | 最高性能 | 建议在数据库、交易系统等场景强制同步目录[^1]。 --- ### 四、扩展优化 1. **使用$fdatasync()$替代** 若需要更新目录的元数据(如修改时间),可用$fdatasync()$减少磁盘写入量[^2] 2. **文系统挂载选项** 通过`mount -o sync`强制所有写入同步,但会显著降低性能 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值