04-Linux系统编程-第01天(文件IO、阻塞非阻塞)
03-系统函数
系统编程章节大纲
1 文件I/O
2 文件系统
3 进程
4 进程间通信
5 信号
6 进程间关系
7 守护进程
8 线程
9 线程同步
10 网络基础
11 socket编程
12 高并发服务器
13 异步I/O
14 shell编程
Man page
1 命令和可执行程序
2 系统调用
3 标准库
系统调用本质就是函数
man 2 printf
man
严格来说write是 sys_write的浅封装
sys_write才是真正的系统调用 不过一般我们就说write是系统调用
内核:操作系统内部核心程序
内核里包含一些驱动程序
printf 然后 系统调用write函数 驱动显卡输出
int 返回一个文件描述符
vim里查看man手册 K
光标移动到open 然后 2 K
fcntl.h 定义O_RDONLY宏
unistd.h 声明open系统调用
stdio.h 声明printf
只有当第二个参数为O_CREAT的时候 才需要在第三个参数指定权限
(上面意思是 读 如果没有就创建)
最终创建出的文件的权限受第三个参数决定和umask一起决定
直接给结论:mode(第三个参数) & ~umask
mode 与 (umask取反)
比如umask是002 那么umask取反就是775
775与777 就是775
以只读方式打开 如果文件不存在就创建 如果文件已经存在就截断
(截断:把文件里内容清除掉)
文件描述符返回-1就是错了
04-read_write
想明白文件描述符本质
要先明白PCB
.bss: 未初始化的全局变量 初始化为0的全局变量 static修饰(静态变量)的未初始化的或初始化为0的变量
data:初始化为非0的全局变量 static修饰(静态变量)的初始化为非0的变量
rodata:常量、只读全局变量
stack:局部变量
段错误:
1. 对只读区域进程写操作。 char *p = "hello"; p[0] = 'H';
2. 对非访问区域进行读写操。 地址1000
3. stack空间耗尽。 函数内部 int arr[N]= {1}; #define N 100000000
当执行./a.out的时候操作系统 会虚拟出这样的虚拟地址
一个进程可能实际只使用几k 。 但是可用地址范围有这么多
pcb 本质是一个结构体
里面有一个成员变量是指针
指针指向一个指针数组(可以理解为一个字符指针数组 这个数组里面都是指针)
(int *p [N])
每一个指针指向字符串 严谨的来说 指向的还是结构体
(… 不明觉厉)
数组下标就理解为文件描述符
(实际上是一个指针指向结构体)
open一个文件时 内核会维护一个结构体让我操作该文件,
man 2 read
read和write函数的使用:
read和write函数的使用:
05-cp命令实现
copy.c:
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main(int argc,char * argv[])
{
char buf[1024];
int ret = 0;
int fd1 = open(argv[1],O_RDONLY);
int fd2 = open(argv[2],O_RDWR |O_TRUNC|O_CREAT,0664);
while((ret = read(fd1,buf,sizeof(buf))) != 0 )
{
write(fd2,buf,ret);
}
close(fd1);
close(fd2);
}
06-预读入缓输出
每当执行一个a.out的时候 在kernal里就会有一个与之唯一对应的PCB进程控制块
注意:
read 返回的是实际读到的字节数
write的时候 第三个参数应该传这个字节数 而不是像read一样是buf的大小
C标库一次读一个字符
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp,*fp_out;
int n;
fp = fopen(“dict.txt”,”r”);
if(fp == NULL) {
perror(“fopen error”);
exit(1)
}
fp_out = fopen(“dict.cp”,”w”);
if(fp == NULL) {
perror(“fopen error”);
exit(1);
}
while((n = fgetc(fp))!=EOF) {
fputc(n,fp_out);
}
fclose(fp);
fclose(fp_out);
return 0;
}
系统调用一次读一个字符:
#include <stdlib.h>
#include <errno.h>
#define N 1
int main(int argc,char * argv[])
{
int fd,fd_out;
int n;
char buf[N];
fd = open(“dict.txt”,O_RDONLY);
if(fd < 0){
perror(“open dict.txt error”);
exit(1);
}
fd_out = open(“dict.cp”,O_WRONLY|O_CREAT|O_TRUNC,0644);
if(fd<0){
perror(“open dict.cp error”);
exit(1);
}
close(fd);
close(fd_out);
return 0;
}
实际跑了一遍发现用read函数一个字节一个字节读还不如用fgetc速度块..
用系统调用不仅没快 反而奇慢
知识点:
预读入缓输出
预读入缓输出机制
我们认为用户程序用户定义buf直接使用系统调用write函数 跳过了标准库函数进入kernal
让kernal写到磁盘文件上了
实际上不是这样的:
实际上在内核中默认也维护了一个缓冲区 默认是4096byte
内核为了提高效率会一次性等缓冲区满以后再刷到磁盘上
当自己写的write函数写的时候 实际上没有写到磁盘上,而是在内核的缓冲区
这种机制称为缓输出
C标准库函数 自己带着一个缓冲区
fgetc fputc。。。 这些内部实现包含缓冲区
fgetc读字符会放到自己的缓冲区里
读4096字节放到自己的蓝色框缓冲区里
然后向下系统调用 write
把数据写到内核的缓冲区
然后再刷到磁盘上
之所以慢是因为
从用户区切换到内核区这个时间消耗很大。
直接系统调用write的方法 从用户区到内核区的切换工作耗了很多时间 每次一个字节就要切换一次
用fgetc fputc 只切换了一次
下面验证一下…:
strace 命令跟踪程序运行时间的系统调用
结果: 每次读写字节都切换
结果:
每次4096个 总的切换次数少很多
预读入:
一次性把缓冲区读满
需要的时候就从缓冲区取
既然效率这么低 那么学习系统调用write函数有啥用 直接用标库函数不就得了..?
答案: 比如qq聊天 要求即时性, 不是要等4096个byte再发过去
emm 把刚才write函数的define N 1 改成 1024 就能发现快很多了
把数据交给内核 内核什么时间写到磁盘上由内核决定 交给它内部的I/O优化算法。
07-错误处理函数
建议对所有的系统调用做错误处理
不使用printf打印错误了
使用perror
(perror不需要加\n)
结果:
还有一个strerror(不常用
08-阻塞非阻塞
终端设备是指: 0 标准输入 1 标准输出 3 标准错误
当cat的时候就阻塞了
stdin stdout stderror对应的文件都是设备文件 dev下面的tty
下面看三个小程序
*
读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。从终端设备或网络读则不一定,如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞,至于会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在那里。同样,写常规文件是不会阻塞的,而向终端设备或网络写则不一定。
现在明确一下阻塞(Block)这个概念。当进程调用一个阻塞的系统函数时,该进程被置于睡眠(Sleep)状态,这时内核调度其它进程运行,直到该进程等待的事件发生了(比如网络上接收到数据包,或者调用sleep指定的睡眠时间到了)它才有可能继续运行。与睡眠状态相对的是运行(Running)状态,在Linux内核中,处于运行状态的进程分为两种情况:
正在被调度执行。CPU处于该进程的上下文环境中,程序计数器(eip)里保存着该进程的指令地址,通用寄存器里保存着该进程运算过程的中间结果,正在执行该进程的指令,正在读写该进程的地址空间。
就绪状态。该进程不需要等待什么事件发生,随时都可以执行,但CPU暂时还在执行另一个进程,所以该进程在一个就绪队列中等待被内核调度。系统中可能同时有多个就绪的进程,那么该调度谁执行呢?内核的调度算法是基于优先级和时间片的,而且会根据每个进程的运行情况动态调整它的优先级和时间片,让每个进程都能比较公平地得到机会执行,同时要兼顾用户体验,不能让和用户交互的进程响应太慢。
阻塞读终端: 【block_readtty.c】
非阻塞读终端 【nonblock_readtty.c】
非阻塞读终端和等待超时 【nonblock_timeout.c】
注意,阻塞与非阻塞是对于文件而言的。而不是read、write等的属性。read终端,默认阻塞读。
总结read 函数返回值:
1. 返回非零值: 实际read到的字节数
2. 返回-1: 1):errno != EAGAIN (或!= EWOULDBLOCK) read出错
2):errno == EAGAIN (或== EWOULDBLOCK) 设置了非阻塞读,并且没有数据到达。
3. 返回0:读到文件末尾
1.阻塞读终端:
block_readtty.c:
2.非阻塞读终端::
nonblock_readtty.c
正常情况下./a.out tty是默认打开的 但是非阻塞的情况下 重新打开了tty文件 目的是给他指定以O_NONBLOCK非阻塞方式打开终端
定义tryagain标签,后面有goto语句
read函数返回小于0 (即-1) 时候已经出错了 但是其实出错了还可以进一步进行判断errorno
如果errno是 EAGAIN或=EWOULDBLOCK 说明当前是以非阻塞的方式读终端 而恰巧终端没有数据
如果errno不是EAGAIN说明是出错了
如果是EAGAIN说明read函数没有出错 只不过读的文件是一个设备文件而当前没有数据递达 所以就sleep了3秒
非阻塞读终端 每隔3秒弹出try again
3.非阻塞读终端 等待超时
nonblack_timeout.c:
man 2 read
正常情况下read函数返回的是你实际读到的字节数
09-lseek
标准库里讲过fseek 设置文件的读写位置
linux中可以使用lseek
lseek.c:
有个问题 当我write完之后 光标指向结尾了 这个时候read不出来了
所以要使用lseek
把指针再指向开头
lseek(fd,0,SEEK_SET)
注意:一个空文件lseek位置以后必须进行一下i/o操作 才会发生实质性的拓展 否则lseek没啥用
(如果不write就没啥用了
vi 查看一下生成的文件
前面这些填充我们称为 文件空洞 其实就是0
应该用od看 不是vi
od –tcx lseek.txt
lseek也可以用来获取文件大小
int len = lseek(fd,0,SEEK_END) // 这个就是文件大小
1 #include <string.h>
2 #include <fcntl.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6
7
8
9 int main(void)
10 {
11 int fd,n;
12 char msg[] = "It's a test for lseek\n";
13 char ch;
14
15
16 fd = open("lseek.txt",O_RDWR|O_CREAT|O_TRUNC,0644);
17
18 if(fd<0){
19 perror("open lseek.txt error");
20 exit(1);
21 }
22
23 int ret = lseek(fd,99,SEEK_SET);
24 if(ret == -1){
25 perror("lseek error");
26 exit(1);
27 }
28 write(fd,"a",1);
29
30 close(fd);
31
32 return 0;
10-fcntl
fcontrol
文件描述符对应着一个结构体 结构体内部控制着访问属性
(man 2 fcntl
fgetfileflag 获取当前文件信息
fsetfileflag 设置当前文件信息
位或 或等于
位图
bitmap
当描述一个文件属性的时候 通过一个整形数的二进制位来描述
i/o control
11-ioctl和传入传出参数
io control
iocntl.c:
TIOCGWINSZ
terminal IO windowsize
虚拟终端窗口大小
locate sys/ioctl.h
sudo grep –r “TIOCGWINSZ” /usr
传出参数 这个函数调用一结束 size结构体就有值了:
通过io control 拿到当前窗口占用的行值和列值
获取当前设备文件的行宽和列宽
举例strcpy(char *dest, const char *src)
*dest就是传出参数
04-Linux系统编程-第01天(文件IO、阻塞非阻塞)的更多相关文章
- Linux系统编程(1)——文件与I/O之C标准I/O函数与系统调用I/O
Linux系统的I/O也就是一般所说的低级I/O--操作系统提供的基本IO服务,与os绑定,特定于Linux平台.而标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头 ...
- Linux系统编程(2)——文件与IO之系统调用与文件IO操作
系统调用是指操作系统提供给用户程序的一组"特殊"接口,用户程序可以通过这组"特殊"接口来获得得操作系统内核提供的特殊服务.在linux中用户程序不能直接访部内核 ...
- linux系统编程快速定位头文件的技巧之强大的grep命令
这个技巧来自于我的实际开发碰到的: inet_addr这个函数用于把ip地址转成网络字节序,他的原型:in_addr_t inet_addr(const char *cp); 返回值为一个in_add ...
- Linux系统编程(4)——文件与IO之ioctl函数
ioctl是设备驱动程序中对设备的I/O通道进行管理的函数.所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率.马达的转速等等.它的参数个数如下:int ioctl(int ...
- Linux系统编程(5)——文件与IO之mmap函数
mmap系统调用它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作.而Posix或系统V的共享内存IPC则纯粹用于共享目的,mmap()实现共享内存也是其主要应用之一. ...
- Linux系统编程(3)——文件与IO之fcntl函数
linux文件I/O用:open.read.write.lseek以及close函数实现了文件的打开.读写等基本操作.fcntl函数可以根据文件描述词来操作文件. 用法: int fcntl(int ...
- Linux系统编程---实现目录或者文件拷贝
关于拷贝文件,前面写过一个例子:点击打开链接 ,可以看看,实现cp命令. 这次我们实现一个目录和文件的拷贝,综合点. #include <stdio.h> #include <fcn ...
- 《Linux系统编程(第2版)》
<Linux系统编程(第2版)> 基本信息 作者: (美)Robert Love 译者: 祝洪凯 李妹芳 付途 出版社:人民邮电出版社 ISBN:9787115346353 上架时间:20 ...
- Linux系统编程温故知新系列 --- 01
1.大端法与小端法 大端法:按照从最高有效字节到最低有效字节的顺序存储,称为大端法 小端法:按照从最低有效字节到最高有效字节的顺序存储,称为小端法 网际协议使用大端字节序来传送TCP分节中的多字节整数 ...
- Linux C 程序 文件操作(Linux系统编程)(14)
文件操作(Linux系统编程) 创建一个目录时,系统会自动创建两个目录.和.. C语言实现权限控制函数 #include<stdio.h> #include<stdlib.h> ...
随机推荐
- 【实时文件同步】rsync+inotify-tools的安装与配置
http://blog.csdn.net/yakson/article/details/52044403
- CF #327 DIV2 D、E
两题都不难. 对于D题,可以使用相对移动,把吹aircraft移动变成相反方向的待援点的移动.假设此时时间为t,把aircraft的速度设为Vmax,看待援点在飞船最大速度飞行t秒的范围内,注意风向变 ...
- js+jquery动态设置/添加/删除/获取元素属性的两种方法集锦对照(动态onclick属性设置+动态title设置)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html140 ...
- SQL Server高速导入数据分享
SQL Server高速导入数据,能够尝试的方法例如以下:CTE.OpenRowSet/OpenDataSource.BULK INSERT.bcp.Shell. 以下依次介绍这几种办法. 1.CTE ...
- HTML_项目符号使用图片
本文出自:http://blog.csdn.net/svitter 创建一个HTML页面. 其内容为一个无序列表. 列表中至少包括了5本畅销书,每本书之前的项目符号必须採用概述封面的缩略图. 这些信息 ...
- spark之map与flatMap差别
scala> val m = List(List("a","b"),List("c","d")) m: List[ ...
- System.IO.Path
System.IO.Path 分类: C#2011-03-23 10:54 1073人阅读 评论(0) 收藏 举报 扩展磁盘string2010c System.IO.Path提供了一些处理文件名和路 ...
- hibernate初步2
Hibernate级联设计 数据库表之间的关系(主要关系有一对多.一对一.多对多)主要是从如下三个方面体现出来: 1.表体设计 2.实体类的设计 3.配置文件 以下是一些重要级联设计参数属性介绍: c ...
- python chunk 方式读取大文件——本质上还是file read自身支持
参考:https://stackoverflow.com/questions/519633/lazy-method-for-reading-big-file-in-python 最优雅方式: file ...
- Codeforces--596A--Wilbur and Swimming Pool(数学)
A - Wilbur and Swimming Pool Crawling in process... Crawling failed Time Limit:1000MS Memory ...