linux用什么打开.o文件,Linux文件I/O---打开/创建文件

打开文件

Open介绍

文件open在手册中有两个函数原型,如下所示:

#include

#include

#include

int open(const char *pathname, int flags);

int open(const char *pathname, int flags, mode_t mode);

//返回值:成功返回新分配的文件描述符,失败返回-1并设置errno

参数解释:

pathname :要打开或创建的文件名,和fopen一样,pathname既可以是绝对路径也可以是相对路径;

flags         :用于指示打开文件的选项,常用的有O_RDONLY、O_WRONLY、O_RDWR。这三个选项必须有一个且只能有一个被指定。除以上三个选项,Linux平台还支持更多的选项,可在fcntl.h文件中找到

O_APPEND:每次进行写操作时,内核都会先定位到文件尾,在执行写操作

O_ASYNC:使用异步I/O模式

O_CLOEXEC:在打开文件的时候,就为文件描述符设置FD_CLOEXEC标志。这是一个新的选项,用于解决在多线程下fork与用于fcntl设置FD_CLOEXEC的竞争。某些应用使用fork来执行第三方业务,为了避免泄露已打开文件的内容,那些文件会设置FD_CLOEXEC标志,但是fork与fcntl是两次调用,在多线程下,可能会在fcntl调用之前,就已经fork出子进程了,从而导致该文件句柄暴露给子进程

O_CREAT:当文件不存在的时候,就创建文件

O_DIRECT:对该文件进行直接I/O,不使用VFS Cache

O_DIRECTORY:要求打开的路径必须是目录

O_EXCL:该标志用于确保是此次调用创建的文件,需要与O_CREAT同时使用,当文件存在时,open函数返回失败

O_LARGEFILE:表明文件为大文件

O_NOATIME:读取文件时,不更新最后的访问时间

O_NONBLOCK、O_NDELAY:将该文件描述符设置为非阻塞(默认都是阻塞的)

O_SYNC:设置为I/O同步模式,每次进行写操作时都会将数据同步到磁盘,然后write才能返回

O_TRUNC:在打开文件的时候,将文件长度截断为0,需要与O_RDWR或O_WRONLY同时使用。在写文件时,如果是作为新文件重新写入,一定要使用O_TRUNC标志,否则可能会造成旧内容依然存在于文件中的错误。

open函数原型:

当我们调用open函数时,实际上调用的是glibc封装的函数,然后由glibc通过自陷指令,进行真正的系统调用。也就是说所有的系统调用都要先经过glibc才会进入操作系统。这样的话是祭祀夯实glibc提供了一个变参函数open来满足两个函数原型,然后通过glibc的变参函数open实现真正的系统调用来调用open函数,open函数的声明如下所示:

extern int open (__const char *__file, int __oflag, ...) __nonnull ((1));

Open源码跟踪

在内核sys_open源码中sys_open->do_sys_open源码(/linux-2.6.30/fs/open.c)如下所示:

long do_sys_open(int dfd, const char __user *filename, int flags, int mode)

{

char *tmp = getname(filename);

int fd = PTR_ERR(tmp);

if (!IS_ERR(tmp)) {

fd = get_unused_fd_flags(flags);

if (fd >= 0) {

struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);

if (IS_ERR(f)) {

put_unused_fd(fd);

fd = PTR_ERR(f);

} else {

fsnotify_open(f->f_path.dentry);

fd_install(fd, f);

}

}

putname(tmp);

}

return fd;

}

打开文件时,内核主要消耗了两种资源:文件描述符与内核管理文件结构file

如何选择文件描述符

根据POSIX标准,当获取一个新的文件描述符时,要返回最低的未使用的文件描述符。在Linux中,通过do_sys_open->get_unused_fd_flags_->alloc_fd(0, (flags))来选择文件描述符,代码(/linux-2.6.30/fs/open.c)如下所示:

/*

* allocate a file descriptor, mark it busy.

*/

int alloc_fd(unsigned start, unsigned flags)

{

struct files_struct *files = current->files;

unsigned int fd;

int error;

struct fdtable *fdt;

spin_lock(&files->file_lock);

repeat:

fdt = files_fdtable(files);

fd = start;

if (fd < files->next_fd)

fd = files->next_fd;

if (fd < fdt->max_fds)

fd = find_next_zero_bit(fdt->open_fds->fds_bits,

fdt->max_fds, fd);

error = expand_files(files, fd);

if (error < 0)

goto out;

/*

* If we needed to expand the fs array we

* might have blocked - try again.

*/

if (error)

goto repeat;

if (start <= files->next_fd)

files->next_fd = fd + 1;

FD_SET(fd, fdt->open_fds);

if (flags & O_CLOEXEC)

FD_SET(fd, fdt->close_on_exec);

else

FD_CLR(fd, fdt->close_on_exec);

error = fd;

#if 1

/* Sanity check */

if (rcu_dereference(fdt->fd[fd]) != NULL) {

printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);

rcu_assign_pointer(fdt->fd[fd], NULL);

}

#endif

out:

spin_unlock(&files->file_lock);

return error;

}

文件描述符fd与文件管理结构file

内核使用fd_install将文件管理结构file与fd组合起来,具体的操作如下代码(/linux-2.6.30/fs/open.c)所示:

/*

* Install a file pointer in the fd array.

*

* The VFS is full of places where we drop the files lock between

* setting the open_fds bitmap and installing the file in the file

* array. At any such point, we are vulnerable to a dup2() race

* installing a file in the array before us. We need to detect this and

* fput() the struct file we are about to overwrite in this case.

*

* It should never happen - if we allow dup2() do it, _really_ bad things

* will follow.

*/

void fd_install(unsigned int fd, struct file *file)

{

struct files_struct *files = current->files;

struct fdtable *fdt;

spin_lock(&files->file_lock);

fdt = files_fdtable(files);

BUG_ON(fdt->fd[fd] != NULL);

rcu_assign_pointer(fdt->fd[fd], file);

spin_unlock(&files->file_lock);

}

当用户使用fd与内核交互时,内核可以使用fd从fdt->fd[fd]中得到内部管理文件的结构struct file

创建文件

creat函数用于创建一个新的文件,其等价于open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode).

creat函数引入的原因:由于历史原因,早期的Unix版本中,open的第二个参数只能是0(O_RDONLY)、1(O_WRONLY)或者2(O_RDWR),这样就没有办法打开一个不存在的文件,因此一个独立的系统调用creat被引入,用于创建新文件。现在的open函数通过使用O_CREAT和O_TRUNC选项,可以实现creat的功能,因此creat已经不是必要的了。

内核creat的实现代码(/linux-2.6.30/fs/open.c)如下所示:

/*

* For backward compatibility? Maybe this should be moved

* into arch/i386 instead?

*/

SYSCALL_DEFINE2(creat, const char __user *, pathname, int, mode)

{

return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);

}

这样就确定了creat无非是open的一种封装实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值