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、阻塞非阻塞)的更多相关文章

  1. 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头 ...

  2. Linux系统编程(2)——文件与IO之系统调用与文件IO操作

    系统调用是指操作系统提供给用户程序的一组"特殊"接口,用户程序可以通过这组"特殊"接口来获得得操作系统内核提供的特殊服务.在linux中用户程序不能直接访部内核 ...

  3. linux系统编程快速定位头文件的技巧之强大的grep命令

    这个技巧来自于我的实际开发碰到的: inet_addr这个函数用于把ip地址转成网络字节序,他的原型:in_addr_t inet_addr(const char *cp); 返回值为一个in_add ...

  4. Linux系统编程(4)——文件与IO之ioctl函数

    ioctl是设备驱动程序中对设备的I/O通道进行管理的函数.所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率.马达的转速等等.它的参数个数如下:int ioctl(int ...

  5. Linux系统编程(5)——文件与IO之mmap函数

    mmap系统调用它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作.而Posix或系统V的共享内存IPC则纯粹用于共享目的,mmap()实现共享内存也是其主要应用之一. ...

  6. Linux系统编程(3)——文件与IO之fcntl函数

    linux文件I/O用:open.read.write.lseek以及close函数实现了文件的打开.读写等基本操作.fcntl函数可以根据文件描述词来操作文件. 用法: int fcntl(int ...

  7. Linux系统编程---实现目录或者文件拷贝

    关于拷贝文件,前面写过一个例子:点击打开链接 ,可以看看,实现cp命令. 这次我们实现一个目录和文件的拷贝,综合点. #include <stdio.h> #include <fcn ...

  8. 《Linux系统编程(第2版)》

    <Linux系统编程(第2版)> 基本信息 作者: (美)Robert Love 译者: 祝洪凯 李妹芳 付途 出版社:人民邮电出版社 ISBN:9787115346353 上架时间:20 ...

  9. Linux系统编程温故知新系列 --- 01

    1.大端法与小端法 大端法:按照从最高有效字节到最低有效字节的顺序存储,称为大端法 小端法:按照从最低有效字节到最高有效字节的顺序存储,称为小端法 网际协议使用大端字节序来传送TCP分节中的多字节整数 ...

  10. Linux C 程序 文件操作(Linux系统编程)(14)

    文件操作(Linux系统编程) 创建一个目录时,系统会自动创建两个目录.和.. C语言实现权限控制函数 #include<stdio.h> #include<stdlib.h> ...

随机推荐

  1. 【实时文件同步】rsync+inotify-tools的安装与配置

    http://blog.csdn.net/yakson/article/details/52044403

  2. CF #327 DIV2 D、E

    两题都不难. 对于D题,可以使用相对移动,把吹aircraft移动变成相反方向的待援点的移动.假设此时时间为t,把aircraft的速度设为Vmax,看待援点在飞船最大速度飞行t秒的范围内,注意风向变 ...

  3. js+jquery动态设置/添加/删除/获取元素属性的两种方法集锦对照(动态onclick属性设置+动态title设置)

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html140 ...

  4. SQL Server高速导入数据分享

    SQL Server高速导入数据,能够尝试的方法例如以下:CTE.OpenRowSet/OpenDataSource.BULK INSERT.bcp.Shell. 以下依次介绍这几种办法. 1.CTE ...

  5. HTML_项目符号使用图片

    本文出自:http://blog.csdn.net/svitter 创建一个HTML页面. 其内容为一个无序列表. 列表中至少包括了5本畅销书,每本书之前的项目符号必须採用概述封面的缩略图. 这些信息 ...

  6. spark之map与flatMap差别

    scala> val m = List(List("a","b"),List("c","d")) m: List[ ...

  7. System.IO.Path

    System.IO.Path 分类: C#2011-03-23 10:54 1073人阅读 评论(0) 收藏 举报 扩展磁盘string2010c System.IO.Path提供了一些处理文件名和路 ...

  8. hibernate初步2

    Hibernate级联设计 数据库表之间的关系(主要关系有一对多.一对一.多对多)主要是从如下三个方面体现出来: 1.表体设计 2.实体类的设计 3.配置文件 以下是一些重要级联设计参数属性介绍: c ...

  9. python chunk 方式读取大文件——本质上还是file read自身支持

    参考:https://stackoverflow.com/questions/519633/lazy-method-for-reading-big-file-in-python 最优雅方式: file ...

  10. Codeforces--596A--Wilbur and Swimming Pool(数学)

     A - Wilbur and Swimming Pool Crawling in process... Crawling failed Time Limit:1000MS     Memory ...