IO与进程
IO
学习IO目的:为了将数据存储以及对于文件的操作
标准IO:
- ANSI(美国标准协会)联合ISO(国际化标准组织)所形成的的一个C标准
(C标准:属于C库,含有一些列输入输出函数),只要操作系统支持C库,就可以使用这一类标准IO提供的函数----》移植性比较高
- 操作的文件一般是普通文件
- 属于高级磁盘IO--》存在缓冲区,减少了用户态切换至内核态,最后又返回用户态这样的频繁操作,意味着减少了系统开销。
- 通过文件流(FILE *)操作文件(打开文件时,系统会自动将该文件的信息定义结构体类型struct FILE 来进行存储,因此可以通过FILE *文件指针来操作文件)
文件IO
POSIX(可移植操作系统接口)推出的对于支持POSIX标准的系统可以操作文件的一系列函数(UNIX系统一般都会支持POSIX的标准)---》移植性不高(只能应用于UNIX系统)
- 操作的文件可以普通文件或者设备文件(硬件)
- 低级磁盘IO---》没有缓冲区,每一次都是系统调用,都会存在用户空间和内核空间的频繁切换工作,好处就是可以直接对于设备进行读写操作。
- 通过文件描述符来(非负的数字)操作文件
概念:文件被打开时,创建的结构体名为FILE的结构体指针,形象的称为“流”
分析:为啥称结构体指针为流?
--》因为标准IO存在缓冲区,所以每一次向缓冲区不断放入数据(每一次的放入数据:均是需要通过文件指针来进行读写指向的文件),存在三个特点:
- 有源头:APP
- 有目的:缓冲区
- 持续性:不断放入数据到缓冲区
---》一旦具备以上3个特点,就会形成流,所以通过文件指针操作文件可以理解为是通过操作流来操作文件
特性函数:
- perror(“string”);---》可以输出出错的原因
- feof(FILE* Stream);
作用:判断文件是否抵达末尾(不管是文本文件还是二进制文件,都可以判断)
返回值:
抵达文件末尾---》返回值为非零
未抵达文件末尾---》返回值0
操作文件IO:open close read write
头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
打开文件
函数原型:
int open(const char *pathname, int flags);
功能:打开指定的文件
参数:
参数1:所需打开文件的名字(包含路径)
参数2:打开文件的方式---》主标志 | 副标志
返回值:成功代表一个大于0 的数字(文件描述符),失败返回-1(errno ie set...)
int open(const char *pathname, int flags, mode_t mode);
功能:打开指定的文件
参数:
参数1:所需打开文件的名字(包含路径)
参数2:打开文件的方式---》主标志 | 副标志
参数3:当需要O_CREAT时,就要写该参数来为创建的新文件指明权限(八进制表示)
返回值:成功代表一个大于0 的数字(文件描述符),失败返回-1(errno ie set...)
关闭文件
#include <unistd.h>
int close(int fd);
功能:关闭一个文件描述符
参数:打开文件成功之后的文件描述符
- 写文件
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:向指定的文件描述符的文件内写入内容
进程
进程:正在运行的程序.它是动态的、资源管理的最小单位
交互进程:
shell命令进程,文本编辑器(vim),图形应用程序等
既有前台进程,又有后台进程
批处理进程
gcc(预处理,汇编,编译,链接) gcc 1.c -o 1 (四步合四为1)
数据库搜索引擎
守护进程
这类进程一直在后台运行,和任何终端都不关联,很多系统进程(各种服务)都是以守护进程的形式存在
进程间的状态
1.就绪态
就绪状态:进程已获得除CPU外的所有必要资源,只等待CPU时的状态。一个系统会将多个处于就绪状态的进程排成一个就绪队列。
2.运行态 (R) TASK_RUNNING
执行状态:进程已获CPU,正在执行。单处理机系统中,处于执行状态的进程只一个;多处理机系统中,有多个处于执行状态的进程。
3.等待态 (阻塞态)
阻塞状态:正在执行的进程由于某种原因而暂时无法继续执行,便放弃处理机而处于暂停状态,即进程执行受阻。(这种状态又称等待状态或封锁状态)
4.停止态 TASK_STOPPED
5.死亡态 EXIT_DEAD
6.僵尸态 EXIT_ZOMIE
进程函数: 进程创建 fork vfork()

进程执行
1.父子执行顺序:不确定
2.若父进程先结束,子进程未结束,子进程就会称为孤儿进程
3.若子进程先结束,父进程未结束,但父进程没有回收子进程的内核资源,子进程会称为僵尸进程进程
主动退出
(1).exit()函数叫C语言库提供,库函数._exit()函数叫系统调用函数,Linux系统提供的
(2).exit()自带清理缓冲区,_exit()不清理缓冲区
如何避免僵尸进程的产生(waitpid()回收子进程资源)
wait() 阻塞回收
waitpid(); 非阻塞回收
不接受子进程退出的状态
为什么要使用Exec函数族
(1)fork()子进程是为了执行新的程序
(2)可以直接在子进程if中写入新程序的代码,但是不够灵活,因为只能将子进程程序的源代码贴过来执行(必须知道源码,而且源码太长不好控制)比如要执行 ls -la命令就不行。
(3)使用exec运行新的可执行程序(可以把一个编译好的的可执行程序直接加载运行)
(4)我们有了exec族函数后,我们典型的父子进程就是这样的:子进程需要运行的程序被单独编写、单独编译成一个可执行文件(叫hello),项目是一个多进程项目,主程序叫父进程,fork创建了一个子进程后在进程中exec来执行hello,达到父子进程分别作不同的程序(宏观上)同时运行
什么是守护进程
1.它是守护进程,也就是通常所说的Daemon进程,是Linux三种进程之一,通常在系统启动时运行,在系统关闭时结束.
2.始终运行在后台,后台服务进程
3.独立于任何终端(和终端无关)。
4.周期性地执行某种任务或等待处理特定事件
什么会话
Linux是以会话(session),进程组的方式管理进程.每个进程属于一个进程组,子进程同属于该进程组.会话是由一个或者多个进程组的集合,通常用户打开一个终端,系统就会创建一个会话,所有通过该终端运行都属于这个会话,shell进程--->会话组的组长,一个会话最多打开一个控制终端,当控制终端结束时,所有的进程也跟着结束。
线程
为什么学习线程
(1).由于进程的地址空间是私有的,因此在进程间上下文切换时,系统开销比较大
(2).为了提高系统的性能,许多操作系统规范里引入了轻量级进程的概念,也被称为线程
(3).在同一个进程中创建的线程共享该进程的地址空间(除了栈区之
2.线程的相关函数
线程的创建 pthread_create
等待线程结束 pthread_join()
(1).pthread_join函数在线程退出时,用来清理线程资源。
(2).phtread_join函数会阻塞调用方,直至pthread_join所指的线程退出。
pthread_detach()
因为线程默认的状态是结合态的,所以,可以通过pthread_detach函数来设置线程为分离态。
pthread_detach函数的特点
使用pthread_detach函数后,使线程处于分离态;
使用pthread_detach函数后,线程在退出后,会自己清理资源
相较pthread_join,使用pthread_detach函数不会阻塞主线程,但是无法获取线程的返回值
线程出现的问题:
什么是互斥
1.多线程访问临界资源时,没有顺序而言,
若线程A访问临界资源,不允许其他线程访问资源。
2.引入互斥锁:用来保证共享数据操作的完整性。
什么是同步
多线程访问临界资源,按照某种顺序执行
无名信号量:用于线程间的同步
有名信号量:用于进程间的同步
无名信号量
信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问.
当信号量用于同步操作时,往往会设置多个信号量,并安排不同的初始值来实现他们之间的顺序执行
进程间的通讯方式
传统的unix通信方式
无名管道的特点:
具有亲缘关系(父子关系,兄弟关系)间的进程间通信
管道也可以看成是一个特殊的文件,对于它的读写也可以使用普通的read(),wirte()等函数,它不属于任何文件系统,存在内核当中.
它是一个半双工的通信模式,具有固定的读端(fd[0])和写端(fd[1])。
无名管道需要注意的问题
只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIGPIPE信号(通常Broken pipe错误)
有名管道的特点:
1.适用于两个不相关的进程间的通信,是一个半双工的通信
2.有名管道可以通过路径名来指出,并且在文件系统中可见,两个进程可以把它当成普通文件进行操作,open,read,write,close
3.有名管道必须遵循先进先出的原则,不支持lseek()函数
信号的特点:是唯一一种异步通信方式
SystemV IPC(进程间通信方式)
消息队列
消息队列特点
1.消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。
2.消息队列可以按照类型来发送/接收消息,相同的类型满足先进先出,不同的类型可以随意存取.
3.全双工通信方式
共享内存
1.共享内存的特点
- 共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
- 为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间
优点:
进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。
缺点:
由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等
信号量一般和共享内存配合使用
1.无名信号量:适用于线程同步
sem_t
sem_init(); 初始化信号量
sem_wait(); P操作 -1
sem_post(); V操作 +1
sem_getvalue() 获得信号的值
