LInux系统及I/O

Linux一切皆文件

linux由

普通文件

目录文件

套接字文件

等等

 

open函数

进程是通过调用open来打开一个已经存在的文件,或者创建一个不存在的文件的。

函数原型

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(char *filename, int flags, mode_t mode);

参数解析

filename:一个文件路径,可以是相对路径,也可以是绝对路径

flags:它有两个可选信息

主要信息:

O_RDONLY:只读

O_WRONLY:只写

O_RDWR:可读可写

次要信息:

O_CREAT:如果文件不存在,就创建它的一个截断的空文件

O_TRUNC:如果文件存在就阶段他

O_APPEND:每次写操作,都在文件结尾处添加

mode参数

主要是一些用户访问权限信息

我们知道,在Linux中,我们文件权限可以看成八进制的标志位

比如对于可读,可写,可执行 分别对应于二进制的100,010,001

在比如可读可写对应于110(十进制6),可读可写可执行(111十进制7)

而对于用户而言,又分为拥有者用户,同一用户组的用户,和所有用户,共同构成一个文件的访问权限信息

比如777 就是对拥有者,同一组用户,所有用户打开所有权限。

而mode参数,就是根据不同的权限信息的“&”和“|”操作,来赋予要给文件不同权限

比如下面这个图

不同的掩码对应不同的权限

 S_IRWXU 00700 权限,代表该文件所有者具有可读、可写及可执行的权限。
  S_IRUSR 或S_IREAD, 00400权限,代表该文件所有者具有可读取的权限。
  S_IWUSR 或S_IWRITE,00200 权限,代表该文件所有者具有可写入的权限。
  S_IXUSR 或S_IEXEC, 00100 权限,代表该文件所有者具有可执行的权限。
  S_IRWXG 00070权限,代表该文件用户组具有可读、可写及可执行的权限。
  S_IRGRP 00040 权限,代表该文件用户组具有可读的权限。
  S_IWGRP 00020权限,代表该文件用户组具有可写入的权限。
  S_IXGRP 00010 权限,代表该文件用户组具有可执行的权限。
  S_IRWXO 00007权限,代表其他用户具有可读、可写及可执行的权限。
  S_IROTH 00004 权限,代表其他用户具有可读的权限
  S_IWOTH 00002权限,代表其他用户具有可写入的权限。
  S_IXOTH 00001 权限,代表其他用户具有可执行的权限。

而且open函数返回的是一个文件描述符,而且是没有打开的最小文件描述符

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(){
	int fd1,fd2;
	fd1 = open("a.txt", O_RDONLY, 0);
	fd2 = open("b.txt", O_RDONLY, 0);
	printf("fd2=%d\n",fd2);
return 0;
}

输出结果:
fd2=4

之前的文件描述符又标准输入0,标准输出1,和错误2

然后fd1打开为3,所以fd2打开后为4

读写文件

系统级的读写主要由两个函数完成read和write


ssize_t read(int fd,void *buf,size_t n);
/*
从描述符为 fd 的当前文件位置复制最多 n 个字节 到 内存位置 buf

 返回:若成功则为读的字节数,若为EOF则为0,若出错为-1
    
*/

ssize_t write(int fd,const void *buf,size_t n);
/* 
从内存位置 buf 复制最多n个字节 到描述符为 fd 的当前文件位置。
返回:若成功则为写的字节数,若出错为-1
*/

我们现在将open和read,write综合起来,实现一个创建两个文件,然后复制的程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(){
	int fdopenin;//新打开文件描述符
	int fdopenout;//复制目的
	int fdin;//复制源
	int fdout;//复制目的
	extern int errno;
	char f[] = {'t','h','i','s',' ','i','s',' ','i','n','\n'};
	char c;
	fdopenin = open("newfilein",O_RDWR|O_CREAT,S_IRWXG|S_IRWXU|S_IRWXO);
	fdopenout = open("newfileout",O_RDWR|O_CREAT,S_IRWXG|S_IRWXU|S_IRWXO);
	printf("新创建的复制源文件描述符为%d\n",fdopenin);
        printf("新创建的复制目的文件描述符为%d\n",fdopenout);

	//先向fdopenin写入一些东西
	write(fdopenin, f, 11);
	close(fdopenin);//关闭
	close(fdopenout);//关闭
	fdin = open("newfilein",O_RDWR,0);
	fdout = open("newfileout",O_RDWR|O_APPEND,0);
	printf("打开的复制源文件描述符为%d\n",fdin);
	printf("打开的复制目的文件描述符为%d\n",fdout);	
	printf("errno=%d\n",strerror(errno));
	//复制过程
	while(read(fdin, &c, 1)!=0)
		write(fdout, &c, 1);

	
	
	return 0;
}

输出结果:
新创建的复制源文件描述符为3
新创建的复制目的文件描述符为4
打开的复制源文件描述符为3
打开的复制目的文件描述符为4
errno=2012729639

newfilein:
this is in
newfileout:
this is out

读取文件元数据

这是stat的数据结构信息

#include <unistd.h>
 #include <sys/stat.h>
 
 int stat(const char *filename,struct stat *buf);//以文件名作为输入
int fstat(int fd,struct stat *buf); //以文件描述符作为输入
 
 struct stat {
    dev_t     st_dev;         /* ID of device containing file */
    ino_t     st_ino;         /* inode number */
   mode_t    st_mode;        /* protection */
   nlink_t   st_nlink;       /* number of hard links */
   uid_t     st_uid;         /* user ID of owner */
   gid_t     st_gid;         /* group ID of owner */
   dev_t     st_rdev;        /* device ID (if special file) */
   off_t     st_size;        /* total size, in bytes */
   blksize_t st_blksize;     /* blocksize for filesystem I/O */
   blkcnt_t  st_blocks;      /* number of 512B blocks allocated */

   /* Since Linux 2.6, the kernel supports nanosecond
      precision for the following timestamp fields.
      For the details before Linux 2.6, see NOTES. */

   struct timespec st_atim;  /* time of last access */
   struct timespec st_mtim;  /* time of last modification */
   struct timespec st_ctim;  /* time of last status change */

#define st_atime st_atim.tv_sec      /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};

这里有两个函数,一个是fstat一个是stat

stat是以文件名作为输入,而fstat是以文件描述符作为输入

我们在这个数据结构里只关心两个,一个是st_size,一个是st_mode

st_size是文件大小

st_mode是文件的权限位

包括了,是什么文件,以及不同用户的访问权限。

可以通过

S_ISREG(st_mode):是否是普通文件
S_ISDIR(st_mode):是否是目录
S_ISSOCK(st_mode):是否是套接字
int main(int argc, char **argv){
	struct stat stat;
	char *type,*readok;
	
	stat("newfilein",&stat);
	if(S_ISREG(stat.st_mode))
		type="regular";
	else if(S_ISDIR(stat.st_mode))
		type="directory";
	else type = "other";
	
	if((stat.st_mode & S_IRUSR))
		readok="yes";
	else readok="no";
	printf("type: %s, read: %s\n",type,readok);
	
	
	return 0;
}

这样可以看到权限信息

获取目录

#include <sys/types.h>
 #include <dirent.h>
 
 DIR *opendir(const char *name);
 
 struct dirent *readdir(DIR *dirp); //若成功,返回指向下一个目录的指针
 
 struct dirent{
     ino_t ;              /*inode number*/
    char d_name[256];    /*Filename*/
};

int closedir(DIR *dirp);//关闭目录

opendir函数打开一个与给定的目录名name相对应的目录流,并返回一个指向该目录流的指针。打开后,该目录流指向了目录中的第一个目录项。

readdir函数返回一个指向dirent结构体的指针,该结构体代表了由dir指向的目录流中的下一个目录项

这里就来做一个测试吧

我是在work工作目录进行操作,运行如下代码

#include<sys/types.h>
#include <stdio.h>
#include<dirent.h>
#include<unistd.h>
int main(int argc,char **argv)
{
    DIR * dir;
    struct dirent * ptr;
    int i;
    if(argc==1)
        dir=opendir("./");//打开某个目录流,是一个目录列表一样
    else
        dir=opendir(argv[1]);
    while((ptr=readdir(dir))!=NULL)//每次都指向目录流的下一个目录项
    {
        printf("d_name: %s\n",ptr->d_name);//需要更详细的信息你可以修改该句
    }
    closedir(dir);
    return 0;
}

运行结果,将当前目录的内容都输出了

共享文件

  • 描述符表。每个进程都有它独立的描述符表,它的表项是由进程打开的文件描述符来索引的。每个打开的描述符表项,指向文件表中的一个表项。

  • 文件表。打开文件的集合是由一张文件表来表示的,所有进程共享这张表。

  • v-node表。同文件表一样,所有进程共享这张v-node表。每个表项包含stat结构中的大多数信息,包括st_mode和st_size成员。

正如我们所说的,一个进程都维护自己的文件描述符表,一个进程多次打开同一个文件,文件指针是互不干扰的,因为他们各自维护自己文件表(数据结构),但是如果修改了文件的内容,那么v结点信息改变,则都会受到影响。

可知道v-node表是与文件对应的,而文件表是与进程对应的。

我们知道一个文件描述符表开头三个都是标准输入0,标准输出1,标准错误2

#include <sys/types.h>
#include <sys/stat.c>
#include <fcntl.h>
#include <stdio.h>
int main(){
    int fd1, fd2;
    char c;
    fd1 = open("a.txt",O_RDONLY,0);
    fd2 = open("a.txt",O_RDONLY,0);
    read(fd1,&c,1);
    read(fd2,&c,1);
    printf("c=%c\n",c);
    return 0;
}

这里,我们同一个进程使用open两次,导致文件表增加了两个,所以对应的文件位置不同

当第一个read读取时,文件1的文件位置+1,然后文件2的文件位置不受影响,所以第二个read读取时还是第一个字符

重定向

重定向实现的一种方式是dup2

int dup2(int oldfd, int newfd);  

这个函数就是复制oldfd到newfd

也就是说在进程的文件描述符表中,newfd的内容将会被oldfd替代

比如我们根据这个图调用dup2(fd2,fd1);

那么

这样实现了重定向

#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(){
    int fd1,fd2;
    char c;
    fd1 = open("a.txt",O_RDONLY,0);
    fd2 = open("a.txt",O_RDONLY,0);
    read(fd2,&c,1);
    dup2(fd2,fd1);
    read(fd1,&c,1);
    printf("c=%c\n",c);
return 0;
}

这里因为fd2首先读取一个字符,所以文件位置+1,

这时候调用了dup2,将fd1的内容替换成了fd2,这样原来fd1的位置也指向了fd2指向的文件表项

那么文件位置也共享了,应该是第二个字符,这时候去读,果然就读到了第二个字符。

发布了66 篇原创文章 · 获赞 31 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_43272605/article/details/103226634