linux系统IO

单片机是“裸奔”的,
	APP					你  要吃猪肉 
	--------			屠夫 
	Hardware			杀猪
	特点:
		简单,直接操作硬件。
	缺点: 
		搞应用的必须要了解硬件的实现细节;
		无并发,不能同时允许多个程序,它是“单任务”
	
“带OS”	
	APP				你要吃猪肉
	------------	
	os 				钱  屠夫 
	------------
	Hardware			杀猪 
	
	特点:  
		开发的可以把业务放在你的应用开发业务逻辑,
		不必关心硬件的具体实现细节;
		提供了并发功能,允许同时跑多个应用,“多任务”
		
os? Operating system(操作系统)是软件
	功能:
		操作系统是管理和分配硬件资源的系统软件		

linux操作系统进行应用开发,就是调用操作系统linux的API函数
接口去操作硬件,或者说去使用linux提供的服务

如: 
	open
	close
	read
	write
	....

1.linux是一个开源的操作系统
Everything is a file in linux(Unix).
在linux下面,任何东西都是文件,
或者说,在linux下面,操作任何东西,都是
操作文件,都是通过文件的接口去实现

文件系统:用来存储、组织、管理文件的一套方式、方法、协议
			及其软件实现等等

文件:
	文件属性:i-node. 唯一标识一个文件存在与否
	文件内容 
	
	linux内核中 
		struct inode{}  用来描述一个文件的物理inode的信息
						系统识别到一个文件的存在了,就会为它创建
						一个struct inode,一个文件只会唯一对应一个
						struct inode
		
		struct file{}	用来描述已经打开的文件 
						文件状态标记(如:O_RDONLY,O_WRONLY,...)
						文件偏移量/offset(类似于,”光标“)
						struct inode *
						
						每一个打开的文件都会对应一个struct file 
						一个文件可以被不同的程序(进程)打开
		操作文件过程 
			struct file --> struct inode -->硬件上的inode ---》文件内容 
			
		linux为了屏蔽文件操作的具体实现细节,它会为每个进程
			创建一个”进程的文件表项“:struct file *的数组
			保存每个进程打开的文件的struct file的地址
			
			struct file* fds[]
							0 	struct file* ---> struct file --->struct inode
							1 
							2 
							3 
							4
								...
		linux再提供了操作文件的函数接口: 
			open 返回值,返回”进程文件表项“的下标, int
				"文件描述符":在linux应用中,用来描述一个打开的文件,
				一个打开的文件的id是唯一,后续操作该文件,都是通过该文件
				描述符。
			read 
			write 
			close 
	-------------
		linux系统提供的这些用于文件的接口函数(如:open, read,write,lseek...)
		我们称之为”系统IO“

2.linux”系统IO“使用
在linux系统文件操作的接口函数:

(1)打开文件open函数 
	头文件
		#include <sys/types.h>
		#include <sys/stat.h>
		#include <fcntl.h>
	函数的功能
		open在linux系统下,用来打开或者创建(创建并且打开)一个文件的
	函数的原型
		int open(const char *pathname, int flags);
		int open(const char *pathname, int flags, mode_t mode);
	函数的参数 
		const char *pathname:要打开或者创建的文件名,带路径(如果不带路径,就是当前路径)
		int flags:打开文件的标记
					O_RDONLY:read only 只读
					O_WRONLY:write only 只写 
					O_RDWR:  read and write 读写
						以上三个标记只能选一个(文件打开的方式)
					O_APPEND:追加标记,打开文件后,文件偏移量会在文件末尾;
					O_CREAT:创建标记,如果文件不存在,则创建它
					O_EXCL:该标志一般和O_CREAT配合使用,用来测试文件是否存在的。
							如果你指定 O_CREAT | O_EXCL,如果文件不存在,则open会
							失败;并且errno == EEXIST
					O_TRUNC:truncate  截短
							 文件截短,把文件内容清0
							 
					O_NONBLOCK:	 block 非阻塞方式打开文件 
						非阻塞 :”不等待“  
							如果文件没有内容,read不会阻塞,直接返回一个错误
							如果文件没有空间啦,write不会阻塞,直接返回一个错误 
						 阻塞:”等待“(默认情况)
							如果文件没有内容,read会阻塞(直到有数据或者出错)
							如果文件没有空间啦,write会阻塞(直到可写或出错)
					。。。
						多个标记用”|“连接 
						如: 
							O_RDWR | O_CREAT | O_TRUNC 
							
		mode_t mode:指定创建的文件的权限,当第二参数中带”O_CREAT“
					有两种文件指定:rwx rwx rwx 
						(1)S_IRUSR S_IWUSR S_IXUSR "用户"
						   S_IRGRP S_IWGRP S_IXGRP "组用户"
						   S_IROTH S_IWOTH S_IXOTH "其它用户"
							如:S_IRUSR | S_IRGRP | S_IROTH
								=》r-- r-- r-- 
						(2) 0666	
							=> 
							110 110 110					
						
	函数的返回值: 
		成功:返回一个打开的文件描述符(>2)
				因为操作系统会自动为每个进程打开三个文件
					标准输入文件	文件描述符为  STDIN_FILENO(0)
					标准输出文件	文件描述符为  STDOUT_FILENO(1)
					标准出错文件	文件描述符为  STDERR_FILENO(2)
		失败:返回-1,同时errno被设置
		
			man errno  
			
			errno 是一个全局变量,#include <errno.h>
			是用来保存最后一个出错的错误码
			perror 把错误吗对应的提示打印出来
				perror("user indi");
				 =>user indi:错误码的提示信息 
				打印出来的: 
					用户自己的提示信息:系统的错误码的提示信息 

(2)操作文件(read write close ...)
	read函数
	头文件
		#include <unistd.h>
	函数功能
		将从fd所代表的文件中读取count个字节数到buf所指向的区域
	函数原型
		ssize_t read(int fd, void *buf, size_t count);
	函数参数
		int fd		//文件描述符,表示你要从哪个文件中读
		void *buf	//指向一段内存的首地址,表示你要把文件内容
					读到哪里去
		size_t count //表示你要读取的字节数				
	函数返回值
		>0 :返回实际读取到的字节数(有可能 <= count)
		=-1:失败,同时errno被设置。
		=0:表示已经读到了文件末尾啦。
		
		NOTE: 
			文件的偏移量("光标位置")会由内核自动维护,
			一般来说,打开文件时,offset = 0,每次成功
			read /write 了count个字节后,offset +=count 
	----------------------------------------------------------				
			
	write函数 
		头文件
			#include <unistd.h>
		函数功能
			将buf所指向的区域里面的数据的count个字节数数据写入到fd所代表的文件
		函数原型
			ssize_t write(int fd, const void *buf, size_t count);
		函数参数 
			int fd			//文件描述符 表示你要写到哪个文件中去
			const void *buf //指向要写的内容所在的首地址
			size_t count	//要写多少个字节
		函数返回值 
			>0:表示实际写成功的字节数
			=0:表示什么也没写
			=-1:表示出错了,同时errno被设置。
			
	lseek函数 
		头文件 
			#include <sys/types.h>
			#include <unistd.h>
		函数功能 
			用来重定位文件的偏移量{"光标位置"}的
		函数原型 
			 off_t lseek(int fd, off_t offset, int whence);
		函数参数
			int fd			//文件描述符 
			off_t offset	//偏移量,具体的新偏移量的位置 需要集合第三个参数:
			int whence		//定位方式,有如下三种: 
							SEEK_SET:基于文件开头定位
								新光标位置 = 文件开头 + offset (>=0)
							SEEK_CUR:基于当前光标定位
								新光标位置 = 当前光标位置 + offset (可正可负)
							SEEK_END:基于文件末尾定位
								新光标位置 = 文件末尾 + offset(可正可负)     
		函数返回值: 
			成功:返回新光标位置离文件开头的字节数 
			失败:返回-1,同时errno被设置。

(3)关闭文件 close函数 
	头文件 
		#include <unistd.h>
	函数功能
		用来关闭fd指定的文件
	函数原型
		int close(int fd);
	函数参数 
		int fd //文件描述符 
	函数返回值 
		成功: 返回0
		失败: 失败返回 -1,同时errno被设置。

3.umask函数
头文件
#include <sys/types.h>
#include <sys/stat.h>
函数功能
设置一个文件在创建时的掩码。
设置的权限位为1的bit,你在创建这个文件时不能指定
linux默认的umask 是 022 -》 000 010 010
默认情况下,创建文件时,不能指定S_IWGRP 和 S_IWOTH
函数原型
mode_t umask(mode_t mask);
函数的参数
mode_t mask //指定文件的掩码
函数返回值
返回系统使用的上一个的文件掩码

4.每个进程都会有字节的工作目录,
获取进程的当前的工作目录(getwd)
头文件
#include <unistd.h>
函数功能
用来获取进程当前工作目录
函数原型
char *getwd(char *buf);
函数参数
char *buf //用来保存进程当前工作目录的”绝对路径“,”字符串“
函数返回值
成功:返回当前工作目录字符串的首地址
失败: 返回NULL,同时errno被设置。

	注意:
		getwd 有一个巨大的bug? 你懂的。有越界的风险。
		如果buf指向的空间,不够大(当前目录字符串,超过buf所指向空间长度),
		getwd就有可能访问buf后面的内存空间,这样就有可能会造成内存的非法访问
		
为了解决getwd的这个bug,getcwd出现啦		
	头文件 
		#include <unistd.h>
	函数功能
		用来获取当前进程的工作目录
	函数原型
		char *getcwd(char *buf, size_t size);
	函数参数
		char *buf	//指向的空间用来保存当前的工作目录的路径字符串的
		size_t size //指定buf指向的空间的最大长度
	函数返回值
		成功: 返回当前工作目录的字符串的首地址
		失败: 返回NULL,同时errno被设置。
				失败的原因可能是: 
					如果当前工作目录的路径字符串长度 > size -1,这个函数就会报错
	get_current_dir_name 也是用来获取进程当前工作目录的绝对路径 需要在头文件前面
	添加 #define  _GNU_SOURCE  这个宏定义
		头文件 
			#include <unistd.h>
		函数功能
			用来获取进程当前工作目录的绝对路径名的
			只不过,这个函数在内部自动malloc足够长的空间
			来保存当前工作目录的路径名,并返回这个首地址。
			所以为了防止内存泄漏,调用这在使用完后,要free这个空间!!!
		函数原型
			char *get_current_dir_name(void);
		函数返回值 
			成功: 返回当前工作目录的字符串的首地址 
			失败: 返回NULL,同时errno被设置。

5.改变进程的当前工作目录
头文件
#include <unistd.h>
函数功能
改变进程的当前工作目录
函数原型
int chdir(const char *path);
int fchdir(int fd);

函数参数 
	const char *path	//填要切换到的工作目录的,目录字符串 
				如:"/home/china/gh"
	int fd				//填要切换的工作目录的,文件描述符 
							如: int fd = open("/home/china",O_RDONLY);  
								打开目录只能以读的标记打开
函数返回值 
	成功: 返回0
	失败: 返回-1,同时errno被设置。

6.文件截短(truncate/ftruncate函数)
头文件
#include <unistd.h>
#include <sys/types.h>
函数功能
用来给一个指定的文件截短到指定的长度
函数原型
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
函数参数
const char *path //填要截短的文件名
off_t length //截短后的文件内容的长度
length < 原文件的长度 ”截短“
length > 原文件的长度 ”扩展“,”留空洞“
函数返回值
成功:返回0
失败:返回-1,同时errno被设置。

7.删除一个文件
unlink函数
头文件
#include <unistd.h>
函数功能
用来删除一个普通文件或者结点的
函数原型
int unlink(const char *pathname);
函数参数
const char *pathname //要删除的哪个文件的文件名(带路径)

	函数返回值 
		成功: 返回0 
		失败:返回-1,同时errno被设置。
rmdir函数 
	头文件 
		#include <unistd.h>
	函数功能
		删除一个空目录
	函数原型 
		int rmdir(const char *pathname);
	函数参数 
		const char *pathname		//要删除的那个文件的文件名
	函数返回值 
		成功:返回0 
		失败:返回-1,同时errno被设置
remove函数 
	头文件 
		#include <stdio.h>
	函数功能
		用来删除一个普通文件或空目录
	函数原型
		int remove(const char *pathname);
	函数参数 
		const char *pathname //要删除的文件的名或要删除的空目录名
	函数返回值 
		成功:返回0 
		失败:返回-1,同时errno被设置。

作业:
1.利用linux提供的系统IO函数(如:open/close/read/write/lseek)
实现两个文件的内容拷贝
cp 1.txt 2.txt
思路
fd1 = open(1,);
fd2 = open(2,)

		while(fd1没有读完)
		{
			read(fd1..)
			write(fd2..)
		}
		close(fd1)
		close(fd2)

今日总结:
1.要理解”裸奔层次“与"linux系统层次"区别
计算机科学中的任何问题,都可以通过加上一层逻辑来解决
2.linux文件操作的大致流程

struct inode 
struct file 
	以及这两者之间的关系? 
	
文件描述符是什么东西? 
	文件描述符是linux系统提供给应用的接口,为了唯一标识一个 
	已经打开的文件的,是一个>=0 的整数
	在linux中,文件描述符是用来描述或标识一个已经打开的
	文件。
系统IO是什么? 
	linux系统提供给应用用来input/output(输入/输出)操作的
	函数接口,如:open/read/write/lseek/close...

3.至于open/read/write。。。。等等这些API函数的具体的用法,以及参数
man

1.获取文件属性
stat, fstat, lstat函数

头文件
	#include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
函数功能 
	用来获取path or fd 指定的那个文件的属性信息
函数原型
   int stat(const char *pathname, struct stat *statbuf);
   int fstat(int fd, struct stat *statbuf);
   int lstat(const char *pathname, struct stat *statbuf);
函数参数
	const char *pathname		//要获取属性的文件名
	struct stat *statbuf		//指向的结构体用来保存文件的属性信息
	int fd						//要获取属性的文件描述符,先open它
	
	
函数返回值 
	成功:返回0 
	失败:返回-1,同时errno被设置

注意: 
	lstat功能与stat相同,只不过,当path是一个符号链接时,
	lstat获取的是符号链接文件本身的属性信息,而stat是获取 
	的符号链接文件指向的那个文件的属性信息的。



	
	
	在linux下面,有一个结构体是用来保存一个文件的状态或属性信息的,
		struct stat:
		
		struct stat {
			dev_t     st_dev;         /* 用来表示容纳该文件的那个设备的设备号 */
													
			ino_t     st_ino;         /* 该文件的inode的编号 */
			mode_t    st_mode;        /* 文件权限位(包含文件的类型) */
						解析 st_mode包含文件的类型与权限,用位域(bit),可以用下列宏来解析:
						struct stat st 
						st.st_mode
						S_ISREG(st.st_mode) 为真则表示该文件是  普通文件     -
						S_ISDIR(st.st_mode) 为真则表示该文件是  目录         d 
						S_ISCHR(st.st_mode)	为真则表示该文件是  字符设备     c 
						S_ISBLK(st.st_mode)	为真则表示该文件是  块设备       b
						S_ISFIFO(st.st_mode)为真则表示该文件是  管道文件     p 
						S_ISLNK(st.st_mode)	为真则表示该文件是  符号链接文件 l 
						S_ISSOCK(st.st_mode)为真则表示该文件是 套接字文件 	 s 
									
						st_mode还包含了文件的权限位,可以是如下代码来解析 
						f(st.st_mode & S_IRUSR)  //user有read的权限
						else  //没有read的权限 
						...
						S_IWUSR, S_IXUSR
						S_IRGRP,  S_IWGRP, S_IXGRP 
						S_IROTH,  S_IWOTH, S_IXOTH 							  
					   
			nlink_t   st_nlink;       /*该文件的硬连接数*/
			uid_t     st_uid;         /* 文件所有者的用户ID */
			gid_t     st_gid;         /* 文件的所有者的组ID */
			dev_t     st_rdev;        /* 设备号(假如该文件是一个设备) */
			off_t     st_size;        /* 文件内容的大小 */
						对于普通文件,就是指本身大小,文件内容的大小 
						对于符号链接(软链接文件),文件内容是什么?
							指向的那个文件的文件名
						目录文件的内容是什么呢? 
							目录项数组

					   
			blksize_t st_blksize;     /* 块大小(与具体的硬件设备有关) */
			blkcnt_t  st_blocks;      /* 该文件占多少块(512字节为一块) */
			

			struct timespec st_atim;  /* 最后访问“文件内容”的时间*/
			struct timespec st_mtim;  /* 最后修改“文件内容”的时间 */
			struct timespec st_ctim;  /* 最后修改“文件属性”的时间 */ inode
			}

2.问题
在linux下面,目录,可以通过open打开它,“文件描述符”
但是我们在用 read(fd,) 去读一个目录的内容时,会报错。

=》在linux下不能用read去读一个目录。
	
目录文件里面的内容到底是什么呢? 

3.目录与普通文件的区别
在linux下面,任何一个文件要存在,必须有一个inode
普通文件需要一个inode
目录文件需要一个inode
设备文件需要一个inode

普通文件的内容,“文件内容”
但 目录文件的内容,是什么吗? 
	目录下面可以包含文件,目录下面又可以包含目录。
		目录文件的内容 是  目录项的数组
	
	创建一个空目录时,系统会为它预留一个“目录项的数组”
	当在这个目录下创建一个文件或目录时,就会填充一个目录项
	到“目录项数组中去”。
	
	目录项到底是什么呢? 
		struct dirent 
	我们要想知道一个目录下面的层次关系,必须要去读目录项!!!		

4.目录操作

(1)打开一个目录
	opendir 
	在linux中用结构体DIR来描述符一个已经打开的目录,
	至于这个结构体中的具体的内容,“透明”,DIR这个结构体到底
	包含了什么成员变量,我们无需关心。
	
	DIR * 表示一个成功打开的目录。
	
	头文件 
		#include <sys/types.h>
		#include <dirent.h>
	函数功能
		打开指定的目录
	函数原型
		DIR *opendir(const char *name);
	函数参数 
		const char *name		//要打开的目录的名字
	函数返回值 
		成功: 返回一个DIR的指针
		失败: 返回NULL,同时errno被设置
	
	函数功能
		打开指定的目录 
	函数原型 
		DIR *fdopendir(int fd);
	函数参数 
		int fd		//要打开的那个目录的文件描述符,
						意思就是说,在调用fdopendir之前,先要open一下。
	函数返回值 
		成功: 返回一个DIR的指针
		失败: 返回NULL,同时errno被设置
(2)读一个目录中的目录项 
	readdir  
		readdir用来从dirp指向的目录中,返回下一个目录项(struct dirent)的指针。
		一个目录里面有多个目录项(一个文件或子目录就会对应一个目录项),每调用一次
		readdir就会返回下一个目录项的指针。知道返回NULL。
		
	在linux中,目录项结构体如下: 
	
		struct dirent {
           ino_t          d_ino;       /* Inode的编号 */
           off_t          d_off;       /* 目录项的偏移*/
           unsigned short d_reclen;    /* 该结构体的长度 */
           unsigned char  d_type;      /* 该目录项指向的那个文件的类型 */
           char           d_name[256]; /* 该目录项指向的文件的名字 */
       };

	该结构体中的成员变量,只有d_ino,d_name是所有linux系统都支持的!!!!
	如果你想让你的代码具有可移植性,该结构体代码中,只能用d_ino,d_name这
	两个成员变量。
	
	头文件 
		#include <dirent.h>
	函数功能
		读取一个目录的目录项
	函数原型 
		struct dirent *readdir(DIR *dirp);
	函数参数 
		DIR *dirp		//表示想要从哪个目录中读取下一个目录项
	函数返回值 
		成功: 返回下一个目录项的指针,如果读完了,则返回NULL。			
		失败: 返回NULL,同时errno被设置

(3)关闭一个目录
	closedir
	头文件 
		#include <sys/types.h>
		#include <dirent.h>
	函数功能
		关闭一个指定的目录
	函数原型
		int closedir(DIR *dirp);
	函数参数 
		DIR *dirp		//指向要关闭的目录的DIR结构体
	函数返回值 
		成功: 返回0 
		失败: 返回-1,同时errno被设置
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值