文件I/O_02文件描述符fd

一、fd是什么

    1.为什么读写文件要先open()?

看下面代码,通常我们读写文件,都是要先open,然后读写,处理完业务之后还有close一下。

fd = open(pathname, flags, mode);
rlen = read(fd, buf, count);
wlen = write(fd, buf, count);
close(fd);

为什么读写文件都要先open()一下,读写完又要close()呢?

以前老师只告诉我们要open和一定一定要close,有时候我们忘记close代码也不报错,一样正常运行。

为什么?希望我们带着好奇心,往下寻找答案

2.要理解文件描述符fd,要从启动进程开始

先看图,网友画的特别好,借用一下

  (1)启动进程,系统会创建进程控制块来存储进程信息,task_struct,产生一个pid,叫进程描述符。

/*
 进程创建时,创建的结构体,进程创建的时候返回进程描写符:pid
 */
struct task_struct {

   //打开文件信息
   struct files_struct *files;

   volatile long state;  //说明了该进程是否可以执行,还是可中断等信息
   unsigned long flags;  //Flage 是进程号,在调用fork()时给出
   int sigpending;    //进程上是否有待处理的信号
   mm_segment_t addr_limit; //进程地址空间,区分内核进程与普通进程在内存存放的位置不同
   //0-0xBFFFFFFF for user-thead
   //0-0xFFFFFFFF for kernel-thread
   //调度标志,表示该进程是否需要重新调度,若非0,则当从内核态返回到用户态,会发生调度
   volatile long need_resched;
   int lock_depth;  //锁深度
   long nice;       //进程的基本时间片
   //进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHER
   unsigned long policy;
   struct mm_struct *mm; //进程内存管理信息
   int processor;
   //若进程不在任何CPU上运行, cpus_runnable 的值是0,否则是1 这个值在运行队列被锁时更新
   unsigned long cpus_runnable, cpus_allowed;
   struct list_head run_list; //指向运行队列的指针
   unsigned long sleep_time;  //进程的睡眠时间
   //用于将系统中所有的进程连成一个双向循环链表, 其根是init_task
   struct task_struct *next_task, *prev_task;
   struct mm_struct *active_mm;
   struct list_head local_pages;       //指向本地页面
   unsigned int allocation_order, nr_local_pages;
   struct linux_binfmt *binfmt;  //进程所运行的可执行文件的格式
   int exit_code, exit_signal;
   int pdeath_signal;     //父进程终止时向子进程发送的信号
   unsigned long personality;
   //Linux可以运行由其他UNIX操作系统生成的符合iBCS2标准的程序
   int did_exec:1;
   pid_t pid;    //进程标识符,用来代表一个进程
   pid_t pgrp;   //进程组标识,表示进程所属的进程组
   pid_t tty_old_pgrp;  //进程控制终端所在的组标识
   pid_t session;  //进程的会话标识
   pid_t tgid;
   int leader;     //表示进程是否为会话主管
   struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;
   struct list_head thread_group;   //线程链表
   struct task_struct *pidhash_next; //用于将进程链入HASH表
   struct task_struct **pidhash_pprev;
   wait_queue_head_t wait_chldexit;  //供wait4()使用
   struct completion *vfork_done;  //供vfork() 使用
   unsigned long rt_priority; //实时优先级,用它计算实时进程调度时的weight值

   //it_real_value,it_real_incr用于REAL定时器,单位为jiffies, 系统根据it_real_value
   //设置定时器的第一个终止时间. 在定时器到期时,向进程发送SIGALRM信号,同时根据
   //it_real_incr重置终止时间,it_prof_value,it_prof_incr用于Profile定时器,单位为jiffies。
   //当进程运行时,不管在何种状态下,每个tick都使it_prof_value值减一,当减到0时,向进程发送
   //信号SIGPROF,并根据it_prof_incr重置时间.
   //it_virt_value,it_virt_value用于Virtual定时器,单位为jiffies。当进程运行时,不管在何种
   //状态下,每个tick都使it_virt_value值减一当减到0时,向进程发送信号SIGVTALRM,根据
   //it_virt_incr重置初值。
   unsigned long it_real_value, it_prof_value, it_virt_value;
   unsigned long it_real_incr, it_prof_incr, it_virt_value;
   struct timer_list real_timer;   //指向实时定时器的指针
   struct tms times;      //记录进程消耗的时间
   unsigned long start_time;  //进程创建的时间
   //记录进程在每个CPU上所消耗的用户态时间和核心态时间
   long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
   //内存缺页和交换信息:
   //min_flt, maj_flt累计进程的次缺页数(Copy on Write页和匿名页)和主缺页数(从映射文件或交换
   //设备读入的页面数); nswap记录进程累计换出的页面数,即写到交换设备上的页面数。
   //cmin_flt, cmaj_flt, cnswap记录本进程为祖先的所有子孙进程的累计次缺页数,主缺页数和换出页面数。
   //在父进程回收终止的子进程时,父进程会将子进程的这些信息累计到自己结构的这些域中
   unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
   int swappable:1; //表示进程的虚拟地址空间是否允许换出
   //进程认证信息
   //uid,gid为运行该进程的用户的用户标识符和组标识符,通常是进程创建者的uid,gid
   //euid,egid为有效uid,gid
   //fsuid,fsgid为文件系统uid,gid,这两个ID号通常与有效uid,gid相等,在检查对于文件
   //系统的访问权限时使用他们。
   //suid,sgid为备份uid,gid
   uid_t uid,euid,suid,fsuid;
   gid_t gid,egid,sgid,fsgid;
   int ngroups; //记录进程在多少个用户组中
   gid_t groups[NGROUPS]; //记录进程所在的组
   //进程的权能,分别是有效位集合,继承位集合,允许位集合
   kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
   int keep_capabilities:1;
   struct user_struct *user;
   struct rlimit rlim[RLIM_NLIMITS];  //与进程相关的资源限制信息
   unsigned short used_math;   //是否使用FPU
   char comm[16];   //进程正在运行的可执行文件名
   //文件系统信息
   int link_count, total_link_count;
   //NULL if no tty 进程所在的控制终端,如果不需要控制终端,则该指针为空
   struct tty_struct *tty;
   unsigned int locks;
   //进程间通信信息
   struct sem_undo *semundo;  //进程在信号灯上的所有undo操作
   struct sem_queue *semsleeping; //当进程因为信号灯操作而挂起时,他在该队列中记录等待的操作
   //进程的CPU状态,切换时,要保存到停止进程的task_struct中
   struct thread_struct thread;
   //文件系统信息
   struct fs_struct *fs;

   //信号处理函数
   spinlock_t sigmask_lock;
   struct signal_struct *sig; //信号处理函数
   sigset_t blocked;  //进程当前要阻塞的信号,每个信号对应一位
   struct sigpending pending;  //进程上是否有待处理的信号
   unsigned long sas_ss_sp;
   size_t sas_ss_size;
   int (*notifier)(void *priv);
   void *notifier_data;
   sigset_t *notifier_mask;
   u32 parent_exec_id;
   u32 self_exec_id;

   spinlock_t alloc_lock;
   void *journal_info;
}
/**
文件描述符表,
内核打开文件会从fd_array找到一个空闲的fd:文件描述符,作为open()返回值,
如果超出长度:open too many files
查看最大值:ulimit -n ,取决内存来设置
 */
struct files_struct {

   /* 用于查找下一个空闲的fd */
   int next_fd;
   /* fd_array为一个固定大小的file结构数组。struct file是内核用于文
          件管理的结构。这里使用默认大小的数组, 就是为了可以涵盖
          态分配 */
   struct file __rcu * fd_array[NR_OPEN_DEFAULT];

   /* count为文件表files_struct的引用计数 */
   atomic_t count;
   /* 文件描述符表 */
   /*
      为什么有两个fdtable呢?这是内核的一种优化策略。fdt为指针, 而fdtab为普通变量。一般情况下,
      fdt是指向fdtab的, 当需要它的时候, 才会真正动态申请内存。因为默认大小的文件表足以应付大多数
      情况, 因此这样就可以避免频繁的内存申请。
      这也是内核的常用技巧之一。在创建时, 使用普通的变量或者数组, 然后让指针指向它, 作为默认情况使
      用。只有当进程使用量超过默认值时, 才会动态申请内存。
      *//*
      struct fdtable __rcu *fdt;
      struct fdtable fdtab;
      * written part on a separate cache line in SMP
   */
   /* 使用____cacheline_aligned_in_smp可以保证file_lock是以cache
      line 对齐的, 避免了false sharing */
   spinlock_t file_lock ____cacheline_aligned_in_smp;

   /* 保存执行exec需要关闭的文件描述符的位图 */
   struct embedded_fd_set close_on_exec_init;
   /* 保存打开的文件描述符的位图 */
   struct embedded_fd_set open_fds_init;
   /* fd_array为一个固定大小的file结构数组。struct file是内核用于文
      件管理的结构。这里使用默认大小的数组, 就是为了可以涵盖
}

 

(2)task_struct有个成员files_struct指针指向文件描述符表结构体,files_struct里面一个定长file结构数组:fd_array,映射fd和file的关系。

(3)内核open文件时,会在这个进程里面的fd_array(文件描述符列表)中找到一个空闲的fd,然后空闲的fd和文件inode号建立映射关系(文件部分属性读入内存),并返回fd_array数组下标,这个下标值就是文件描述符,简称fd。

所以fd本质就是一个数组下标。也就是网上说的非负整数。

3.查看进程所以打开的fd

(1)#lsof  -p  pid     

$$表示当前窗口进程的pid

lsof输出各列信息:

COMMAND:进程的名称 PID:进程标识符

USER:进程所有者

FD:文件描述符,应用程序通过文件描述符识别该文件。如cwd、txt等 .任何文件都有012

0u标准输入

1u标准输出

2u错误输出

TYPE:文件类型,如DIR(目录)、REG(基本文件)等

DEVICE:指定磁盘的名称

SIZE/OFF:文件的大小

NODE:索引节点(文件在磁盘上的标识)

NAME:打开文件的确切名称

        (2)exec 8<  io.txt 

            创建一个io.txt,里面随便打个1234,

           将操作符8指向标准输入,读输入流,将io.txt 中的内容作为exec的标准输入.

           可以看到 FD:8r (8数组下标,r只读模式) , OFFSET:0t4:(文件指针读取偏移量),node:786508(文件的inode号,系统唯一可以找到该文件)

  (3)查看系统限制最大fd数      

#cat /proc/sys/fs/file-nr #输出:9216     0    6553600 (已分配fd,已分配但未使用,最大fd数)

  (4)单进程限制打开最大fd数      

#ulimit -n    #输出:1048576  (刚安装系统默认1024,大小设置取决内存。太小容易:too many open files)

4.回到第1个问题为什么要open,close

(1)open?

操作系统需要检查这个文件是否真正存在,要检查当前进程还有没有空闲的fd,没空闲的直接:too many open files了。存在fd需要和它做映射。操作系统还有检查你是否有权限,有哪些可操作权限等等

(2)close?

fd_array:大小是有限制的,系统的资源有限。假如完成业务程序退出了,fd一直不关,那这个fd一值占着不放。一开始fd_array空闲数量充足程序还没问题,open多了的时候,很快就会提示:too many open files了

可以用#cat /proc/sys/fs/file-nr #输出:9216     0    6553600 (已分配fd,已分配但未使用,最大fd数)查看是否已分配完了。

可以用#ls -l /proc/进程PID/fd/  ,进入具体的进程看看都打开了哪些进程,会有详细信息记录。

5.fd相关面试题,判断一下对错,可以思考一下再看答案

(1)两个进程可以同时读取一个文件,分别产生生成两个独立的fd?

(2)两个进程可以任意对文件进行读写操作,操作系统并不保证写的原子性?

(3)进程可以通过系统调用对文件加锁,从而实现对文件内容的保护?

(4)任何一个进程删除该文件时,另外一个进程会立即出现读写失败?

(5)两个进程可以分别读取文件的不同部分而不会相互影响?

(6)一个进程对文件长度和内容的修改另外一个进程可以立即感知?

(7)一个进程不能有两个8,两个进程可以分别有8,同一进程内的FD相互独立,操作系统内,进程之间是严重隔离的?

1)两个进程可以同时读取一个文件,分别产生生成两个独立的fd?(对,两个fd_array,各管各的)
2)两个进程可以任意对文件进行读写操作,操作系统并不保证写的原子性?(对,因为写的原子性应该是由锁来控制)
3)进程可以通过系统调用对文件加锁,从而实现对文件内容的保护?(对,锁互斥)
4)任何一个进程删除该文件时,另外一个进程会立即出现读写失败?(错。并不会立即出现读写失败,一般删除都是文件索引,一个线程执行删除操作,只要另一个线程不退出,就可以继续对该文件进行操作,文件变更后fd后面会标记(deleted)一旦退出才找不到该文件的索引节点而报错)
5)两个进程可以分别读取文件的不同部分而不会相互影响(对,fd指针偏移量是独立的)
6)一个进程对文件长度和内容的修改另外一个进程可以立即感知(对,文件删除或修改后fd后面会标记(deleted))
7)一个进程不能有两个8,两个进程可以分别有8,同一进程内的FD相互独立,操作系统内,进程之间是严重隔离的(对)

二、inode是什么

前面有说的,fd会和inode号做映射,那inode是什么?inode号又是什么?

1、要理解inode,要从文件储存说起

我们都知道,文件持久化存储最后都是要落在磁盘上的。我们看看一个图(本图是网上摘得,画得真好)

(1)磁盘存储单位:最小单位扇区

cylinder:柱面

magnetic layer:磁层(磁盘多个盘面)

track:磁道(分若干弧段)

sector:扇区(弧段,512字节=0.5kb)//最小单位

(2)文件存储单位:块

block:块(连续8扇=4kb) (一个字节=8位) ,如果读写文件一个个扇区来,效率太低了,所以文件会8扇一起来刷。

(3)读写起始位置

disk read-and-write heads:磁头,磁头外缘从0开始,写满外圈再往内圈写

(4)查找位置:先柱面-在盘面-扇区

先找到在哪圈,哪层,然后磁盘转到具体扇区,开始转圈读写

既然文件数据是存储在磁盘中,那谁来记录这些数据在哪个圈层?占了多少块?是谁创建的?有什么权限?那是不是还要有个东西来存储。答案就是inode结构体

2、inode结构体

struct inode {
   umode_t         i_mode;//文件的访问权限(eg:rwxrwxrwx)
   unsigned short      i_opflags;
   kuid_t          i_uid;//inode拥有者id
   kgid_t          i_gid;//inode拥有者组id
   unsigned int        i_flags;//inode标志,可以是S_SYNC,S_NOATIME,S_DIRSYNC等

   #ifdef CONFIG_FS_POSIX_ACL
   struct posix_acl    *i_acl;
   struct posix_acl    *i_default_acl;
   #endif

   const struct inode_operations   *i_op;//inode操作
   struct super_block  *i_sb;//所属的超级快
   /*
      address_space并不代表某个地址空间,而是用于描述页高速缓存中的页面的一个文件对应一个address_space,一个address_space与一个偏移量能够确定一个一个也高速缓存中的页面。i_mapping通常指向i_data,不过两者是有区别的,i_mapping表示应该向谁请求页面,i_data表示被改inode读写的页面。
   */
   struct address_space    *i_mapping;

   #ifdef CONFIG_SECURITY
   void            *i_security;
   #endif

   /* Stat data, not accessed from path walking */
   unsigned long       i_ino;//inode号
   /*
    * Filesystems may only read i_nlink directly.  They shall use the
    * following functions for modification:
    *
    *    (set|clear|inc|drop)_nlink
    *    inode_(inc|dec)_link_count
    */
   union {
   const unsigned int i_nlink;//硬链接个数
   unsigned int __i_nlink;
   };
   dev_t           i_rdev;//如果inode代表设备,i_rdev表示该设备的设备号
   loff_t          i_size;//文件大小
   struct timespec     i_atime;//最近一次访问文件的时间
   struct timespec     i_mtime;//最近一次修改文件的时间
   struct timespec     i_ctime;//最近一次修改inode的时间
   spinlock_t      i_lock; /* i_blocks, i_bytes, maybe i_size */
   unsigned short          i_bytes;//文件中位于最后一个块的字节数
   unsigned int        i_blkbits;//以bit为单位的块的大小
   blkcnt_t        i_blocks;//文件使用块的数目

   #ifdef __NEED_I_SIZE_ORDERED
   seqcount_t      i_size_seqcount;//对i_size进行串行计数
   #endif

   /* Misc */
   unsigned long       i_state;//inode状态,可以是I_NEW,I_LOCK,I_FREEING等
   struct mutex        i_mutex;//保护inode的互斥锁

   //inode第一次为脏的时间 以jiffies为单位
   unsigned long       dirtied_when;   /* jiffies of first dirtying */

   struct hlist_node   i_hash;//散列表
   struct list_head    i_wb_list;  /* backing dev IO list */
   struct list_head    i_lru;      /* inode LRU list */
   struct list_head    i_sb_list;//超级块链表
   union {
   struct hlist_head   i_dentry;//所有引用该inode的目录项形成的链表
   struct rcu_head     i_rcu;
   };
   u64         i_version;//版本号 inode每次修改后递增
   atomic_t        i_count;//引用计数
   atomic_t        i_dio_count;
   atomic_t        i_writecount;//记录有多少个进程以可写的方式打开此文件
   const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */
   struct file_lock    *i_flock;//文件锁链表
   struct address_space    i_data;
   #ifdef CONFIG_QUOTA
   struct dquot        *i_dquot[MAXQUOTAS];//inode磁盘限额
   #endif
   /*
      公用同一个驱动的设备形成链表,比如字符设备,在open时,会根据i_rdev字段查找相应的驱动程序,并使i_cdev字段指向找到的cdev,然后inode添加到struct cdev中的list字段形成的链表中
   */
   struct list_head    i_devices;,
   union {
   struct pipe_inode_info  *i_pipe;//如果文件是一个管道则使用i_pipe
   struct block_device *i_bdev;//如果文件是一个块设备则使用i_bdev
   struct cdev     *i_cdev;//如果文件是一个字符设备这使用i_cdev
   };

   __u32           i_generation;

   #ifdef CONFIG_FSNOTIFY
   //目录通知事件掩码
   __u32           i_fsnotify_mask; /* all events this inode cares about */
   struct hlist_head   i_fsnotify_marks;
   #endif

   #ifdef CONFIG_IMA
   atomic_t        i_readcount; /* struct files open RO */
   #endif
   //存储文件系统或者设备的私有信息
   void            *i_private; /* fs or device private pointer */
}

3、一个文件一个inode结构体,里面对应一个系统唯一inode号

struct inode的结构体就是存储文件属性的结构体。注意inode只是存储文件的属性,文件属性和文件存储的内容是分开存储的。

硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。

inode结构体很多数据,有兴趣可以详细研读,我们现在只关心(unsigned long i_ino;//inode号):一个文件一个inode结构体,里面对应一个系统唯一inode号。

4、查看文件inode信息

#stat rongshu.txt

file:文件名

size:大小

blocks:文件使用的数据块总数 8

io blocks:IO块大小4096

device:fd01h/64769d:磁盘位置

inode:786486:索引节点(这里看到每个文件都有一个inode号)

link:链接数,即有多少文件名指向这个inode

5、fd的node和inode struct里面的inode

细心的你可能发现,fd的node和inode struct里面的inode都是一个数字,名字也是类似,那是不是同一个东西?答案:是的,是同一个东西,可以自己验证

#exec 8<  io.txt

#lsof  -p  $$

#stat io.txt

6.inode大小

(1)#df -i

(2)一般是128字节或256字节

7.inode是VFS使用的一个对象

(1)刚有说到,inode只是文件的属性,和内容是分开的,实际上inode是VFS使用的一个对象,是linux虚拟文件系统,挂载磁盘真实文件而创建的一个东西。

索引节点有两种:

一种是这里所说的VFS索引节点,存在内存中

一种是具体文件系统的索引节点,存在于磁盘上(可以理解为文件存储块的起始位置)

(2)打开一个文件时,操作系统就会把对应的文件的inode的部分属性摘录下来放在内存里,存在fd_array中

fd_array,可以理解是个二维数组,key=fd,value=node,node是inode号,这时返回个fd给用户,就可以操作文件了

(3)操作文件的时候,就可以快速通过fd找到node(inode号),通过inode号可以找到文件具体位置

三、了解fd有啥用

最后了解文件描述符有啥用呢?我理解

1.我们要做系统性能优化,I/O就是很重要的一块,I/O又分文件I/O,网络I/O。要想理解操作系统I/O工作原理,文件描述符是第一个要掌握的。这里是为后面学习I/O知识打基础。

2.理解这个更好理解操作系统抽象统一的思想,vfs就是个很好的例子,万物可以挂载。内核通过fd就可以高效管理系统资源

3.扩展:操作文件调用open可以拿到一个fd,启动服务调用socket函数可以拿到一个fd,进程间通信调用pipe可以拿到2个fd。

关于I/O学习是很重要的,文件描述符就写到这。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端 在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序。 比如我们使用ftp程序从另外一 个地方获取文件的时候,是我们的ftp程序主动同外面进行通信(获取文件), 所以这个地方我们的ftp程序就是客户端程序。 服务端 和客户端相对应的程序即为服务端程序。被动的等待外面的程序来和自己通讯的程序称为服务端程序。 比如上面的文件获取中,另外一个地方的程序就是服务端,我们从服务端获取文件过来。 [代码] [C/C++]代码 /******* 服务器程序 (server.c) ************/ #include #include #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { int sockfd,new_fd; struct sockaddr_in server_addr; struct sockaddr_in client_addr; int sin_size,portnumber; char hello[]="Hello! Are You Fine?\n"; if(argc!=2) { fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]); exit(1); } if((portnumber=atoi(argv[1]))<0) { fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]); exit(1); } /* 服务器端开始建立socket描述符 */ if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) { fprintf(stderr,"Socket error:%s\n\a",strerror(errno)); exit(1); } /* 服务器端填充 sockaddr结构 */ bzero(&server;_addr,sizeof(struct sockaddr_in)); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=htonl(INADDR_ANY); server_addr.sin_port=htons(portnumber); /* 捆绑sockfd描述符 */ if(bind(sockfd,(struct sockaddr *)(&server;_addr),sizeof(struct sockaddr))==-1) { fprintf(stderr,"Bind error:%s\n\a",strerror(errno)); exit(1); } /* 监听sockfd描述符 */ if(listen(sockfd,5)==-1) { fprintf(stderr,"Listen error:%s\n\a",strerror(errno)); exit(1); } while(1) { /* 服务器阻塞,直到客户程序建立连接 */ sin_size=sizeof(
POSIX API(Portable Operating System Interface)是一组操作系统API标准,用于确保可移植性。这些API包括文件 I/O 操作方法,可以在不同的操作系统上使用相同的语法进行文件操作。下面是一些文件 I/O 操作方法: 1. 打开文件: ``` int open(const char *path, int flags); // 返回文件描述符 ``` `open()` 函数用于打开文件,并返回文件描述符(file descriptor),这个文件描述符被用来在后续的文件操作中标识文件。参数 `path` 是文件路径,参数 `flags` 是打开文件的模式,比如 `O_RDONLY` 表示只读,`O_WRONLY` 表示只写,`O_RDWR` 表示读写等。 2. 写文件: ``` ssize_t write(int fd, const void *buf, size_t count); // 返回成功写入的字节数 ``` `write()` 函数用于将数据写入文件。参数 `fd` 是文件描述符,参数 `buf` 是要写入的数据缓冲区,参数 `count` 是要写入的字节数。函数返回成功写入的字节数。 3. 读文件: ``` ssize_t read(int fd, void *buf, size_t count); // 返回成功读取的字节数 ``` `read()` 函数用于从文件中读取数据。参数 `fd` 是文件描述符,参数 `buf` 是存储读取数据的缓冲区,参数 `count` 是要读取的字节数。函数返回成功读取的字节数。 4. 关闭文件: ``` int close(int fd); // 返回0表示成功 ``` `close()` 函数用于关闭文件。参数 `fd` 是文件描述符。函数返回0表示成功关闭文件。 以上是 POSIX API 中文件 I/O 操作的几个基本方法,还有一些其他的方法,如 `lseek()` 用于改变文件读写位置, `fstat()` 用于获取文件状态等。在实际的文件操作中,需要根据实际需求选择合适的方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值