linux系统调用IO

1. 系统调用概念:

库函数-> 内核函数[系统调用]  -> 驱动[磁盘、显示器]

 int main(){
 int a=10;            //  在用户空间执行
 printf("%s\n","hello kernel");  // 系统调用进入内核,在内核执行
 int b=10;    // 回到用户空间
 return 0
  } 

每个进程打开文件都要打开这3个设备:
  0:标准输入对应文件描述符0    stdin
  1:标准输出                  stdout
  2:标准出错                  stderror
  返回值:
成功 返回文件描述符
失败 -1

 2.  基本系统调用IO函数

2.1.open函数

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

int main(){

  /* 
    使用 man 2 open:可以自己去看文档,一定要学会看文档,系统调用在第二卷
    库函数在第3卷
    参数2:
    O_RDONLY:只读方式打开文件。
    O_WRONLY:可写方式打开文件。
    O_RDWR:读写方式打开文件。
    O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三的参数为其设置权限(读写执行权限)。
    O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。
    O_TRUNC:如文件已经存在,那么打开文件时先删除文件中原有数据。
    O_APPEND:以添加方式打开文件,所以对文件的写操作都在文件的末尾进行。//write 文件开始位置开始写
   参数3: 
    0666 & umask(0002)[文件的实际权限 0666 & umask取反] 就是 0664 创建的文件权限
    110 110  110  0666
    111 111  101  000 000 010  0002
    110 110  100  664
  参数也可以使用枚举man 2 open
  返回值:  
   -1:表示失败, 成功:返回文件描述符
如何open函数执行失败如何查看具体错误:
  方式1:使用:perror("open failed");    里面是自定义错误,具体错误errno头文件中,都会打出来
 方法2: 使用 printf("%s\n",strerror(errno)); 
     
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
  函数creat:creat("test.txt", 0666); // open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);

   
    */
	int fd;
	fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC , S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH,0666);
	if(-1 == fd)
	{
         //printf("%s\n",strerror(errno)); 
		perror("open failed");
		 //  errno.h  linux内部对错误状态码封装到这个头文件中,解析错误状态码返回字符串:strerror
         //  man errno  
	}
	close(fd);//1024

  return 0;
}

2.2. read、write、lseek 函数使用

linux不区分文本流和二进制流
文本流:
  字符以ASCII 方式存储
  文本流写入 \n 会被转化为 回车和换行对应ASCII 0DH和OAH
  输出的时候,0DH和0AH会被转化为\n
二进制流:
  以二进制0001存储,\n会被原本存储

======================== 
 标准IO:支持标准c库的系统都可以
   系统读取文件,首先读取到缓冲区中,然后进程去缓冲区读取 
   写

 文件IO:无缓冲区, 实现posix规范的系统都可以

  缓冲分类:(标准IO)
全缓冲: 写满缓冲区才写入磁盘,linux 4096,fopen也是使用输入文件中
   刷新机制:缓冲区写满、调用fflush
行缓冲: 遇到\n写入磁盘,标准输入、输出使用,默认1024个字节
   刷新机制:\n、读满1024个字节、fflush
无缓冲区: 标准出错使用
  为什么需要缓冲:减少系统调用次数,提高效率

缓冲区是通过malloc申请的空间
申请的实际是发生在I/O操作的时候

比如:
  printf("hello world")
  while(1){} 
 //行缓冲,不会输出hello world
如何输出:
1.添加\n
2.进程结束
3.添加 fflush(stdout);

函数api:

#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

flag: 必选

O_RDONLY, O_WRONLY, or O_RDWR. 读 写 读写

可选:

O_APPEND 追加

O_CREAT 创建

mode 权限位 最终( mode & ~umask)

O_NONBLOCK 非阻塞

返回值: 放回最小可用文件描述符

close : 关闭文件描述符

- 1 : 失败 0 成功

#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

flag: 必选

O_RDONLY, O_WRONLY, or O_RDWR. 读 写 读写

可选:

O_APPEND 追加

O_CREAT 创建

mode 权限位 最终( mode & ~umask)

O_NONBLOCK 非阻塞

返回值: 放回最小可用文件描述符

close : 关闭文件描述符

- 1 : 失败 0 成功

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>


int main000(int argc, const char *argv[])
{
	int fd;
	fd = open("test.txt", O_RDWR | O_CREAT| O_APPEND , S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
	if(-1 == fd)
	{
		perror("open failed1");
		return -1;
	}

	char *writebuf = "\nwrite context 0";
	printf("%ld\n", strlen(writebuf));
	long int writeCount = -1;
	writeCount = write(fd, writebuf, strlen(writebuf));
	if (-1 == writeCount) {
		perror("write failed");
		return -1;
	}

	// 写入内容以后,文件指针偏移到末尾去了,后面无法读取内容,把指针偏移到头部来
	// -1 失败   成功:返回较起始位置偏移量
	// 1. lseek偏移
	lseek(fd,SEEK_SET,0);

	char buf[1024] = { 0 };
	long int count = 0;
	long unsigned int readsize = 20;
	// 如果count == 0  那么读到末尾了
	while ((count = read(fd, buf, readsize)) > 0) {
		if (count == -1) {
			perror("read failed");
			return -1;
		}
		printf("----%s\n", buf);
		memset(buf, 0, sizeof(buf));
	}

	lseek(fd,0,SEEK_SET);
	// 2.通过lseek获取文件大小
	int length=lseek(fd,0,SEEK_END);
	printf("filesize1---%d\n",length);
	// 3. lseek制造文件空洞
    // 文件空洞[偏移的1000个字节叫做空洞],必须在最后添加\0引起IO操做
    // 空洞文件:造空洞,下载的时候多开几个进程去下载
	length=lseek(fd,1000,SEEK_CUR);
	printf("filesize2---%d\n",length);
// 扩张空洞文件,必须要写一次
	write(fd,"q",1);
	close(fd);

//	fd = open("test.txt", O_RDWR | O_CREAT| O_APPEND , S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
//	if(-1 == fd)
//	{
//			perror("open failed2");
//			return -1;
//	}


	close(fd);//1024

}

问题1:  使用库函数fgetc 和 系统调用read函数哪个拷贝内容快,每次读写1个字节

使用fgetc快,如果使用fgetc 那么首先用户态写入内存态,写满4096个字节,才一次写入磁盘但是如果使用 read, 每次读一个字节写入磁盘一个字节,速度慢,这样的话

 问题2:  文件描述符内核理解

 pcb进程控制块: 本质结构体
  成员: 文件描述符表
  文件描述符表,一个进程可以打开1024个文件
  0  1  2:标准输入|输出|出错 文件描述符,每一个int对应一个结构体,每次打开一个文件,分配一个int

 3. 阻塞概念、fcntl函数

阻塞:只有设备文件[dev/tty,终端输入文件]和网络存在阻塞问题,普通文件不存在阻塞问题

3.1.等待解决阻塞

STDIN_FILENO:接收键盘的输入

STDOUT_FILENO:向屏幕输出

https://blog.csdn.net/sinat_25457161/article/details/48548231

/**
 *  等待解决阻塞
 * /dev/tty 下文件默认是阻塞
 */
int main002(int argc, const char *argv[]){
  char buf[10];
  int n;
  //  /dev/tty 下文件默认是阻塞,程序停留在这里
  n= read(STDIN_FILENO,buf,10);
  if(n<0){
	  perror("read STDIN_FILEND");
	  exit(1);
  }
  write(STDIN_FILENO,buf,n);
  return 0;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>


int main(int argc, const char *argv[]){
  int fd = open("/dev/tty",O_RDWR);

  char buf[10];
  int n =0 ;
  while(1){

//  /dev/tty 下文件默认是阻塞,程序停留在这里
  n= read(fd,buf,10);
  if(n<0){
      perror("read STDIN_FILEND");
      exit(1);
  }
 // write(STDIN_FILENO,buf,n);
  printf("---%s\n", buf);
  sleep(1);
  }

  close(fd);
  
  return 0;
}

3.2.轮寻解决阻塞、fcntl函数

/**
 * fcntl函数: 文件属性控制函数,可以修改文件属性,比如文件可读、可写、文件阻塞
 * 获取文件状态:  F_GETFL
 * 设置文件状态:  F_SETFL
 * fcntl (int __fd, int __cmd, ...)
 * 轮寻解决阻塞
 * 阻塞和非阻塞:
 * 产生阻塞场景:读设备文件、读网络文件(读常规文件无阻塞概念)
 * /dev/tty: 终端设备文件
 */
#define  MSG_TRY "try agin\n"

int main003(int argc, const char *argv[]){

   char buf[10];
   int flags,n;
   flags= fcntl(STDIN_FILENO,F_GETFL); // 获取stdin属性的信息

   if(flags == -1){
	   perror("fcntl error");
	   exit(1);
   }
   // 这里是int 32个bit 位的位运算
   flags = flags|O_NONBLOCK;
   // 把/dev/tty从阻塞状态设置成非阻塞状态
   int ret= fcntl(STDIN_FILENO,F_SETFL,flags);
   if(ret == -1){
	   perror("fcntl error");
	   exit(1);
   }

  tryagain:
  /**  
   *  >0 实际督导字节数
   *   0 读到末尾(对端已经关闭)
   *  -1: 读取失败  应该进一步判断errno的值
       errno = EAGAIN or EWOULDBLOCK: 设置了非阻塞方式读 
       errno = EINTR  慢速系统调用被 中断
       errno =" 其他情况" 异常

   *  /dev/tty 读取非阻塞状态文件内容为空,也返回-1,但是此时errno==EAGAIN,此时轮读取内容
   *  读取文件失败的时候也返回-1 , 
   *  如果  errno==EAGAIN   表示读取的是 非阻塞文件返回 -1 , 不是读取读取文件失败
   */
  	  n = read(STDIN_FILENO,buf,10);
  	  if(n<0){
  		  if(errno != EAGAIN){
             perror("read /dev/tty");
             exit(1);
  		  }
  		  sleep(3);
  		  write(STDIN_FILENO,MSG_TRY,strlen(MSG_TRY));
  		  goto tryagain;
  	  }
     	 write(STDIN_FILENO,buf,n);
     	 return 0;
}

4. stat函数、 dup2 文件流重定向

4.1. stat函数:保存文件属性api类

int main004(int argc, const char *argv[])
{
	 struct stat st;
	 int ret=stat("test.txt",&st);
	 if(ret == -1){
		 perror("stat error");
		 exit(-1);
	 }
     printf("文件大小:%ld\n",st.st_size);

     // 判断是否为文件
     if(S_ISREG(st.st_mode)){
    	 printf("it is regular file\n");
     }else if(S_ISDIR(st.st_mode)){
    	 printf("it is directory file\n");
     }else if(S_ISFIFO(st.st_mode)){

     }
     // stat函数无法识别软连接,需要使用
     // 使用宏函数判断
     struct stat lst;
     lstat("test.soft",&lst);
//	if (S_ISREG(lst.st_mode)) {
//		printf("it is regular file");
//	} else if (S_ISDIR(lst.st_mode)) {
//		printf("it is directory file");
//	} else if (S_ISLNK(lst.st_mode)) {
//		printf("it is soft file");
//	}

      // 使用位运算 判断文件类型,具体如何进行位运算,看下图,文件的16位掩码
	 switch (lst.st_mode & S_IFMT) {
	case S_IFBLK:
		printf("block device\n");
		break;
	case S_IFCHR:
		printf("character device\n");
		break;
	case S_IFDIR:
		printf("directory\n");
		break;
	case S_IFIFO:
		printf("FIFO/pipe\n");
		break;
	case S_IFLNK:
		printf("symlink\n");
		break;
	case S_IFREG:
		printf("regular file\n");
		break;
	case S_IFSOCK:
		printf("socket\n");
		break;
	default:
		printf("unknown?\n");
		break;
	}

	 // 如何查看stat 的demo
	 // man 2 stat
	 // 输入G

	 return 0;
}

就是Linux 的 stat命令

4.1.1.文件理论:

inode 本质就是一个结构体, 存储了文件属性信息, 权限、 类型、大小、 时间 、 用户

     每一个文件都有(dentry[文件向导])  用于保存文件名和文件inode节点号
     里面保存 文件名称和inode号
     可以通过inode号去寻找 inode结构体,里面保存文件的基本信息和文件的位置

通过文件位置找到文件磁盘位置,
  文件硬链接: 2个文件有不同的文件名称,相同的inode 号保存在 dentry中
  删除文件:把dentry和inode删除,文件还在磁盘上,重新拷贝文件就是覆盖
  数据恢复:恢复dentry和inode即可寻找对应关系

文件类型16位:
  前9位文件权限
  3位特殊权限
  4位文件类型, 如下图:

4.2. 重定向:

int main(int argc, const char *argv[])
{
//	int fd;
//	fd = open("test.temp", O_RDWR | O_CREAT| O_APPEND , S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
//	if(-1 == fd)
//	{
//		perror("open failed1");
//		return -1;
//	}
//	char* ch="abc";
//	write(fd,ch,strlen(ch));
//	close(fd);
//	//remove("test.temp");
//	char ch1[1024];
//	scanf("%s",ch1);
//	unlink("test.temp");

	 int fd1 = open("a.txt", O_RDWR| O_TRUNC );
	 int fd2 = open("b.txt", O_RDWR| O_TRUNC );
     int fdret = dup2(fd1,fd2);   // fd2指向fd1,那么 fd2输出内容也写入fd1
     // 返回文件描述符 fd2
      printf("fd1--%d--fd2--%d---fdret---%d\n",fd1,fd2,fdret);
      write(fdret,"fdret",5);
      int ret=write(fd2,"fd2",3);
      printf("ret---%d",ret);    //写入到a.txt

     dup2(fd1,STDOUT_FILENO);  // 重定向STDOUT_FILENO,输入内容写入到fd1中的a.txt
     printf("---------hello------");
	 return 0;
}

5. 目录

5.1. 使用ls -l 查看目录权限理解,目录也是一个文件,如果对应权限被改了,不能执行对应操作

5.2. opendir、readdir函数

int main006(int argc, const char *argv[])
{
	 DIR * dir= opendir("..");
	 if(dir==NULL){
		 perror("open dir fail");
		 exit(1);
	 }

	 struct dirent *sdp;
	 // 没有内容了返回NULL
	 while( (sdp= readdir(dir)) !=NULL ){
	    if(strcmp(sdp->d_name,".")== 0 || strcmp(sdp->d_name,"..")== 0){
	    	continue;
	    }
		 printf("%s\n",sdp->d_name);
	 }
	 closedir(dir);

	 return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值