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

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

                

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

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

磁盘为什么这么慢?

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

          1s=1000ms

          1s=1000,1000us

          1s=1000,000,000ns

二、PCB和文件描述符

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

            

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

三、虚拟地址空间

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

        

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

查看文件类型,file命令:

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

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

         

五、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;
}

 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;
}
 

 

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

 

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;
}

猜你喜欢

转载自blog.csdn.net/u011285208/article/details/81277193