linux中文件操作命令,linux文件信息详解

  linux中文件操作命令,linux文件信息详解

  Mkdir和rmdir

  我们可以使用mkdir和rmdir来创建和删除目录。

  其语法如下:

  #包含系统/统计信息

  int mkdir(const char *path,mode _ t mode);

  mkdir系统调用可用于创建目录,这与mkdir程序相同。mkdir创建一个名为path的新目录。目录的权限由参数mode指定,与开放系统调用中O_CREAT的选项相同,也受umask的影响。

  Rmdir语法如下:

  #包括unistd.h

  int rmdir(const char * path);

  rmdir系统调用将删除目录,但是只有当目录为空时,操作才会成功。rmdir程序使用系统调用来完成它的工作。

  Chdir和getcwd

  在程序目录中浏览的方式类似于用户在文件系统中浏览的方式。类似于在Shell中使用cd命令切换目录,程序也可以使用chdir系统调用。

  #包括unistd.h

  int chdir(const char * path);

  程序可以通过调用getcwd函数来确定当前的工作目录。

  #包括unistd.h

  char *getcwd(char *buf,size _ t size);

  函数的作用是:将当前目录的名称写入指定的缓冲区buf。如果目录名超过了作为参数传递的缓冲区大小(Elange错误),它将返回null。如果成功,将返回buf。

  浏览目录

  Linux系统上一个常见的问题就是目录浏览,即确定文件所在的具体目录。在Shell程序中,这很简单,只需要Shell来扩展通配符表达式。过去,不同的Unix变体允许程序员访问底层文件系统结构。我们仍然可以像打开普通文件一样打开一个目录,然后直接读取目录实体,但是不同的文件系统结构和实现使得这种方法不具有可移植性。现在已经开发了一个标准的库函数套件,使得目录浏览更加容易。

  目录是在头文件dirent.h中声明的,它们使用一个结构DIR作为目录操作的基础。指向这个结构的指针称为目录流(DIR *),其作用类似于通常文件操作的文件流(FILE *)。dirent结构中返回的目录实体也在dirent.h中声明,所以我们千万不要直接修改DIR结构的域。

  我们将了解以下功能:

  打开目录,关闭目录

  读取目录项

  特尔迪尔

  塞克迪尔

  打开目录

  opendir函数将打开一个目录并创建一个目录流。如果成功,他将返回一个指向用于读取目录的实体的DIR结构。

  #包含sys/types.h

  #包含目录. h

  DIR * opendir(const char * name);

  如果失败,将返回一个空指针。请注意,目录流使用低级文件描述符来访问目录本身,因此如果打开太多文件,opendir将会失败。

  读取目录项

  readdir函数返回一个结构,该结构指向目录流dirp中下一个目录实体的细节。成功调用readdir函数将返回下一个目录实体。如果出现错误或到达目录末尾,readdir将返回NULL。POSIX系统在返回NULL时不改变errno的值,只是在发生错误时才改变。

  #包含sys/types.h

  #包含目录. h

  struct dirent * readdir(DIR * dirp);

  请注意,如果其他进程正在同时创建或删除目录中的文件,readdir浏览将不会列出目录中的所有文件。

  ENT结构包含目录实体的详细信息,包括以下实体:

  Ino_t d_ino:文件的I节点

  Char _ name []:文件名

  为了确定目录中文件的更详细信息,我们需要调用stat函数,我们在前面已经讨论过了。

  特尔迪尔

  Telldir函数返回目录流中记录的当前位置的值。然后我们可以调用seekdir将目录浏览设置到当前位置。

  #包含sys/types.h

  #包含目录. h

  long int tell DIR(DIR * dirp);

  塞克迪尔

  Seekdir函数设置由dirp指定的目录流的目录实体指针。用于设置位置的loc值应通过之前的telldir函数获得。

  #包含sys/types.h

  #包含目录. h

  void seekdir(DIR *dirp,long int loc);

  关闭目录

  closedir函数关闭一个目录流并释放它的相关资源。如果成功,将返回0;否则,它将返回一个错误。

  #包含sys/types.h

  #包含目录. h

  int closedir(DIR * dirp);

  在接下来的程序printdir.c中,我们集中了各种文件操作函数,创建了一个简单的目录列表。目录中的每个文件都在其所在的行中列出。每个子目录的名称后都有一个破折号,其中的文件显示时有四个空格。

  程序可以切换到子目录,这样它寻找的文件就可以有可用的名字,也就是可以直接传递给opendir。这个程序将在太深的嵌套目录中失败,因为打开目录流的数量有限制。

  当然,我们可以通过命令行参数来指定起始点,从而使程序更加通用。我们可以看看ls或find的Linux源代码,看看更通用的目录方法。

  目录浏览程序

  1我们的相关头文件,和printdir函数来打印当前目录。对于子目录,深度参数可用于重用。

  #包括unistd.h

  #包含stdio.h

  #包含目录. h

  #包含字符串. h

  #包含系统/统计信息

  #包含stdlib.h

  void printdir(char *dir,int depth)

  {

  DIR * dp

  结构目录*条目;

  struct stat statbuf

  if((dp=opendir(dir))==NULL) {

  fprintf(stderr,"无法打开目录:%s/n ",dir);

  返回;

  }

  chdir(dir);

  while((entry=readdir(dp))!=NULL) {

  lstat(entry- d_name,stat buf);

  if(S_ISDIR(statbuf.st_mode)) {

  /*找到一个目录,但忽略。和.*/

  if(strcmp(" . ",entry- d_name)==0

  strcmp(" . ",entry- d_name)==0)

  继续;

  printf("%*s%s//n ",深度,"",entry-d _ name);

  /*在新的缩进级别递归*/

  printdir(entry- d_name,深度4);

  }

  else printf("%*s%s/n ",深度,"",entry-d _ name);

  }

  chdir(" . ");

  克洛斯迪尔(DP);

  }

  现在让我们开始主函数

  int main()

  {

  printf("目录扫描/home:/n ");

  printdir("/home ",0);

  printf("完成。/n ");

  退出(0);

  }

  程序的处理结果输出如下:

  $ printdir

  /h的目录扫描

  尼尔/。x默认值。Xmodmap。Xresources。bash _历史。没有则创建。kde/

  分享/

  应用程序/

  konqueror/

  dirtree/

  public_html .桌面

  工具栏/

  书签. xml

  konq _历史

  kdisplay/

  配色方案/

  BLP3/

  Gnu _公共_许可证

  第四章/

  argopt.c

  args.c

  第三章/

  file.out

  mmap.c

  打印目录

  完成了。

  操作原理

  大多数大的动作都在printdir函数中,所以这就是我们要看的地方。对于某些初始检测,使用opendir来确定指定的目录是否存在,printdir在指定的目录上调用chdir函数。当readdir返回的实体不为空时,程序会检查是否为目录。如果没有,使用深度缩进来打印文件实体。

  如果这个实体是一个目录,我们需要做一些递归操作。在.之后.和.实体(当前目录和父目录)被忽略,printdir函数将调用自身并再次执行相同的处理操作。那么如何跳出这个循环呢?一旦while循环结束,chdir( . )函数调用将返回到目录树,以便继续列表优先操作。调用closedir(dp)可以确保打开目录流的数量不高于要求的数量。

  为了简单介绍将在第四章讨论的Linux环境,让我们来看看一种使程序更通用的方法。该程序是有限的,因为它被指定为/home/neil目录。通过对main函数的如下修改,我们可以把它变成一个更有用的目录浏览器:

  int main(int argc,char* argv[])

  {

  char *topdir=",";

  如果(argc=2)

  top dir=argv[1];

  printf("对%s/n的目录扫描",top dir);

  printdir(topdir,0);

  printf("完成。/n ");

  退出(0);

  }

  我们改了三行代码,加了五行。现在它是一个带有可选参数目录名的通用程序,默认为当前目录。我们可以使用以下命令运行它:

  $ printdir2 /usr/local 更多

  输出将被分页,以便用户可以在输出中浏览分页。这样,用户将拥有一个方便小巧的通用目录树浏览器。也可以有一些小的修改。我们可以添加空白使用数据,显示限制深度,然后等待。

  错误

  正如我们所看到的,本章中描述的许多系统函数和调用会因为各种原因而失败。发生这种情况时,它们通过设置外部变量errno来指示失败原因。许多不同的库使用这个变量作为报告问题的标准方法。但在程序指出问题后,程序必须立即测试errno变量的值,因为它会被下一次函数调用覆盖,尽管函数本身并没有失败。

  头文件errno.h中列出了错误的含义,包括:

  EPERM:不允许操作

  没有这样的文件或目录。

  EINTR:系统调用中断

  Ei:输入/输出错误

  电子商务:设备或资源忙

  EEXIST:文件存在。

  EINVAL:参数不可用

  EMFILE:打开的文件太多。

  ENODEV:没有这样的设备。

  这是一个目录。

  ENOTDIR:不是目录。

  当错误发生时,有一对非常有用的函数来报告它:strerror和perror。

  错误字符串映射

  Strerror函数将错误号映射到描述错误类型的字符串。这对于记录错误情况很有用。

  其语法如下:

  #包含字符串. h

  char * strerror(int errnum);

  得到错误提示字符串

  Perror函数还将报告的errno映射到一个字符串中,并将其打印到标准错误流中。他以一个指定的字符串s(如果不为空)开始,后跟一个冒号和一个空格。

  其语法如下:

  #包含stdio.h

  void perror(const char * s);

  例如:

  perror(“程序”);

  您将在标准错误输出中获得以下输出:

  程序:打开的文件太多

  /proc文件系统

  本章前面我们提到Linux把大部分内容看成文件,文件系统里有硬件设备文件。/dev文件用于通过低级系统调用以特殊方式访问硬件。

  控制硬件的软件驱动程序通常可以以特定的方式配置,或者可以报告信息。例如,磁盘控制器可以配置为使用特定的DMA模式。网卡可以报告它是否是高速、多路连接。

  与设备驱动程序通信的程序在过去很常见。例如,hdparm可以配置一些磁盘参数,而ifconfig可以报告其网络参数。近年来,更流行的方式是提供一种更方便的方式来访问驱动程序信息,而事实上,这些通信已经包含在Linux内核的各种元素中。

  Linux提供了一个特殊的文件系统procfs,它通常以目录/proc的形式出现。它包含许多特殊文件,允许高级访问驱动器和内核信息。只有拥有正确的访问权限,程序才能读取或写入这些文件以获取信息或设置参数。

  出现在/proc中的文件会因系统而异,但是在Linux发行版中,更多的驱动程序和程序支持的procfs文件系统中包含更多的文件。在这里,我们将看看一些常见的文件,并简要讨论它们的用法。

  浏览用于编写本章的计算机上的/proc目录,您将获得以下实体信息:

  1/1377/1771/951/cpuinfo模块

  10/1401/1777/961/设备安装@

  1007/1414/1778/966/dma mtrr

  1023/1457/2/968/driver/net/

  1053/1460/3/969/execdomains分区

  1056/1463/385/970/fb pci

  1059/1465/388/971/文件系统scsi/

  1061/1476/4/974/fs/self@

  1071/1477/424/975/ide/slabinfo

  1077/1479/4775/976/中断飞溅

  1079/1480/4850/977/iomem统计

  1080/1482/496/978/I端口互换

  1082/1484/5/979/irq/sys/

  isapnp sysvipc/

  1090/1491/6/982/kcore tty/

  1093/1494/625/983/kmsg运行时间

  1095/1495/7/999/ksyms版本

  1096/1496/75/apm加载平均视频/

  1100/1502/8/a声音/锁

  1101/1503/884/buddyinfo lvm/

  1104/1545/905/bus/mdstat

  1118/1546/917/cmdline meminfo

  config.gz杂项

  在许多情况下,这些文件可以被读取并给出状态信息。例如,/proc/cpuinfo将给出处理器的详细信息:

  $ cat /proc/cpuinfo

  处理器:0

  供应商标识:正版英特尔

  cpu系列:6

  型号:6

  型号名称:赛扬(Mendocino)

  步进:0

  中央处理器主频:451.028

  缓存大小:128 KB

  不可以

  不可以

  不知道

  昏迷_bug:没有

  fpu:是的

  fpu _异常:是

  cpuid级别:2

  wp:是的

  标志:fpu VME de PSE TSC MSR PAE MCE cx8 sep mtrr PGE MCA cmov pat

  pse36 mmx fxsr

  博戈米普斯:897.84

  类似地,/proc/meminfo和/proc/version分别给出了关于内存使用和内核版本的信息:

  $ cat /proc/meminfo

  总计:已用:空闲:共享:缓冲区:缓存:

  记忆:527392768 240873472 286519296 0 8331264 134004736

  互换:139788288 0 139788288

  内存总量:515032 kB

  内存空闲:279804 kB

  内存共享:0 kB

  缓冲区:8136 kB

  缓存:130864 kB

  交换缓存:0 kB

  活动:101208 kB

  不活动:106056 kB

  总高度:0 kB

  HighFree: 0 kB

  低总计:515032 kB

  低可用空间:279804 kB

  交换总量:136512 kB

  交换空间:136512 kB

  BigFree: 0 kB

  $ cat /proc/version

  Linux版本2 . 4 . 19-4GB(root @ Pentium . SuSE . de)(gcc版本3.2)第一名11月27日星期三

  世界协调时2002年00时56分40秒

  关于特定内核函数的更多信息可以在/proc的子目录中找到。例如,我们可以从/proc/net/sockstat获得网络windows套接字的使用数据:

  $ cat /proc/net/sockstat

  插座:已用246

  TCP:使用20孤儿0 tw 0分配22内存11

  UDP:使用3

  原始:使用0

  FRAG:使用0内存0

  /proc中的一些实体可以读写。例如,所有正在运行的程序可以同时打开的文件数量是一个Linux内核参数。的当前值可以通过/proc/sys/fs/file-max读取:

  $ cat /proc/sys/fs/file-max

  52428

  这里它的值是52428。如果我们需要增加这个值,我们可以通过编写相同的文件来实现。如果我们正在运行一个特殊的程序套件,那么也许我们需要这样做,比如一个使用很多表的数据库系统,需要同时打开很多文件。

  注意:写入/proc文件需要超级访问权限。当写/proc文件时,我们应该小心。如果我们写了不正确的值,将会导致严重的问题。

  如果我们将系统文件处理的限制增加到60000,我们可以简单地将这个新的限制数写入file-max:

  # echo 60000/proc/sys/fs/file-max

  现在我们可以重新读取这个文件,我们将看到新的值:

  $ cat /proc/sys/fs/file-max

  60000

  /proc下带有数字名称的子目录用于提供对正在运行的程序的信息的访问。我们将在第11章学习更多关于程序如何作为一个过程被执行。

  然而,现在我们只需要注意第一个进程有一个惟一的标识符:1到32000之间的一个数值。ps命令提供了当前正在运行的进程的列表。例如,在写这一章时:

  neil@beast:~/BLP3/chapter03 ps

  TTY时间指令

  1104分/1 00:00:00狂欢

  1503分/2 00:00:01巴什

  1771 pts/4 00:00:00 bash

  4991 pts/2 00:00:01 emacs

  4994 pts/2 00:00:00 ps

  尼尔@野兽:~/BLP 3/第3章

  这里我们可以看到一些运行bash shell的终端会话,一个运行Emacs文件编辑器的编辑会话。我们可以通过查看/proc来了解关于Emacs会话的更多细节。

  在这里,Emacs进程的标识是4991,所以我们需要检查/proc/4991以获得更详细的信息:

  $ ls -l /proc/4991

  总计0

  尼尔用户0 2003-02-09 12:45命令行

  lrwxrwxrwx 1 neil用户0 2003-02-09 12:45 CWD-/home/Neil/BLP 3/chapter 03

  尼尔使用者0 2003-02-09 12:45附近

  lrwxrwxrwx 1 neil用户0 2003-02-09 12:45 exe-/usr/bin/emacs

  dr-x - 2 neil用户0 2003-02-09 12:45 fd

  -rw - 1 neil用户0 2003-02-09 12:45 mapped_base

  尼尔用户2003-02-09 12:45地图

  尼尔用户0 2003-02-09 12:45 mem

  尼尔用户0 2003-02-09 12:45安装

  lrwxrwxrwx 1 neil用户0 2003-02-09 12:45 root - /

  尼尔用户0统计

  尼尔用户0 2003-02-09 12:45

  尼尔用户状态

  在这里,我们可以看到各种特殊的文件来告诉我们在这个过程中发生了什么。

  我们可以看到程序/usr/bin/emacs正在运行,它当前的工作目录是/home/neil/BLP3/chpter03。您还可以读取该目录中的其他文件,以查看启动它所需的命令以及它所拥有的shell环境。Cmdline和environ文件以一系列非终结符字符串的形式提供这些信息,因此我们在查找它们时需要小心。我们将在第4章更深入地讨论Linux环境。

  $ od -c /proc/4991/cmdline

  0000000美元。t x t

  0000020 /0

  0000021

  在这里,我们可以看到Emacs是通过命令行emacsdraft2.txt启动的。

  fd子目录提供了进程正在使用的文件描述符的信息。这些信息非常有用,可以用来确定一个程序一次打开了多少个文件。每个打开的文件描述符都有一个实体;其名称与文件描述号相匹配。在我们的例子中,我们可以看到Emacs打开了描述符0,1,2,这正是我们所期望的。这是标准的输入、输出和错误描述符。虽然此时他正在编辑一个文件,但是这个进程并没有使他保持打开状态,所以这里不会显示它。

  $ ls /proc/4991/fd

  0 1 2

  高级主题:fcntl和mmap

  在这里,我们将讨论几个我们可能会跳过的主题,因为它们很少被使用。正如我们所说,我们在这里引用它,因为它们可以为一些技术问题提供一些简单的解决方案。

  记录锁

  fcntl系统调用提供了更多处理底层文件描述符的方法。

  #包含fcntl.h

  int fcntl(int fildes,int cmd);

  int fcntl(int fildes,int cmd,long arg);

  我们可以使用fcntl系统调用对打开的文件描述符执行各种操作,包括复制、获取和设置文件描述符标记、获取和设置文件状态标记以及管理文件锁。

  各种操作由fcntl.h中定义的命令参数cmd的不同值选择.根据选择的命令,系统调用需要第三个参数arg:

  NTL (Fildes,F _ DUPFD,newfd):这个调用将返回一个新的文件描述符,其值等于或大于NEWFD的值。新的文件描述符是描述符fildes的副本。根据打开文件的数量和newfd的值,这和dup(fildes)一样高效。

  FCL (fildes,f _ getfd):这个调用将返回fcntl.h中定义的文件描述符标记,其中包括FD_CLOEXEC,它将确定在成功调用系统调用的EXEC系列之一后是否关闭。

  FCL (Fildes,f _ setFD,flags):这个调用用于调用文件描述符标签,通常只是FD_CLOEXEC。

  FCL (Fildes,f _ getFL)和fcntl(fildes,F_SETFL,flags):这些函数分别用于获取和设置文件状态标志和访问模式。我们可以通过使用fcntl.h中定义的O_ACCMODE掩码来获取访问模式,其他标记包括使用O_CREAT传递给open的第三个参数。注意,我们不能设置所有的标志。一般来说,我们不能使用fcntl来设置文件权限。

  我们可以用fcntl来实现咨询文件锁。我们可以通过查看手册页的第2部分或第7章来获得更多信息,在这一章中我们将讨论文件锁。

  内存映射

  Unix提供了一个非常有用的程序,允许程序共享内容,好消息是它已经包含在Linux内核2.0和更高版本中。Mmap函数可以设置两个或多个程序可以读写的一段内存。一个程序所做的更改可以被另一个程序看到。

  我们可以使用相同的程序来操作文件。我们可以让磁盘文件的物理内容看起来像内存中的数组。如果文件由可以用C结构描述的记录组成,我们可以使用结构数组访问来更新文件。

  这是通过使用具有特殊访问集的虚拟内存段来实现的。读写段会导致操作系统读写磁盘文件的相应部分。

  mmap函数创建一个指向与文件内容相关的内存区域的指针,可以通过一个打开的文件描述符来访问它。

  #include sys/mman.h

  void *mmap(void *addr,size_t len,int prot,int flags,int fildes,off _ t off);

  我们可以通过传递off参数来修改共享段访问的文件数据的开头。可以访问的数据量(例如,内存段的长度)由len参数设置。

  我们可以使用addr参数来请求一个特定的内存地址。如果为空,则自动分配结果指针。这是推荐的用法,否则很难移植;系统的可变地址空间发生变化。

  prot参数用于设置内存段的访问权限。这是下列常数的按位“或”运算:

  PROT里德:这一段是可读的

  PROT _写:段是可写的

  PROT_EXEC:可执行段

  PROT _无:分段不可访问

  flags参数控制如何反映程序对段所做的更改:

  MAP_PRIVATE段是私有的,更改是局部的。

  MAP_SHARED段的变化反映在文件中。

  MAP_FIXED段必须位于指定的地址addr。

  msync函数使内存段中的一些或所有更改写回(或读取)映射文件:

  #include sys/mman.h

  int msync(void *addr,size_t len,int flags);

  要更新的段由传送的起始地址addr和长度len确定。flags参数控制如何执行这些更新。

  MS_ASYNC执行异步写入。

  MS_SYNC执行同步写入。

  MS_INVALIDATE从文件中读取数据。

  munmap函数释放内存段。

  #include sys/mman.h

  int munmap(void *addr,size _ t len);

  下面的程序mmap_eg.c显示了一个使用mmap和数组格式访问更新的结构文件。2.0之前的Linux并不完全支持mmap的使用。该程序可以在Sun Solaris和其他系统中正常运行。

  1我们从定义ERCORD结构和创建NRECOREDS版本开始,每个版本记录它们的编号。这些文件被添加到records.dat文件中

  #包括unistd.h

  #包含stdio.h

  #include sys/mman.h

  #包含fcntl.h

  #包含stdlib.h

  typedef结构{

  int整数;

  char string[24];

  }记录;

  #定义n记录(100)

  int main()

  {

  记录记录,*映射;

  int i,f;

  FILE * fp

  fp=fopen("records.dat "," w ");

  for(I=0;I记录;i ) {

  record . integer=I;

  sprintf(record.string," RECORD-%d ",I);

  fwrite( record,sizeof(record),1,FP);

  }

  fclose(FP);

  2我们现在将记录43的整数值更改为143,并写入第43个记录字符串:

  fp=fopen("records.dat "," r ");

  fseek(fp,43*sizeof(record),SEEK _ SET);

  fread( record,sizeof(record),1,FP);

  record.integer=143

  sprintf(record.string," RECORD-%d ",RECORD . integer);

  fseek(fp,43*sizeof(record),SEEK _ SET);

  fwrite( record,sizeof(record),1,FP);

  fclose(FP);

  3为了将整数值更改为243(并更新记录字符串),我们将这些记录映射到内存,并使用以下内存映射来访问第43条记录:

  f=open("records.dat ",O _ RDWR);

  mapped=(RECORD *)mmap(0,NRECORDS*sizeof(record),

  PROT _读 PROT _写,地图_共享,f,0);

  已映射[43]。整数=243;

  sprintf(映射[43])。字符串,“记录-%d”,已映射[43]。整数);

  msync((void *)mapped,NRECORDS*sizeof(record),MS _ ASYNC);

  munmap((void *)mapped,NRECORDS * sizeof(record));

  关闭(f);

  退出(0);

  }

  在第13章,我们将看到另一个共享内存程序:System V共享内存。

  摘要

  在本章中,我们学习了Linux如何提供对文件和设备的直接访问。我们还了解了库函数是如何建立在这些底层函数之上,为程序问题提供灵活的解决方案的。因此,我们可以只用几行代码编写一个相当强大的目录浏览例程。

  现在我们已经对文件和目录操作有了足够的了解,我们可以使用一个更加结构化的基于文件的解决方案来将我们在第2章末尾编写的CD程序转换成C程序。但是,这个时候我们还不能给程序添加新的功能,所以我们会在知道如何处理屏幕和键盘之后重新编写,这将是接下来两章的内容。

linux中文件操作命令,linux文件信息详解