基础提供程序在 open 上失败_Linux IO操作应用编程基础

276f5112bf35e395a4decc9b9cef5e71.png
SYNOPSIS 
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

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

       int creat(const char *pathname, mode_t mode);

open函数成功返回文件描述符,失败返回 -1

pathname是要打开的文件名,包含路径,缺省为当前路径
flags参数可用来说明此函数的多个选项,用下列一个或多个常量进行“或”运算构成flags参数,其中以下三个参数必须包含一个:

O_RDONLY :  只读打开
O_WRONLY :  只写打开
O_RDWR   :  读、写打开

大多数实现将 O_RDONLY 定义为 0,O_WRONLY定义为 1,O_RDWR 定义为 2,以与早期的程序兼容。此外还有许多参数,具体的大家可以自行去man。

如果使用O_CREAT标志创建文件,则需要设定mode参数来表示文件的访问权限。


creat函数用来创建一个新文件,成功返回文件描述符,失败返回 -1
此函数等效于:

open(pathname, O_WRONLY|O_CREAT|O_TRUNC,mode);

在早期的Unix版本中,open的第二个参数只能是 0、1 或 2.无法打开一个尚未存在的文件,因此需要另一个系统调用creat来创建新文件。现在open函数提供了选项O_CREAT 和 O_TRUNC,于是也就不再需要单独的creat函数了。

creat函数的另一个不足时它以只写方式打开所创建的文件。在提供open的新版本之前,如果要创建一个临时文件,并要先写该文件然后读该文件,则必须先调用creat、close,然后再调用open。


close函数原型:

SYNOPSIS
       #include <unistd.h>

       int close(int fd);
       //成功返回 0,失败返回 -1

接下来我们看一个简单的打开文件的例子:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
        int fd;

        fd = open("Love",O_CREAT|O_RDONLY,0666);

        if(fd < 0)
        {
                perror("open");
                exit(1);
        }

        printf("open %d.n",fd);

        close(fd);

        return 0;
}

编译运行:

root@luxiaodai:/home/luxiaodai/lxd# gcc -o open open.c 
root@luxiaodai:/home/luxiaodai/lxd# ./open 
open 3.
root@luxiaodai:/home/luxiaodai/lxd# ll Love 
-rw-r--r-- 1 root root 0 8月  29 10:43 Love

1.4 read和write函数

read函数原型:

SYNOPSIS
       #include <unistd.h>

       ssize_t read(int fd, void *buf, size_t count);

       //功能:从文件描述符fd所指定的文件读取count字节到buf缓冲区,返回值为实际读取字节数,若已到文件尾,返回 0,出错返回 -1

接下来来看一个简单的例子,代码如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(void)
{
    int fd;
    char buf[1024];

    fd = open("Love",O_CREAT|O_RDONLY,0666);

    if(fd < 0)
    {
        perror("open");
        exit(1);
    }

    printf("open %d.n",fd);

    memset(buf, 0, 1024);

    int readnum = read(fd, buf, 1024);
    if(readnum < 0)
    {
        perror("read");
        exit(1);
    }
    else if(readnum > 0)
    {
        printf("read from %d is %sn",fd,buf);
    }
    else
        printf("end of file.n");

    close(fd);

    return 0;
}

执行如下操作:

d2b399d76cc247ae5f3efb2c1d7bf03b.png

文件最初被创建时里面是空的,所以提示到文件尾了,当文件里有了内容时,read函数将其读取出来。


write函数原型:

#include <unistd.h>

       ssize_t write(int fd, void *buf, size_t count);
       //把count字节从buf写到fd所指定文件中,若成功返回实际写入的字节数,失败返回 -1

lseek函数原型:

SYNOPSIS
       #include <sys/types.h>
       #include <unistd.h>

       off_t lseek(int fd, off_t offset, int whence);

每个打开文件都有一个与其相关联的“当前文件偏移量”。它通常是一个非负整数,用以度量从文件开始处计算的字节数。lseek将文件读写指针相对whence移动offset个字节。操作成功时,返回文件指针相对于文件头的位置。

  • 若whence是SEEK_SET,则将该文件的偏移量设置为据文件开始处offset个字节
  • 若whence是SEEK_CUR,则将该文件的偏移量设置为其当前值加offset,offset可为正或负
  • 若whence是SEEK_END,则将该文件的偏移量设置为文件长度加offset,offset可正可负

lseek仅将当前的文件偏移量记录在内核中,它并不引起任何IO操作。


下面来看一个例子:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(void)
{
    int fd;
    char buf[1024];

    fd = open("Love",O_CREAT|O_RDWR,0666);    //打开一个文件

    if(fd < 0)    //打开失败
    {
        perror("open");
        exit(1);
    }

    printf("open %d.n",fd);

    /*清空缓存,写入数据到buf*/
    memset(buf, 0, 1024);
    strcpy(buf, "nice weekend");

    int writenum = write(fd, buf, strlen(buf));    //写入实际数据长度

    if(writenum < 0)    //写出错处理
    {
        perror("write");
        exit(1);
    }

    memset(buf, 0, 1024);

//  lseek(fd, SEEK_SET, 0);

    int readnum = read(fd, buf, 1024);
    if(readnum < 0)    //出错
    {
        perror("read");
        exit(1);
    }
    else if(readnum > 0)    //正确读取
    {
        printf("read from %d is %sn",fd,buf);
    }
    else
        printf("end of file.n");    //文件尾

    close(fd);

    return 0;
}

编译运行一下:

3bd645bf8ffdf14e959854da37405340.png

我们发现第一次执行时没有读取到文件里的数据而是读到了文件尾,这是因为写的时候指针自动移动到了文件尾,当读取时,指针并没有移动,我们加入lseek主动将指针移到文件头,即将上面注释的代码加入到程序中,我们发现这个时候再编译运行就能正确读取数据了。

1.5 文件共享

内核使用 3 种数据结构表示打开文件,它们之间的关系决定了文件共享方面一个进程对另一个进程可能产生的影响。

1 )每个进程在进程表中都有一个记录项,每个记录项中有一张打开文件描述符表,可将其视为一个矢量,每个描述符占用一项。与每个文件描述符相关联的是:
a. 文件描述符标志。
b. 指向一个文件表项的指针。

2 )内核为所有打开文件维持一张文件表。每个文件表项包含:
a. 文件状态标志(读、写、增写、同步等)。
b. 当前文件位移量。
c. 指向该文件v节点表项的指针。

3 )每个打开文件(或设备)都有一个v节点(v-node)结构。
v节点包含了文件类型和对此文件进行各种操作的函数的指针信息。对于大多数文件, v节点还包含了该文件的i节点(i-node,索引节点)。例如, i节点包含了文件的所有者、文件长度、文件所在的设备、指向文件在盘上所使用的实际数据块的指针等等

Linux没有使用 v 节点,而是使用了通用i节点结构。虽然两种实现有所不同,但在概念上,v 节点和 i 节点是一样的。两者都指向文件系统特有的 i 节点结构

下图显示了一个进程对应的三张表之间的关系&#

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值