linux系统调用IO
1. 系统调用概念:
库函数-> 内核函数[系统调用] -> 驱动[磁盘、显示器]
int main(){
int a=10; // 在用户空间执行
printf("%s\n","hello kernel"); // 系统调用进入内核,在内核执行
int b=10; // 回到用户空间
return 0
}
2. 基本系统调用IO函数
2.1.open函数
[code]#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取反] 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 函数使用
[code]#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进程控制块: 本质结构体
成员: 文件描述符表< 4000 br /> 文件描述符表,一个进程可以打开1024个文件
0 1 2:标准输入|输出|出错 文件描述符,每一个int对应一个结构体,每次打开一个文件,分配一个int
3. 阻塞概念、fcntl函数
阻塞:只有设备文件[dev/tty,终端输入文件]和网络存在阻塞问题,普通文件不存在阻塞问题
3.1.等待解决阻塞
[code]/** * 等待解决阻塞 * /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函数
[code]/** * 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,此时轮读取内容 */ 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类
[code]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. 重定向:
[code]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函数
[code]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; }
- 点赞
- 收藏
- 分享
- 文章举报
- CentOS 7 安装Boost 1.67及boost_python
- Linux编程:命令行选项单横线“-”与双横线“--”的区别
- Linux编程:通过 free 命令查看Linux系统实际可用内存大小
- Centos配置本地yum源
- Linux基本命令
- RedHat、centos 7.0以上设置系统打开文件数等设置问题
- 关于centos7下无法挂在win10的NTFS分区的靠谱解决方法
- linux VIM编辑器常用指令
- LINUX 常用指令学习
- linux自动执行指令crontab和at
- linux configuration
- Linux常用基本命令 每日小节
- Linux基础命令
- linux基础命令
- Linux系统DHCP基本配置
- centos8网卡使用-nmcli
- linux网卡配置
- centos6.5完全删除python,yum与重装
- linux基础操作 与yum编辑器的使用
- linux下安装python3.6