您的位置:首页 > 运维架构 > Linux

linux系统调用IO

2020-02-17 11:53 525 查看

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;
}

 

  • 点赞
  • 收藏
  • 分享
  • 文章举报
小置同学 发布了57 篇原创文章 · 获赞 57 · 访问量 10万+ 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: