【嵌入式学习】IO进程线程-Day3-IO基础

作业

  1. 使用标准io完成两个文件的拷贝
#include <stdio.h>

int main(int argc, const char *argv[]){
	if(argc!=3){
		puts("输入有误!");
		return -1;
	}
	FILE* fp=NULL;
	FILE*fp_cp=NULL;
	if((fp=fopen(argv[1],"r"))==NULL){
		perror("打开1文件出错:");
		return -1;
	}

	if((fp_cp=fopen(argv[2],"w"))==NULL){
		perror("打开2文件出错:");
		return -1;
	}

	char buff[1024];
	while(fgets(buff,sizeof(buff),fp)!=NULL){
		fputs(buff,fp_cp);
	}

	puts("拷贝成功!");
	fclose(fp);
	fclose(fp_cp);
	return 0;
}
  1. 使用文件IO完成两个文件的拷贝
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
    if(argc!=3)
    {
        puts("输入有误");
        return -1;
    }
    int fd=-1,fd_cp=-1;
    fd=open(argv[1],O_RDONLY);
    if(fd==-1)
    {
        perror("打开文件1失败:");
        return -1;
    }
    fd_cp=open(argv[2],O_WRONLY | O_CREAT | O_TRUNC,0664);
    if(fd_cp==-1)
    {
        perror("打开文件2失败:");
        return -1;
    }
    
    // ssize_t write(int fd, const void *buf, size_t count);
    // 功能:将buf指向的地址中count个字节,写入到fd指向的文件中
    // 参数1:文件描述符
    // 参数2:容器起始地址,void*类型,表明可以写入任何类型的数据
    // 参数3:要写入数据的个数
    // 返回值:成功返回写入的字符个数,失败返回-1并置位错误码

    // ssize_t read(int fd, void *buf, size_t count);
    // 功 能:从fd文件中,将count个字节读取到buf对应的容器中
    // 参数1:文件描述符
    // 参数2:容器起始地址,void*类型,表明可以读取任何类型的数据
    // 参数3:要读取数据的个数
    // 返回值:成功返回读取字节的个数,失败返回-1并置位错误码

    //循环写入
    char buff[4096];
    int res;
    while((res=read(fd,buff,sizeof(buff)))>0)
    {
        write(fd_cp,buff,res);
    }
    //关闭文件
    close(fd);
    close(fd_cp);
}
  1. 将stat函数实现一遍
#include<myhead.h>
 
int main(int argc, const char *argv[])
{
	//定义文件属性类型的数据
	struct stat sb;      //用于存储获得的文件属性
 
	//调用函数的到文件属性
	stat(argv[1], &sb);
 
	switch(sb.st_mode&S_IFMT)
	{
	case S_IFSOCK:
		{
			printf("这是套接字文件\t");
		}
		break;
	case S_IFLNK:
		{
			printf("这是链接文件\t");
		}
		break;
	case S_IFREG:
		{
			printf("这是普通文件\t");
		}
		break;
	case S_IFBLK:
		{
			printf("这是块设备文件\t");
		}
		break;
	case S_IFDIR:
		{
			printf("这是目录文件\t");
		}
		break;
	case S_IFCHR:
		{
			printf("这是字符设备文件\t");
		}
		break;
	case S_IFIFO:
		{
			printf("这是管道文件\t");
		}
		break;
 
	}
 
	printf("%#o\t%ld\t%ld\n", sb.st_mode&0777, sb.st_size, sb.st_ino);
 
	return 0;
}
 
  1. 将目录操作实现一遍
#include<myhead.h>
 
int main(int argc, const char *argv[])
{
	//判断外部传参个数
	if(argc != 2)
	{
		printf("input error\n");
		printf("usage:./a.out name\n");
		return -1;
	}
 
	//定义目录指针
	DIR *dp = NULL;
	//打开目录
	if((dp = opendir(argv[1])) == NULL)
	{
		perror("opendir error");
		return -1;
	}
 
	//读取目录中的文件或目录信息
	struct dirent *sdp = NULL;
	while((sdp = readdir(dp)) != NULL)
	{
		//输出当前文件或目录的信息
		printf("inode:%10ld, size:%10d, %10s, ",\
				sdp->d_ino, sdp->d_reclen, sdp->d_name);
 
		//输出类型
		switch(sdp->d_type)
		{
		case DT_BLK:
			{
				printf("b\n");
			}
			break;
	case DT_CHR:
			{
				printf("c\n");
			}
			break;
 
	case DT_DIR:
			{
				printf("d\n");
			}
			break;
 
	case DT_FIFO:
			{
				printf("p\n");
			}
			break;
	case DT_LNK:
			{
				printf("l\n");
			}
			break;
	case DT_REG:
			{
				printf("-\n");
			}
			break;
	case DT_SOCK:
			{
				printf("s\n");
			}
			break;
		}
	}
 
 
 
 
 
	//关闭目录
	closedir(dp);
 
	return 0;
}
 

笔记

三、文件IO

  1. 文件IO是基于系统调用
  2. 程序每进行一次系统调用,就会从用户空间向内核空间进行一次切换,执行效率较慢
  3. 目的:由于后期进程间通信,如管道、套接字通信,都使用的是文件IO,所以引入文件IO操作的概念

3.1 文件描述符

  1. 件描述符本质上是一个非负整数,每个打开的文件,都会对应一个整数用于系统调用
  2. 每个程序打开文件的个数是有上限的,默认是1024个,可以通过ulimit -a进行查看
  3. 文件描述符使用原则:最小未分配原则
  4. 当使用open函数打开一个文件时,系统会给该文件分配一个文件描述符作为句柄
  5. 当一个程序运行时,默认会打开三个文件描述符,分别对应标准输入、标准输出、标准出错
#include<stdio.h>
 
int main(int argc, const char *argv[])
{
	printf("%d\n", stdin->_fileno);     //0
	printf("%d\n", stdout->_fileno);    //1
	printf("%d\n", stderr->_fileno);    //2
 
	return 0;
}
 

3.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);
        功能:打开或可能创建一个文件
       参数1:文件路径,是一个字符串表示要打开的文件
       参数2:打开标识
               三个必须选一个:O_RDONLY(只读)O_WRONLY(只写)O_RDWR(读写)
               后面的可以以位或的方式连接,表示拥有该属性
               O_CREAT:表示创建一个文件, 当第二个参数中有O_CREAT时,第三个参数必须要加上
               O_APPEND:追加方式打开文件
               O_TRUNC:清空文件内容
               O_NONBLOCK:以非阻塞形式打开文件
               O_EXCL:确保本次操作一定创建文件,如果文件已经存在,则open函数会报错,错误码为EEXIST
               eg:    "w": O_WRONLY | O_CREAT | O_TRUNC
                      "r":O_RDONLY
                      "a":O_WRONLY | O_APPEND | O_CREAT  
                      "w+":O_RDWR | O_CREAT | O_TRUNC
                      "r+":O_RDWR
                      "a+":O_RDWR | O_CREAT | O_APPEND
        参数3:如果第二个参数中有O_CREAT,该参数必须设置,表示文件的权限,如果不设置,该文件的权限是一个随机权限
                文件最终的权限是由给定的权限 & ~umask 得到
                终端的umask的值,可以通过指令 umask查看,可以通过指令 umask -n更改当前终端的umask的值
                一般创建普通文件最大权限为:664
                目录文件权限最大权限为:775
        
        返回值:成功返回一个新的文件描述符,失败返回-1并置位错误码。

3.3 close函数

       #include <unistd.h>
 
       int close(int fd);
功能:关闭指定的文件描述符
参数:要关闭的文件描述符,关闭后,该文件描述符可以分配给其他文件使用
返回值:成功返回0,失败返回-1并置位错误码
#include<myhead.h>
 
int main(int argc, const char *argv[])
{
	int fd = -1;      //定义文件描述符变量
 
	fd = open("./test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0664);
	if(fd == -1)
	{
		perror("open error");
		return -1;
	}
 
	printf("fd = %d\n", fd);            //?3
 
	//关闭文件描述符
	close(fd);
	
 
 
 
	return 0;
}
 

3.4 write\read函数

       #include <unistd.h>
 
       ssize_t write(int fd, const void *buf, size_t count);
功能:将buf指向的地址中count个字节,写入到fd指向的文件中
参数1:文件描述符
参数2:容器起始地址,void*类型,表明可以写入任何类型的数据
参数3:要写入数据的个数
返回值:成功返回写入的字符个数,失败返回-1并置位错误码
 
 
       #include <unistd.h>
 
       ssize_t read(int fd, void *buf, size_t count);
功能:从fd文件中,将count个字节读取到buf对应的容器中
参数1:文件描述符
参数2:容器起始地址,void*类型,表明可以读取任何类型的数据
参数3:要读取数据的个数
返回值:成功返回读取字节的个数,失败返回-1并置位错误码
#include<myhead.h>
 
int main(int argc, const char *argv[])
{
	//定义文件描述符变量
	int fd = -1;
 
	//以只写的形式打开文件
	if((fd = open("./test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0664)) ==-1)
	{
		perror("open error");
		return -1;
	}
	printf("fd = %d\n", fd);             //3
 
	char buf[128] = "hello world ni hao I love China\n";
	//将字符串写入到文件
	int res = write(fd, buf, strlen(buf));
	printf("res = %d\n", res);                //
 
	//关闭文件
	close(fd);
 
	//以只读的形式打开文件
	if((fd = open("./test.txt", O_RDONLY)) ==-1)
	{
		perror("open error");
		return -1;
	}
	printf("fd = %d\n", fd);             //3
 
	char rbuf[128] = "";
	res = read(fd, rbuf, sizeof(rbuf));
	printf("res = %d\n", res);                //?
	write(stdout->_fileno, rbuf, res);
 
	//关闭文件
	close(fd);
 
 
 
 
	return 0;
}
 

练习:使用read、wriet完成两个文件的拷贝

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
	//判断传入的文件个数
	if(argc != 3)
	{
		printf("input file error\n");
		printf("usage:./a.out srcfile dstfile\n");
		return -1;
	}
 
	//定义文件描述符变量
	int srcfd, dstfd;
 
	//以只读的形式打开源文件
	if((srcfd = open(argv[1], O_RDONLY)) ==-1)
	{
		perror("open srcfile error");
		return -1;
	}
	//以只写的形式打开目标文件
	if((dstfd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0664)) ==-1)
	{
		perror("open dstfile error");
		return -1;
	}
	
	//不断得将源文件中的内容读出,并写入的目标文件中
	//直到源文件全部读取结束
	char buf[128] = "";
	while(1)
	{
 
		memset(buf, 0, sizeof(buf));    //将容器清空
 
		int res = read(srcfd, buf, sizeof(buf));   //从源文件中读取数据
 
		write(dstfd, buf, res);     //将数据写入目标文件
 
		//对读取的数据个数进行判断
		if(res == 0)
		{
			break;
		}
	}
	
 
	//关闭两个文件
	close(srcfd);
	close(dstfd);
 
	printf("拷贝成功\n");
 
	return 0;
}
 

3.5 光标移动(lseek)

       #include <sys/types.h>
       #include <unistd.h>
 
       off_t lseek(int fd, off_t offset, int whence);
功能:移动光标位置
参数1:要移动光标的文件描述符
参数2:偏移量
        >0:表示向后偏移
        =0:表示不偏移
        <0:表示向前偏移
参数3:偏移的起始位置
        SEEK_SET:从文件开头偏移
        SEEK_CUR:从文件光标当前位置偏移
        SEEK_END:从文件末尾开始偏移
返回值:成功返回光标当前位置,失败返回(off_t-1并置位错误码
        lseek = fseek + ftell
#include<myhead.h>
 
int main(int argc, const char *argv[])
{
	//定义文件描述符变量
	int fd = -1;
	
	//以只读的形式打开图片文件
	if((fd = open("./bb.bmp", O_RDONLY)) == -1)
	{
		perror("open error");
		return -1;
	}
	
	//读取图像的大小
	//偏移2个自己
	lseek(fd, 2, SEEK_SET);
	//读取数据
	unsigned int size;
	read(fd, &size, sizeof(unsigned int));
	printf("size = %d\n", size);              //?
 
 
	size = lseek(fd, 0, SEEK_END);         //将光标定位在结尾,将光标位置返回
	printf("size = %d\n", size);              //?
	
	//关闭文件
	close(fd);
 
	return 0;
}
 

3.6 关于文件描述符的拷贝问题

1> 使用赋值的情况直接拷贝,多个文件描述符变量,共享同一个文件光标,通过一个文件描述符进行移动,所有的文件描述符的光标都会改变

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
	//以只读的形式打开一个文件
	int fd1 = -1;
	if((fd1 = open("./test.txt", O_RDONLY)) == -1)
	{
		perror("open error");
		return -1;
	}
 
	printf("fd1 = %d\n", fd1);    //3
 
	int fd2 = fd1;             //对文件描述符直接进行拷贝
	
	//使用fd1将光标后移10个字节
	lseek(fd1, 12, SEEK_SET);
 
	//从fd2中读取数据
	char buf[10] = "";
	read(fd2, buf, sizeof(buf)-1);
	
	printf("buf = %s\n", buf);     //如果输出的是hello开头的,说明fd1和fd2不共享文件光标
					//如果输出的是ni hao开头的,说明fd1和fd2共享同一个文件光标
 
 
 
	return 0;
}
 

2> 使用dup函数,完成文件描述符的拷贝

       #include <unistd.h>
 
       int dup(int oldfd);
功能:通过旧的文件描述符,拷贝出一个新的文件描述符,新文件描述符遵循最小未分配原则
参数:旧文件描述符
返回值:新文件描述符,失败返回-1并置位错误码
执行后,新旧两个文件描述符都指向同一个打开的文件,共享文件光标位置
#include<myhead.h>
 
int main(int argc, const char *argv[])
{
	//以只读的形式打开一个文件
	int fd1 = -1;
	if((fd1 = open("./test.txt", O_RDONLY)) == -1)
	{
		perror("open error");
		return -1;
	}
 
	printf("fd1 = %d\n", fd1);    //3
 
 
	int fd2 = dup(fd1);      //拷贝旧文件描述符,产生一个新文件描述符
	printf("fd2 = %d\n", fd2);      //4
 
	//使用fd1将光标后移10个字节
	lseek(3, 12, SEEK_SET);
 
	//从fd2中读取数据
	char buf[10] = "";
	read(4, buf, sizeof(buf)-1);
	
	printf("buf = %s\n", buf);     //如果输出的是hello开头的,说明fd1和fd2不共享文件光标
								//如果输出的是ni hao开头的,说明fd1和fd2共享同一个文件光标
 
 
 
	return 0;
}
 

3> 使用dup2函数完成两个文件描述符的拷贝

        int dup2(int oldfd, int newfd);
功能:通过拷贝旧的文件描述符到新的文件描述符中
参数1:旧文件描述符
参数2:新文件描述符,如果newfd已经指向了某个已经打开的文件,则在进行拷贝之前,先将其关闭
返回值:成功返回新的文件描述符,失败返回-1并置位错误码
注意:newfd不是使用最小为分配原则,因为newfd在调用之前可能已经指向某个文件,调用后,无论是newfd函数oldfd都指向oldfd指向的文件
        通过该方式复制的文件描述符,依然共享同一个文件的光标
#include<myhead.h>
 
int main(int argc, const char *argv[])
{
	//以只读的形式打开一个文件
	int fd1 = -1;
	if((fd1 = open("./test.txt", O_RDONLY)) == -1)
	{
		perror("open error");
		return -1;
	}
 
 
	//定义一个文件描述符变量,打开另一个文件
	int fd2 = -1;
	if((fd2 = open("./bb.bmp", O_RDONLY)) == -1)
	{
		perror("open error");
		return -1;
	}
 
	printf("fd1 = %d\n", fd1);    //3
	printf("fd2 = %d\n", fd2);    //4
 
	//执行文件描述符拷贝工作
	dup2(fd1, fd2);
 
 
 
 
	//使用fd1将光标后移10个字节
	lseek(3, 12, SEEK_SET);
 
	//从fd2中读取数据
	char buf[10] = "";
	read(4, buf, sizeof(buf)-1);
	
	printf("buf = %s\n", buf);     //如果输出的是hello开头的,说明fd1和fd2不共享文件光标
									//如果输出的是ni hao开头的,说明fd1和fd2共享同一个文件光标
 
 
 
	return 0;
}
 

4> 多次使用open函数完成对同一个文件的打开时,不同的文件描述符使用的是独立的光标

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
	//以只读的形式打开一个文件
	int fd1 = -1;
	if((fd1 = open("./test.txt", O_RDONLY)) == -1)
	{
		perror("open error");
		return -1;
	}
 
 
	//定义一个文件描述符变量,打开另一个文件
	int fd2 = -1;
	if((fd2 = open("./test.txt", O_RDONLY)) == -1)
	{
		perror("open error");
		return -1;
	}
 
	printf("fd1 = %d\n", fd1);    //3
	printf("fd2 = %d\n", fd2);    //4
 
	//使用fd1将光标后移10个字节
	lseek(fd1, 12, SEEK_SET);
 
	//从fd2中读取数据
	char buf[10] = "";
	read(fd2, buf, sizeof(buf)-1);
	
	printf("buf = %s\n", buf);     //如果输出的是hello开头的,说明fd1和fd2不共享文件光标
								//如果输出的是ni hao开头的,说明fd1和fd2共享同一个文件光标
 
 
 
	return 0;
}
 

四、文件属性获取(stat)

       #include <sys/types.h>
       #include <sys/stat.h>
       #include <unistd.h>
 
       int stat(const char *pathname, struct stat *statbuf);
 
功能:将给定的文件的相关属性,通过statbuf返回出来
参数1:要获取属性的文件路径是一个字符串
参数2:文件属性结构体指针,需要传递一个文件属性类型的结构体变量
        文件属性结构体类型:
            struct stat {
               dev_t     st_dev;         /* ID of device containing file */   设备id号
               ino_t     st_ino;         /* Inode number */       文件的inode号
               mode_t    st_mode;        /* File type and mode */   文件的类型和权限
               nlink_t   st_nlink;       /* Number of hard links */  硬链接数
               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;     /* Block size for filesystem I/O */  块的大小
               blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */   块的个数               
           };
           关于文件类型和文件权限的介绍
           使用 st_mode &  S_IFMT后的到文件的类型
           S_IFSOCK   0140000   socket           套接字文件类型
           S_IFLNK    0120000   symbolic link    链接文件类型 
           S_IFREG    0100000   regular file     普通文件类型
           S_IFBLK    0060000   block device     块设备文件类型
           S_IFDIR    0040000   directory        目录文件类型 
           S_IFCHR    0020000   character device 字符设备文件类型
           S_IFIFO    0010000   FIFO              管道文件类型
           
           使用 st_mode & 0777后得到文件的权限
返回值:成功返回0,失败返回-1并置位错误码                        
 
#include<myhead.h>
 
int main(int argc, const char *argv[])
{
	//定义文件属性类型的数据
	struct stat sb;      //用于存储获得的文件属性
 
	//调用函数的到文件属性
	stat(argv[1], &sb);
 
	switch(sb.st_mode&S_IFMT)
	{
	case S_IFSOCK:
		{
			printf("这是套接字文件\t");
		}
		break;
	case S_IFLNK:
		{
			printf("这是链接文件\t");
		}
		break;
	case S_IFREG:
		{
			printf("这是普通文件\t");
		}
		break;
	case S_IFBLK:
		{
			printf("这是块设备文件\t");
		}
		break;
	case S_IFDIR:
		{
			printf("这是目录文件\t");
		}
		break;
	case S_IFCHR:
		{
			printf("这是字符设备文件\t");
		}
		break;
	case S_IFIFO:
		{
			printf("这是管道文件\t");
		}
		break;
 
	}
 
	printf("%#o\t%ld\t%ld\n", sb.st_mode&0777, sb.st_size, sb.st_ino);
 
	return 0;
}
 

五、目录相关操作

5.1 opendir函数

       #include <sys/types.h>
       #include <dirent.h>
 
       DIR *opendir(const char *name);
功能:打开一个指定的目录,并返回该目录的目录指针
参数:要打开的目录,是一个字符串
返回值:成功返回目录指针,失败返回NULL并置位错误码

5.2 closedir函数

       #include <sys/types.h>
 
       #include <dirent.h>
 
       int closedir(DIR *dirp);
功能:关闭一个已经打开的目录指针
参数:目录指针
返回值:成功返回0,失败返回-1并置位错误码

5.3 readdir函数

       #include <dirent.h>
 
       struct dirent *readdir(DIR *dirp);
功能:读取指定目录中的下一个文件或目录的信息
参数:目录指针
返回值:成功返回当前文件或目录的信息,失败返回NULL并置位错误码
struct dirent {
               ino_t          d_ino;       /* Inode number */         //当前目录或文件的inode号   8字节
               off_t          d_off;       /* Not an offset; see below */   光标偏移量            8字节
               unsigned short d_reclen;    /* Length of this record */       当前记录的大小       2字节
               unsigned char  d_type;      /* Type of file; not supported     文件类型            1字节
                                              by all filesystem types */
               char           d_name[256]; /* Null-terminated filename */     文件名
           };
           
            关于文件或目录类型
              DT_BLK      This is a block device.
 
              DT_CHR      This is a character device.
 
              DT_DIR      This is a directory.
 
              DT_FIFO     This is a named pipe (FIFO).
 
              DT_LNK      This is a symbolic link.
 
              DT_REG      This is a regular file.
 
              DT_SOCK     This is a UNIX domain socket.
 
              DT_UNKNOWN  The file type could not be determined.
 
 
 
#include<myhead.h>
 
int main(int argc, const char *argv[])
{
	//判断外部传参个数
	if(argc != 2)
	{
		printf("input error\n");
		printf("usage:./a.out name\n");
		return -1;
	}
 
	//定义目录指针
	DIR *dp = NULL;
	//打开目录
	if((dp = opendir(argv[1])) == NULL)
	{
		perror("opendir error");
		return -1;
	}
 
	//读取目录中的文件或目录信息
	struct dirent *sdp = NULL;
	while((sdp = readdir(dp)) != NULL)
	{
		//输出当前文件或目录的信息
		printf("inode:%10ld, size:%10d, %10s, ",\
				sdp->d_ino, sdp->d_reclen, sdp->d_name);
 
		//输出类型
		switch(sdp->d_type)
		{
		case DT_BLK:
			{
				printf("b\n");
			}
			break;
	case DT_CHR:
			{
				printf("c\n");
			}
			break;
 
	case DT_DIR:
			{
				printf("d\n");
			}
			break;
 
	case DT_FIFO:
			{
				printf("p\n");
			}
			break;
	case DT_LNK:
			{
				printf("l\n");
			}
			break;
	case DT_REG:
			{
				printf("-\n");
			}
			break;
	case DT_SOCK:
			{
				printf("s\n");
			}
			break;
		}
	}
 
 
 
 
 
	//关闭目录
	closedir(dp);
 
	return 0;
}
 

img

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值