linux系统调用IO

标签: C++

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:可以自己去看文档,一定要学会看文档
    参数2:
    O_RDONLY:只读方式打开文件。
    O_WRONLY:可写方式打开文件。
    O_RDWR:读写方式打开文件。
    O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三的参数为其设置权限(读写执行权限)。
    O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。
    O_TRUNC:如文件已经存在,那么打开文件时先删除文件中原有数据。
    O_APPEND:以添加方式打开文件,所以对文件的写操作都在文件的末尾进行。//write 文件开始位置开始写
   参数3: 
    0666 & umask(0002)[文件的实际权限 0666 & umask取反]
    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 函数使用

#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.等待解决阻塞

/**
 *  等待解决阻塞
 * /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;
}

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 读到末尾
   *  -1: 读取失败
   *  /dev/tty 读取非阻塞状态文件内容为空,也返回-1,但是此时errno==EAGAIN,此时轮读取内容
   */
  	  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;
}

4.1.1.文件理论:

     每一个文件都有(dentry[文件向导])
     里面保存 文件名称和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;
}

 

原文链接:加载失败,请重新获取