欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

系统IO和标准C库IO函数 ——Linux编程

程序员文章站 2022-06-07 14:30:05
...

一、C库IO函数工作流程示意图:

 

                系统IO和标准C库IO函数 ——Linux编程

 FILE 类型的指针,是特殊结构体类型,包含文件描述符、读写指针位置、内存地址等信息,用于文件读写操作。

I/O缓冲区用于利用内存减少硬盘操作。在右侧三种情况下刷新缓冲区,存到硬盘上。

磁盘为什么这么慢?

         大部分硬盘是机械硬盘,读取寻道时间和写入寻道时间都是在毫秒级(ms)、相对于内存来说读写速度都非常快,因为内存术语电子设备,读写速度都是纳秒(ns)级别的。

          1s=1000ms

          1s=1000,1000us

          1s=1000,000,000ns

二、PCB和文件描述符

           每一个新的文件打开,则会占用一个文件描述符(整数),而且使用的空间是空闲的最小的一个文件描述符。

            系统IO和标准C库IO函数 ——Linux编程

 前三个文件描述符默认打开。

三、虚拟地址空间

程序启动后,在磁盘上分配4G空间供进程使用,最多4G,用多少分多少。

        系统IO和标准C库IO函数 ——Linux编程

        0-3G在用户区,程序员可操作;3-4G为内核区,程序员不可操作。受保护的地址(0-4K)也不许用户访问,如NULL在此区域。程序从main函数开始执行,即从代码段执行,然后根据代码中变量类型等将元素分配到各个空间中。

查看文件类型,file命令:

系统IO和标准C库IO函数 ——Linux编程

            注:程序启动后,从硬盘上分配 4G 空间可供选择。但并不会少4G空间。实际上,你用了多少空间,硬盘就会少多少空间。

四、库函数与系统函数的关系

 

         系统IO和标准C库IO函数 ——Linux编程

五、Linux系统IO函数

1、open函数

            man 2 open 

              函数原型:

                                #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);

              参数:

                           falgs 设置:

                                O_RDONLY                     以只读方式打开文件

                                O_WRONLY                    以只写的方式打开文件

                                O_RDWR                        以读写的方式打开

                                O_CREAT                       如果文件不存在则创建文件

                                O_EXCL                          如果文件存在,则强制  open() 操作失败

                                O_TRUNC                       如果文件存在,将文件清零

                                O_APPEND                     把文件添加内容的指针设到文件的结束处

                           mode 设置:

                               文件权限 = 给定对的文件权限   &    本地掩码(取反)

                                例如:

                                       设定权限     0777

                                       umask 出来的本地掩码是   0002

                                       777  ----------------------------二进制                                          111 111 111

                                       002  ----------------------------二进制  00  000 010    取反后得   111 111 101

                                                                                                                                   &   (按位与)

                                                                                                                        实际权限  111 111 101 

                              即实际权限为 0775

               返回值:

                               若成功返回文件描述符;若出错,返回-1                           

2、read函数

               函数原型:

                           #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);

               返回值:

                            读到的字节数,若已到达文件结尾,返回 0 ;若出错 返回 -1  。   

3、write函数

              函数原型:

                                  #include <unistd.h>

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

               返回值:

                             若成功,返回已写的字节数;若出错 返回 -1 ;读完了,返回0。

              open,read,write 函数的运用:从一个文件汇总读取内容后,写入另一个文件中

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
 
int main(void)
{
	int fd = 0;
	int fd_write = 0;
 
	int size = 0; 
	char buf[1024] = "";
 
	fd = open("./open.c",O_RDONLY);
	if( fd == -1 )
	{
		perror("open file:");
		exit(1);
	}
 
	// 创建一个新的文件,不存在则创建,权限777,截断文件
	fd_write = open("stdio1.h",O_RDWR|O_CREAT|O_TRUNC,0777);
 
	if( fd_write == -1 )
	{	
		perror("open file:");
		exit(1);
	}
 
	// 读文件 
	size = read(fd,buf,sizeof(buf));
	if(size == -1 )
	{
		perror("read file:");
		exit(1);
	}
	while( size )
	{
 		// 读文件 
		size = read(fd,buf,sizeof(buf));
		printf("buf = %s\n",buf);
		// 创建文件
		// 写文件
		write(fd_write,buf,strlen(buf));
 
	    // 清空缓冲区
		memset(buf,0,sizeof(buf));
	}
     	
	close(fd);
 
	close(fd_write);
 
	return 0;
}

 

4、lseek函数

              函数原型:

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

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

              作用: 设置文件偏移量。

                         若文件的偏移量大于当前文件的长度,在这种情况下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞。位于文件中没有写过的字节都被 读为0.、

                         文件中的空洞并不要求在磁盘上占用存储区。

               参数:

                           whence的取值:

                                 
                                SEEK_SET               文件的便宜位置设置为距开始位置 offset 个字节
                                       
                                SEEK_CUR               文件的便宜位置设置为当前值 + offset  ,offset 的值可正可负
             
                                SEEK_END               文件的便宜位置设置为文件长度 +offset ,offset 的值只能W为正的,只能向后拓展不能向向拓展         

               返回值:

                               若成功返回文件描述符;若出错,返回-1                         

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<stdio.h>
 
 
int main(void)
{
	int fd = open("./bb.txt",O_RDWR);
 
	if( fd == -1)
	{
		perror("open bb.txt:");
		exit(1);
	}
 
	int ret = lseek(fd,0,SEEK_END);
	printf("file length = %d\n",ret);
	
	// 文件扩展
	ret = lseek(fd,2000,SEEK_END);
	printf("return value = %d\n",ret);
 
	// 实现文件拓展,需要最后一次写操作
 
	write(fd,"a",1);
 
	close(fd);
	
	return 0;
}

5.获取文件属性—stat、lstat、fstat

5.1、函数原型

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

                int stat(const char *path, struct stat *buf);
                int fstat(int fd, struct stat *buf);
                int lstat(const char *path, struct stat *buf);


5.2、参数

      1、path :文件名或者目录名

      2、fd :    文件描述符

      3、 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 */                   // 连到该文件的硬链接数目,新建的文件则硬连接数为 1 
               uid_t     st_uid;     /* user ID of owner */                          //    用户ID
               gid_t     st_gid;     /* group ID of owner */                        // 组ID
               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 */           // 块数
               time_t    st_atime;   /* time of last access */                                  //  最后一次访问时间
               time_t    st_mtime;   /* time of last modification */                         // 最后一次修改时间
               time_t    st_ctime;   /* time of last status change */                       // 最后一次改变时间
           };

       st_mode :该变量占 2 byte,共16位       

               (1)、掩码的使用: st_mode  &   掩码

               (2)、其他*限( 0-2 bit )

                        (a)、S_IROTH    00004                 读权限           

                        (b)、S_IWOTH    00002                写权限                     掩码:S_IRWXO  00007

                        (c)、S_IXOTH    00001                  执行权限

               (3)、所属组权限(3-5bit)

                        (a)、S_IRWXG    00070              读权限           

                        (b)、S_IRGRP    00040              写权限                     掩码:S_RWXG  00070

                        (c)、S_IXGRP    00010               执行权限

               (4)、文件所有者权限(6-8bit)

                        (a)、S_IRUSR    00400               读权限           

                        (b)、S_IWUSR    00200              写权限                     掩码:S_IRWXU  00700

                        (c)、S_IXUSR    00100                执行权限

               (5)、文件特权位(9-11bit)

                        (a)、 S_ISUID    0004000            设置用户ID                 

                        (b)、 S_ISGID    0002000            设置组ID                   文件特权位很少用

                        (c)、 S_ISVTX    0001000          设置黏住位

               (6)、文件类型(12-15bit)

                       (a) 、S_IFSOCK   0140000   socket(套接字)
                       (b) 、S_IFLNK    0120000   symbolic link(符号链接--软连接)
                       (c) 、S_IFREG    0100000   regular file(普通文件)
                       (d)、 S_IFBLK    0060000   block device(块设备)                                掩码:S_IFMT 017000
                       (e) 、S_IFDIR    0040000   directory(目录)
                       (f) 、 S_IFCHR    0020000   character device(字符设备)
                       (g)、 S_IFIFO    0010000   FIFO(管道)

5.3、返回值

         以上三个获取文件属性的函数 若成功,返回0;若失败,返回 -1;

5.4、stat、lstat、fstat之间的区别

       1、fstat  函数:系统调用的是一个 ”文件描述符”,而另外两个则直接接收“文件路径”。文件描述符是我们用 open 系统调用后得到的,而文件全路径直接写就可以了。

       2、stat   函数与 lstat 函数的区别: 当一个文件是符号链接时,lstat 函数返回的是该符号链接本身的信息;而 stat 函数返回的是该链接指向文件的信息。 

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<stdlib.h>
 
int main(int argc,char *argv[])
{
	if( argc<2 )
	{
		perror("a.out ");
		exit(1);
	}
 
	struct stat st;
 
	int ret = lstat(argv[1],&st);
	if( ret == -1)
	{
		perror("lstat");
		exit(1);
	}
 
	int size = st.st_size;
 
	printf("file size = %d\n",size);
 
	return 0;
}

系统IO和标准C库IO函数 ——Linux编程

系统IO和标准C库IO函数 ——Linux编程

 5.5 使用 stat() 函数实现一个简单的  ls -l Shell 命令:

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<pwd.h>     // 所有者信息
#include<grp.h>     // 所属组信息
#include<time.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
 
int main(int argc,char *argv[])
{
	if( argc<2 )
	{
		perror("./a.out filename\n");	
		exit(1);
	}
 
	struct stat st;		
	int i;
	for( i = 1; i<argc; i++)
	{
 
		int ret = stat(argv[i],&st);   // 获取文件或者目录的所有信息存储于 st 结构体中
 
		if( ret == -1 )
		{
			perror("stat");
			exit(1);
		}
 
		// 存储文件类型和访问权限
 
		char perms[11] = {0};
 
		// 判断文件类型
 
		switch( st.st_mode & S_IFMT )
		{
			case S_IFSOCK:   // 套接字文件
				perms[0] = 's';
				break;
			case S_IFLNK:	 // 软连接文件
				perms[0] = 'l';
				break;
			case S_IFREG:	 // 普通文件
				perms[0] = '-';
				break;
			case S_IFBLK:    // 块设备文件
				perms[0] = 'b';
				break;
			case S_IFDIR:    // 目录文件
 
				perms[0] = 'd';
				break;
			case S_IFCHR:    // 字符设备文件
 
				perms[0] = 'c';
				break;
			case S_IFIFO:    // 管道文件
 
				perms[0] = 'p';
				break;
			default:
				break;
 
		}
 
		// 判断文件的访问权限
		// 文件的所有者
		perms[1] = (st.st_mode & S_IRUSR) ? 'r':'-';
		perms[2] = (st.st_mode & S_IWUSR) ? 'w':'-';
		perms[3] = (st.st_mode & S_IXUSR) ? 'x':'-';
 
		// 文件的所属组
		perms[4] = (st.st_mode & S_IRGRP) ? 'r':'-';
		perms[5] = (st.st_mode & S_IWGRP) ? 'w':'-';
		perms[6] = (st.st_mode & S_IXGRP) ? 'x':'-';
 
		// 文件的其他用户
 
		perms[7] = (st.st_mode & S_IROTH) ? 'r':'-';
		perms[8] = (st.st_mode & S_IWOTH) ? 'w':'-';
		perms[9] = (st.st_mode & S_IXOTH) ? 'x':'-';
 
		// 硬链接计数
 
		int nums = st.st_nlink;
 
		// 文件所有者
 
		char *fileuser = getpwuid(st.st_uid)->pw_name;
 
		// 文件所属组
		char *filegroup = getgrgid(st.st_gid)->gr_name;
 
		// 文件大小
		int size = (int)st.st_size;
 
		// 文件修改时间
 
		char *time = ctime(&st.st_mtime);
		char mtime[512]="";
		strncpy(mtime,time,strlen(time)-1);
 
		// 保存输出信息格式
		char buf[1024]={0};
 
		// 把对应信息按格式输出到 buf 中
		sprintf(buf,"%s %d %s %s      %d %s %s",perms,nums,fileuser,filegroup,size,mtime,argv[i]);
 
		// 打印 buf 
		printf("%s\n",buf);
 
		//	drwxrwxr-x 3 arrayli arrayli      4096 11月 13 23:19 day05
		//	-rw-r--r-- 1 arrayli arrayli      8980 11月  7 22:05 examples.desktop
 
	}
 
	return 0;
}
 

 系统IO和标准C库IO函数 ——Linux编程

实用函数,字符串转整数。

系统IO和标准C库IO函数 ——Linux编程 

6 目录操作

6.1 chdir 函数

       1、作用:修改当前进程的路径

       2、函数原型:

        #include <unistd.h>
             int chdir(const char *path);

6.2 getcwd 函数

       1、作用:获取当前进程工作目录

       2、函数原型:

       #include <unistd.h>
                char *getcwd(char *buf, size_t size);
                char *getwd(char *buf);

chdir 函数和 getcwd 函数的运用:

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<stdio.h>
 
 
int main(int argc, char *argv[] )
{
 
	if( argc<2 )
	{
		perror("./a.out filepath");
		exit(1);
	}
 
	printf(" agrv[1] = %s\n",argv[1]);
	// 修改当前的路径
	int ret =chdir(argv[1]);
	if( ret == -1 )
	{
		perror("chdir");
		exit(1);	
	}
 
	// 在这里通过在改变后的目录下创建一个新的文件,来证明目录已经改变
	int fd = open("chdir.txt",O_CREAT|O_RDWR,0644);
	if( fd == -1 )
	{
		perror("open");	
		exit(1);
	}
 
	close(fd);
 
	// 获取改变目录后的目录名
	char buf[100]={0};
 
	getcwd(buf,sizeof(buf));
	printf("current dir: %s\n",buf);
 
	return 0;
}

 

6.3 rmdir 函数

       1、作用:删除一个目录

       2、函数原型:

             #include <unistd.h>
              int rmdir(const char *pathname);

6.4 mkdir 函数

       1、作用:创建一个目录

       2、函数原型:

       #include <sys/stat.h>
       #include <sys/types.h>
            int mkdir(const char *pathname, mode_t mode);


6.5 opendir  函数

       1、作用:打开一个目录

       2、函数原型

       #include <sys/types.h>
       #include <dirent.h>
 
           DIR *opendir(const char *name);
           DIR *fdopendir(int fd);
 

6.6 readdir 函数

       1、作用:读目录

       2、函数原型:

         #include <dirent.h>
            struct dirent *readdir(DIR *dirp);

      3、返回值:             返回一个记录项  

         struct dirent {
               ino_t          d_ino;       /* inode number */                  // 目录进入点的 inode
               off_t          d_off;       /* not an offset; see NOTES */      // 目录文件头开始至此目录进入点的位移
               unsigned short d_reclen;    /* length of this record */         // d_name 长度
               unsigned char  d_type;      /* type of file; not supported      // d_name 所指的文件夹 
                                              by all filesystem types */
               char           d_name[256]; /* filename */                      // 文件名
         };
 
 
         d_tyep 有 8 种类型:
          
            (1)、 DT_BLK      This is a block device.           块设备
            (2)、 DT_CHR      This is a character device.       字符设备
            (3)、 DT_DIR       This is a directory.              目录
 
            (4)、 DT_FIFO     This is a named pipe (FIFO).      管道
            (5)、 DT_LNK      This is a symbolic link.          软链接
            (6)、 DT_REG      This is a regular file.           普通文件
            (7)、 DT_SOCK     This is a UNIX domain socket.     套接字
            (8)、 DT_UNKNOWN     The file type is unknown.         未知类型
 

  6.7 closedir 函数

        1、作用:关闭一个目录

        2、函数原型:

       #include <sys/types.h>
 
       #include <dirent.h>
 
       int closedir(DIR *dirp);
       3、返回值:

       若函数执行成功,返回0;若失败,返回 -1.

#include<unistd.h>
#include<dirent.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
 
 
// 获取 root 目录下的文件个数
int get_file_count(char *root)
{
	// open dir
	DIR * dir = NULL;
	dir = opendir(root);
	if( NULL == dir )
	{
		perror("opendir");
		exit(1);
	}
 
	// 遍历当前打开的目录
	struct dirent* ptr = NULL;
	char path[1024]={0};
	int total = 0;
	while( (ptr = readdir(dir) )!= NULL)
	{
		// 过滤掉 . 和 ..
		if( strcmp(ptr->d_name,".") == 0 || strcmp(ptr->d_name,"..") == 0 )
		{		
			continue;	
		}
	
		// 如果是目录,递归读目录
		if(ptr->d_type == DT_DIR)
		{
			sprintf(path,"%s/%s",root,ptr->d_name);
			total += get_file_count(path);
		}
 
		// 如果是普通文件
		if( ptr->d_type == DT_REG )
		{
				total++;	
		}
	}
	
	// 关闭目录
	closedir(dir);
	return total;
}
 
int main(int argc,char *argv[])
{
	if( argc<2 )
	{
		perror("./a.out dir\n");
		exit(1);	
	}
 
	// 获取文件个数
	int count =	get_file_count(argv[1]);
 
	printf("%s has file numbers : %d\n",argv[1],count);	
	return 0;
}

6.8 dup 和 dup2 函数

       1、作用:复制现有的文件描述符

       2、函数原型:       

         #include <unistd.h>
 
       int dup(int oldfd);
       int dup2(int oldfd, int newfd);
 

3、返回值:

              (1)、dup 返回的是文件描述符中没有被占用的

              (2)、dup2 分两种情况讨论下:

                      (a)、oldfd----->newfd 如果  newfd 是一个被打开的文件描述符,在拷贝前会先关掉 newfd

                      (b)、oldfd------>newfd是同一个文件描述符,不会关掉 newfd , 直接返回 oldfd

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
 
int main(void)
{
	int fd =open("a.txt",O_RDWR);
	if( fd == -1 )
	{
		perror("open");
		exit(1);
	}
 
	printf("file open fd = %d\n",fd);
 
	// 找到进程文件描述符表   ======= 第一个========== 可用的文件描述符
	// 将参数指定的文件复制到该描述后          返回这个描述符
 
	int ret = dup(fd);
	if( fd == -1 )
	{
		perror("dup");
		exit(1);
	}
 
	printf(" dup fd = %d\n",ret);
	char *buf = "你是猴子请来的救兵吗??\n";
	char *buf1 = "你大爷的,我是程序猿!!!\n";
 
	write(fd,buf,strlen(buf));
	write(ret,buf1,strlen(buf1));
 
	close(fd);
	
	return 0;
}
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
 
int main(void)
{
	int fd =open("english.txt",O_RDWR);
	if( fd == -1 )
	{
		perror("open");
		exit(1);
	}
 
	int fd1 =open("a.txt",O_RDWR);
	if( fd1 == -1 )	
	{
		perror("open");
		exit(1);
	}
 
	printf("fd = %d\n",fd);
	printf("fd1 = %d\n",fd1);
 
	int ret = dup2(fd1, fd);
	if( ret == -1 )
	{	
		perror("dup2");
		exit(1);
	}
 
	printf(" current fd = %d\n",ret);
 
	char *buf = "主要看气质 !!!!!!!!!!!!!!!!!\n";
 
	write(fd,buf,strlen(buf));
	write(fd1,"hello world!",12);
 
	close(fd);
	close(fd1);
 
	return 0;
}

6.9 fcntl 函数

        1、作用:改变已经打开文件的属性

         2、函数原型:

         #include <unistd.h>
      #include <fcntl.h>
 
       int fcntl(int fd, int cmd, ... /* arg */ );             这是一个可变长参数的函数
  3、功能:

 (1)、复制一个现有的描述符--------cmd                F_DUPFD

             (2)、 获得 / 设置文件状态标价--------cmd( 参数设置如下 )             

                       (a)、F_GETFD

                       (b)、F_STFD

             (3)、获得 / 设置文件标记状态-------- cmd 

                       (a)、

                               O_RDONLY                      只读打开

                               O_WRONLY                     只写打开

                               O_RDWR                         读写打开

                               O_EXEC                           执行打开

                               O_SEARCH                      搜索打开

                               O_APPEND                      追加打开

                               O_NONBLOCK                 非阻塞模式

                       (b)、F_SETFL

                               O_APPEND                      

                               O_NONBLOCK                

             (4)、  获得  / 设置异步 I / O 所有权-------- cmd 

                       (a)、F_GETOWN

                       (b)、F_SETOWN

             (5)、获得 / 设置记录锁-------- cmd 

                       (a)、F_GETLK

                       (b)、F_SETLK

                       (c)、SETLKW

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
 
int main(void)
{
	int flag;
	int fd;
 
	// 测试字符串
 
	char *p = "我们是一个由中国特使*的国家!!!!!";
	char *q ="呵呵,*好哇";
 
	// 以只写方式打开文件
 
	fd = open("test.txt",O_WRONLY);
	if( fd == -1 )
	{
		perror("open");
		exit(1);
	
	}
 
	// 输入新的内容,该内容会覆盖原来的内容
	if( write(fd,p,strlen(p)) == -1 )
	{
		perror("write");
		exit(1);
	}
 
	//	使用 F_GETFL 命令得到文件状态标志
	int flags = fcntl(fd,F_GETFL,0); 
	if( flags == -1 )
	{
		perror("fcntl");
		exit(1);
	}
 
	//	将文件状态标志添加 “追加写” 选项
 
	flag |= O_APPEND;
 
	// 将文件状态修改为追加写
 
	if( fcntl(fd,F_SETFL,flag) == -1 )
	{
		perror("fcntl");
		exit(1);
	}
 
	// 再次输入新的内容,该内容会追加到旧内容对的后面
 
	if( write(fd,q,strlen(q)) == -1 )
	{
		perror("write again");
		exit(1);
	}
	
	return 0;
}