- I/O
- 一、标准IO
- 1.概念
- 在C库中定义的一组用于输入输出的函数
- 2.特点
- (1).通过缓冲机制减少系统调用,提高效率
-
- (2.)围绕流进行操作,流用FILE *来描述
- (3).标准IO默认打开了三个流,stdin(标准输入)、stdout(标准输出)、stderr(标准错误)
- (4).一般操作普通文件
- (1).通过缓冲机制减少系统调用,提高效率
- 3.缓冲区
- (1).全缓冲:与文件相关
- 缓冲区刷新条件:
- 程序正常退出:return(main) exit
- 缓冲区溢出
- 强制刷新fflush
- fclose关闭对应的流
- (2).行缓冲:与终端相关
- 缓冲区刷新条件:
- \n
- 程序正常退出:return(main) exit
- 缓冲区溢出
- 强制刷新fflush
- fclose关闭对应的流
- (3).不缓冲:没有缓冲区,标准错误
- 计算缓冲区大小(1kb)
-
-
- 4.函数接口
- 4.1打开文件fopen
- FILE *fopen(const char *path, const char *mode)
- 功能:打开文件
- 参数:path:打开的文件 mode:打开的方式 r:只读,当文件不存在时报错,文件流定位到文件开头 r+:可读可写,当文件不存在时报错,文件流定位到文件开头 w:只写,文件不存在创建,存在清空 w+:可读可写,文件不存在创建,存在清空 a:追加(在末尾写),文件不存在创建,存在追加,文件流定位到文件末尾 a+:读和追加,文件不存在创建,存在追加,读文件流定位到文件开头,写文件流定位到文件末尾
- 注:当a的方式打开文件时,写只能在末尾进行追加,定位操作是无法改变写的位置,但是可以改变读的位置
- 返回值:成功:文件流;失败:NULL,并且会设置错误码。
- 4.2关闭文件 fclose
- int fclose(FILE* stream);
- 功能:关闭文件
- 参数:stream:文件流
-
-
- 4.3读写操作fread fwrite fgets fputs
- (1).按照字符串读写
- char * fgets(char *s, int size, FILE * stream); 功能:从文件中每次读取一行字符串 参数:s:存放字符串的地址 size:一次读取的字符个数 stream:文件流 返回值:成功:s的地址;失败或读到文件末尾:NULL特性:每次实际读取的字符个数为size-1个,会在末尾自动添加\0 每次读一行,遇到\n后不再继续,读下一行。int fputs(const char *s, FILE * stream); 功能:向文件中写字符串 参数:s:要写的内容 stream:文件流 返回值:成功:非负整数;失败:EOF
- char * fgets(char *s, int size, FILE * stream);
- 功能:从文件中每次读取一行字符串
- 参数:s:存放字符串的地址 size:一次读取的字符个数 stream:文件流
- 返回值:成功:s的地址;失败或读到文件末尾:NULL
- 特性:每次实际读取的字符个数为size-1个,会在末尾自动添加\0 每次读一行,遇到\n后不再继续,读下一行。
- int fputs(const char *s, FILE * stream);
- 功能:向文件中写字符串
- 参数:s:要写的内容 stream:文件流
- 返回值:成功:非负整数;失败:EOF
-
-
- 练习:通过fgets实现"wc -l 文件名"命令功能(计算文件行数)
-
- (2).按照二进制的方式进行读写:一般操作二进制文件
- size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
- 功能:从文件流读取多个元素
- 参数:ptr :用来存放读取元素 size :元素大小 sizeof(数据类型) nmemb :读取元素的个数 stream :要读取的文件
- 返回值:成功:读取的元素的个数; 读到文件尾: 0 失败: -1
- size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 功能:按对象写
- 参数:同上
- 返回值:成功:写的元素个数;失败 :-1
-
-
- (1).按照字符串读写
- 4.4文件定位操作
- void rewind(FILE *stream);
- 功能:将文件位置指针定位到起始位置 int fseek(FILE *stream, long offset, int whence);
- 功能:文件的定位操作
- 参数:stream:文件流 offset:偏移量:正数表示向后文件尾部偏移,负数表示向文件开头偏移 whence:相对位置: SEEK_SET:相对于文件开头 SEEK_CUR:相对于文件当前位置 SEEK_END:相对于文件末尾
- 返回值:成功:0;失败:-1
- 注:当打开文件的方式为a或a+时,fseek不起作用
- long ftell(FILE *stream);
- 功能:获取当前的文件位置
- 参数:要检测的文件流
- 返回值:成功:当前的文件位置,出错:-1
-
-
- 4.1打开文件fopen
- 1.概念
- 二、文件IO
- 1.概念
- 在系统中(posix)定义的一组用于输入输出的接口
- posix:可移植操作系统的接口
- 2.特点
- (1).没有缓冲机制,每次调用都会引起系统调用
- (2).围绕文件描述符进行操作,文件描述符都是非负整数(>=0),依次分配
- (3).文件IO默认打开了三个文件描述符,分别是0(标准输入),1(标准输出),2(标准错误)
- (4).可以操作任意类型的文件,目录文件除外。
- 3.函数接口
- 3.1打开文件 open
- int open(const char *pathname, int flags);
- 功能:打开文件
- 参数:
- pathname:文件路径名
- flags:打开文件的方式
- O_RDONLY:只读
- O_WRONLY:只写
- O_RDWR:可读可写
- O_CREAT:创建
- O_TRUNC:清空
- O_APPEND:追加
- O_EXCL:判错
- 返回值:成功:文件描述符;失败:-1
- 当第二个参数中有O_CREAT选项时,需要给open函数传递第三个参数,指定创建文件的权限。
- int open(const char *pathname, int flags, mode_t mode);
- 创建出来的文件权限为指定权限值&(~umask) //umask为文件权限掩码
- 3.2关闭文件 close
- int close(int fd);
- 功能:关闭文件
- 参数:fd:文件描述符
-
-
- 3.3读写操作 read write
- ssize_t read(int fd, void *buf, size_t count);
- 功能:从一个已打开的可读文件中读取数据
- 参数:
- fd 文件描述符
- buf 存放位置
- count 期望的个数
- 返回值:
- 成功:实际读到的个数
- 返回-1:表示出错,并设置errno号
- 返回0:表示读到文件结尾
- ssize_t write(int fd, const void *buf, size_t count);
- 功能:向指定文件描述符中,写入 count个字节的数据。
- 参数:
- fd 文件描述符
- buf 要写的内容
- count 期望值
- 返回值:成功:实际写入数据的个数。失败 : -1
- 3.4定位操作 lseek
- off_t lseek(int fd, off_t offset, int whence);
- 功能:设定文件的偏移位置
- 参数:
- fd:文件描述符
- offset偏移量 正数:向文件结尾位置移动 负数:向文件开始位置
- whence 相对位置 SEEK_SET 开始位置 SEEK_CUR 当前位置 SEEK_END 结尾位置
- 返回值:成功:文件的当前位置 。失败:-1
-
- 3.1打开文件 open
- 1.概念
- 三、文件IO与标准IO对比
-
- 四、库
- 1.库的定义
- 通俗讲将用户写好的程序打包形成一个整体;当其他用户或其他模块使用时,只要有这个库文件就可以,不需要源代码。也可以理解为一组预先编译好的方法集合。 本质上来说库就是一种可执行代码的二进制形式。
- 当要使用别人的函数的时候除了包含头文件还要有库 。
- linux系统存储库的位置一般在/lib或/usr/lib
- 2.库的分类
- 静态库和动态库,本质区别是代码被载入时刻不同。
- (1).静态库在程序编译时会被连接到目标代码中。
- 优点:程序运行时将不再需要该静态库;运行时无需加载库,运行速度更快
- 缺点:静态库中的代码复制到了程序中,因此体积较大; 静态库升级后,程序需要重新编译链接
- (2).动态库是在程序运行时才被载入代码中。
- 优点:程序在执行时加载动态库,代码体积小; 程序升级更简单; 不同应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。
- 缺点:运行时还需要动态库的存在,移植性较差
- 3.静态库的制作
- (1).将源文件生成目标文件(.o) gcc -c xxx.c -o xxx.o
- (2).创建静态库文件,用ar指令,它会将许多.o文件转换为.a文件 ar crs libxxx.a xxx.o 静态库文件名命名规范:lib为前缀,紧跟的是静态库名 ,.a为扩展名
- (3).测试静态库使用 gcc xxx.c -L指定库的路径 -l指定库名
-
-
- 4.动态库制作
- (1).用gcc创建共享库/动态库 gcc -fPIC -c xxx.c -o xxx.o -fPIC:创建与地址无关的编译程序 gcc -shared -o libxxx.so xxx.o
- (2).测试动态库的使用 gcc xxx.c -L指定库的路径 -l指定库名
-
- 解决方法
- (1).将库拷贝到系统库路径下(/lib /usr/lib)(编译时不需要指定库的路径)
- (2).更改环境变量,将库所在的路径添加到环境变量内 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. (只更改当前终端的环境变量,切换终端环境变量就不存在了)
- (3).添加/etc/ld.so.conf.d/*.conf文件。把库所在的路径加到文件末尾,并执行ldconfig刷新。
- sudo vi /etc/ld.so.conf.d/*.conf 添加动态库存在的路径
- 刷新:sudo ldconfig
- 解决方法
- 1.库的定义
- 五、进程
- 1.进程、程序
- 程序:编译好的可执行文件 存放在磁盘上的指令和数据的有序集合 程序是静态的,没有任何执行的概念
- 进程:一次程序的运行过程,一个独立的可调度的任务 执行一个程序所分配资源的总称 进程是动态的,包括创建、调度、执行和消亡。
- 2.进程的特点
- (1).系统会为每个进程分配0-4G的虚拟空间,其中0-3G为每个进程所独有,3-4G为所有进程所共有。
-
- (2).CPU调度进程时会为进程分配时间片(几毫秒-十几毫秒之间),当时间片用完之后,CPU会再进行其他进程的调度,实现进程的轮转,进而实现多任务操作。
- (1).系统会为每个进程分配0-4G的虚拟空间,其中0-3G为每个进程所独有,3-4G为所有进程所共有。
- 3.进程段
- Linux中的进程包含五个段:
- “BSS段”存放程序中未初始化的全局变量的一块内存区域
- “数据段”已初始化的全局变量的一块内存区域。
- “代码段”存放程序执行代码的一块内存区域
- “堆段”存放进程运行中被动态分配的内存段
- “栈段”又称为“堆栈段”存放的是函数的返回地址、函数的参数以及程序中的局部变量
- 4.进程分类
- 交互进程:该类进程是由shell控制和运行的。交互进程既可以在前台运行,也可以在后台运行。该类进程经常与用户进行交互,需要等待用户的输入,当接收到用户的输入后,该类进程会立刻响应,典型的交互式进程有:shell命令进程、文本编辑器等。
- 批处理进程:该类进程不属于某个终端,它被提交到一个队列中以便顺序执行。
- 守护进程:该类进程在后台运行。它一般在Linux启动时开始执行,系统关闭时才结束。
- 5.进程状态
- (1).运行态(TASK_RUNNING):R 指正在被CPU运行或者就绪的状态。这样的进程被成为runnning进程。
- (2).睡眠态(等待态): 可中断睡眠态(TASK_INTERRUPTIBLE)S:处于等待状态中的进程,一旦被该进程等待的资源被释放,那么该进程就会进入运行状态。 不可中断睡眠态(TASK_UNINTERRUPTIBLE)D:该状态的进程只能用wake_up()函数唤醒。
- (3).暂停态(TASK_STOPPED):T 当进程收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时就会进入暂停状态。可向其发送SIGCONT信号让进程转换到可运行状态。
- (4).死亡态:进程结束 X
- (5).僵尸态(TASK_ZOMBIE):Z 当进程已经终止运行,但还占用系统资源,要避免僵尸态的产生
- 6.进程状态切换
- 进程创建后,进程会进入就绪态,当CPU调度到此进程进入执行态,当时间片用完,此进程会进入就绪态等待CPU的下一次调度;当此进程在执行过程中需要执行某些IO操作(阻塞操作)会进入阻塞态,当完成IO操作时又可进入就绪态,等待CPU调度,当进程运行结束进入结束态。
-
- 进程创建后,进程会进入就绪态,当CPU调度到此进程进入执行态,当时间片用完,此进程会进入就绪态等待CPU的下一次调度;当此进程在执行过程中需要执行某些IO操作(阻塞操作)会进入阻塞态,当完成IO操作时又可进入就绪态,等待CPU调度,当进程运行结束进入结束态。
- 7.函数接口
- 7.1创建子进程
- pid_t fork(void);
- 功能:创建子进程
- 返回值: 成功:在父进程中:返回子进程的进程号 >0 在子进程中:返回值为0 失败:-1并设置errno
-
-
- 特点
- 1.子进程几乎拷贝了父进程的所有内容,包括代码、数据,缓冲区,系统数据段中的值,栈中的数据,父进程打开的文件,但是PID,PPID不同
- 2.fork之前的代码会被复制但是不会被重新执行,fork之后的代码会被复制,并且父子进程分别执行一遍。
- 3.父子进程的空间相互独立,互不影响,当在相应的进程中改变全局变量,静态变量,都互不影响
- 4.fork之前打开的文件,fork之后拿到的是同一个文件描述符,操作的是同一个文件指针
- 5.若父进程先结束,子进程成为孤儿进程,被init进程所收养,会变成后台进程。
- 6.若子进程先结束,父进程不结束,父进程没有及时回收,子进程就会变成僵尸进程(避免僵尸进程的产生)
- 7.1创建子进程
- 8.回收进程
- pid_t wait(int *status);
- 功能:回收子进程资源(阻塞)
- 参数:status:子进程退出状态,不接受子进程状态设为NULL
- 返回值:成功:回收的子进程的进程号 失败:-1
- pid_t waitpid(pid_t pid, int *status, int options);
- 功能:回收子进程资源
- 参数:
- pid:>0 指定子进程进程号 =-1 任意子进程 =0 等待其组ID等于调用进程的组ID的任一子进程 <-1 等待其组ID等于pid的绝对值的任一子进程
- status:子进程退出状态
- options:0:阻塞 WNOHANG:非阻塞
- 返回值:正常:结束的子进程的进程号 当使用选项WNOHANG且没有子进程结束时:0 出错:-1
-
-
- 9.结束进程
- void exit(int status);
- 功能:结束进程,刷新缓存
- 参数:退出的状态 不返回。
-
-
- 10.获取进程号
- pid_t getpid(void);
- 功能:获取当前进程的进程号
- pid_t getppid(void);
- 功能:获取当前进程的父进程号
-
-
- 1.进程、程序
- 六、进程间通信
- 1.为什么要进行进程间通信
- 数据传输、资源共享、事件通知、进程控制
- 2.进程间通信方式(7)
- (1).早期进程间通信 无名管道(pipe)、有名管道(fifo)、信号(sem)
- (2).system V IPC通信 共享内存(share memory)、消息队列(message queue)、信号灯集(semaphore)
- (3).BSD: 套接字(socket)
- 3.无名管道
- 3.1原理图
-
- 通信原理:一个进程的输出可以当作另一进程的输入
-
- 3.2特点
- (1).只能用于具有亲缘关系的进程间通信
- (2).半双工通信模式,具有固定的读端和写端 {单工:只能单方向通信, 广播 半双工:可以双向通信,但是同一时间不可以 对讲机 全双工:可以双向同时通信 打电话}
- (3).无名管道可以被看做一个特殊的文件,对于他的读写可以使用文件IO函数 (注意:不是文件,他只是存在于内核空间的一部分,无实际文件)
- (4).管道基于文件描述符进行通信。当一个管道建立的时候,它会自动创建两个文件描述符,一个用于读fd[0],一个用于写fd[1]。
- 3.3函数
- int pipe(int fd[2])
- 功能:创建无名管道
- 参数:文件描述符 fd[0]:读端 fd[1]:写端
- 返回值:成功 0 失败 -1
- 3.1原理图
- 4.有名管道(FIFO)
- 4.1特点
- (1).可以使互不相干的两个进程通信
- (2).有名管道可以通过路径名来指出,并在文件系统中可见,但是内容存储在内存中
- (3).进程通过文件IO操作有名管道
- (4).有名管道遵循先进先出的原则,不支持lseek()操作
- (5).半双工通信
- 4.2函数
- int mkfifo(const char *filename,mode_t mode);
- 功能:创建有名管道
- 参数:filename:有名管道文件名 mode:权限
- 返回值:成功:0 失败:-1,并设置errno号
- 4.1特点
- 1.为什么要进行进程间通信
- 七、信号
- 1.例子
- kill -l:查看系统中信号
- kill -num pid:给pid进程发送pid信号
- 2.概念
- (1).信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
- (2).信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。
- (3).如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
- 3.信号响应方式
- (1.)忽略信号:对信号不做任何处理,但是有两个信号不能做忽略处理:SIGKILL和SIGSTOP
- (2).捕捉信号:定义信号处理函数,当信号发生的时候,执行相应的处理函数,但是有两个信号不能做捕捉处理:SIGKILL和SIGSTOP
- (3).执行缺省操作:linux对每种信号都规定了默认信号。
- 4.信号的种类
- 2)SIGINT:结束进程,对应快捷方式ctrl+c
- 3)SIGQUIT:退出信号,对应快捷方式ctrl+\
- 9)SIGKILL:结束进程,不能被忽略不能被捕捉
- 14)SIGALRM:闹钟信号,alarm函数设置定时,当到设定的时间时,内核会向进程发送此信号结束进程。
- 15)SIGTERM:结束终端进程,kill 使用时不加数字默认是此信号
- 17)SIGCHLD:子进程状态改变时给父进程发的信号
- 19)SIGSTOP:暂停进程,不能被忽略不能被捕捉
- 20)SIGTSTP:暂停信号,对应快捷方式ctrl+z
- 信号的种类
- 在Linux中,信号被分为不可靠信号和可靠信号,一共64种,可以通过kill -l命令来查看
- ●不可靠信号:也称为非实时信号,不支持排队,信号可能会丢失,比如发送多次相同的信号,进程只能收到一次,信号值取值区间为1~31
- ●可靠信号:也称为实时信号,支持排队,信号不会丢失,发多少次,就可以收到多少次,信号值取值区间为32~64 信号产生的方式有如下几种:
- ● 对于前台进程,用户可以输入特殊终端字符来发送,比如输入Ctrl+C
- ● 系统异常,比如浮点异常和非法内存段访问
- ● 系统状态变化,比如alarm定时器到期时将引起SIGALRM信号
- ● 在终端运行kill命令或在程序中调用kill函数
- 5.函数接口
- 5.1发送信号
- int kill(pid_t pid, int sig);
- 功能:信号发送
- 参数:pid:指定进程 sig:要发送的信号
- 返回值:成功 0 失败 -1
- int raise(int sig);
- 功能:进程向自己发送信号
- 参数:sig:信号
- 返回值:成功 0
- 失败 -1
- int pause(void);
- 功能:用于将调用进程挂起,直到收到信号为止。
-
-
- 5.2定时器
- unsigned int alarm(unsigned int seconds)
- 功能:在进程中设置一个定时器
- 参数:seconds:定时时间,单位为秒
- 返回值:如果调用此alarm()前,进程中已经设置了闹钟时间,则 返回上一个闹钟时间的剩余时间,否则返回0。
- 注意:一个进程只能有一个闹钟时间。如果在调用alarm时 已设置过闹钟时间,则之前的闹钟时间被新值所代替
-
-
- 5.3信号处理
- #include <signal.h>
- typedef void (*sighandler_t)(int);
- sighandler_t signal(int signum, sighandler_t handler);
- 功能:信号处理函数
- 参数:
- signum:要处理的信号
- handler:信号处理方式
- SIG_IGN:忽略信号
- SIG_DFL:执行默认操作
- handler:捕捉信号 void handler(int sig){} //函数名可以自定义
- 返回值:成功:设置之前的信号处理方式 失败:-1
-
-
- 5.1发送信号
- 1.例子
- 八、共享内存
- 1.特点
- (1).共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,不需要进行任何数据的拷贝
- (2).为了在多个进程间进行数据的交互,内核专门留了一块内存区,可以由需要访问的进程将其映射到自己的地址空间
- (3).由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等
- 2.编程步骤
- (1).创建或者打开共享内存shmget
- (2).映射共享内存到自己的用户空间shmat
- (3).使用共享内存
- (4).撤销映射
- (5).删除共享内存
-
-
- 3.函数接口
- int shmget(key_t key, size_t size, int shmflg);
- 功能:创建或打开共享内存
- 参数: key 键值 size 共享内存的大小 shmflg IPC_CREAT|IPC_EXCL(判错)|0666
- 返回值:成功 shmid 出错 -1
-
-
- void *shmat(int shmid,const void *shmaddr,int shmflg);
- 功能:映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
- 参数:
- shmid 共享内存的id号
- shmaddr 一般为NULL,表示由系统自动完成映射,如果不为NULL,那么由用户指定
- shmflg:SHM_RDONLY就是对该共享内存只进行读操作 ,0 可读可写
- 返回值:成功:完成映射后的地址, 失败:-1的地址
- 用法:if((p = (char *)shmat(shmid,NULL,0)) == (char *)-1)
- int shmdt(const void *shmaddr);
- 功能:取消映射
- 参数:要取消的地址
- 返回值:成功0 失败的-1
- int shmctl(int shmid,int cmd,struct shmid_ds *buf);
- 功能:(删除共享内存),对共享内存进行各种操作
- 参数:
- shmid 共享内存的id号
- cmd IPC_STAT 获得shmid属性信息,存放在第三参数
- IPC_SET 设置shmid属性信息,要设置的属性放在第三参数
- IPC_RMID:删除共享内存,此时第三个参数为NULL即可
- 返回:成功0 失败-1
- 用法:shmctl(shmid,IPC_RMID,NULL);
-
-
- 4.命令
- ipcs -m:查看系统中共享内存
- ipcrm -m shmid :删除共享内存
- 1.特点
- 九、信号灯集
- 1.概念
- 信号灯(semaphore),也叫信号量。它是不同进程间或一个给定进程内部不同线程间同步的机制;System V的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。 通过信号灯集实现共享内存的同步操作。
- 2.编程
- (1).创建key值
- (2).创建或打开信号灯集:semget(semaphore)
- (3).初始化信号灯:semctl
- (4).PV操作:semop
- (5).删除信号灯集:semctl
- 3.函数接口
- int semget(key_t key, int nsems, int semflg);
- 功能:创建/打开信号灯
- 参数:
- key:ftok产生的key值
- nsems:信号灯集中包含的信号灯数目
- semflg:信号灯集的访问权限,通常为IPC_CREAT |IPC_EXCL |0666
- 返回值:成功:信号灯集ID 失败:-1
- int semctl ( int semid, int semnum, int cmd…/*union semun arg*/);
- 功能:信号灯集合的控制(初始化/删除)
- 参数:
- semid:信号灯集ID
- semnum: 要操作的集合中的信号灯编号
- cmd:
- GETVAL:获取信号灯的值,返回值是获得值
- SETVAL:设置信号灯的值,需要用到第四个参数:共用体
- IPC_RMID:从系统中删除信号灯集合
- 返回值:成功 0 失败 -1
- 用法:初始化: union semun{ int val; }mysemun; mysemun.val = 10;
- semctl(semid, 0, SETVAL, mysemun);
- 获取信号灯值:函数semctl(semid, 0, GETVAL)的返回值
- 删除信号灯集:semctl(semid, 0, IPC_RMID);
- int semop ( int semid, struct sembuf *opsptr, size_t nops);
- 功能:对信号灯集合中的信号量进行PV操作
- 参数:
- semid:信号灯集ID
- opsptr:操作方式
- nops: 要操作的信号灯的个数 1个
- 返回值:成功 :0 失败:-1
- struct sembuf { short sem_num; // 要操作的信号灯的编号 short sem_op; // 0 : 等待,直到信号灯的值变成0 // 1 : 释放资源,V操作 // -1 : 申请资源,P操作 short sem_flg; // 0(阻塞),IPC_NOWAIT, SEM_UNDO };
- 用法:
- 申请资源 P操作:
- mysembuf.sem_num = 0;
- mysembuf.sem_op = -1;
- mysembuf.sem_flg = 0;
- semop(semid, &mysembuf, 1);
- 释放资源 V操作:
- mysembuf.sem_num = 0;
- mysembuf.sem_op = 1;
- mysembuf.sem_flg = 0;
- semop(semid, &mysembuf, 1);
- 1.概念
- 十、消息队列
- 1.特点
- (1).消息队列是一种IPC对象,由消息队列ID来唯一标识
- (2).消息队列就是一个消息的列表,用户可以在消息队列中添加消息,读取消息
- (3).消息队列可以按照类型来进行消息的添加与读取
- (4).消息队列是存在linux内核中,以链表的形式进行存放
- 2.编程步骤
- (1).创建key值
- (2).创建或打开消息队列
- (3).使用:添加消息:按照类型将消息添加到已打开的消息队列末尾 读取消息:按照类型从消息队列中读取消息
- (4).删除消息队列
- 3.函数接口
- int msgget(key_t key, int flag);
- 功能:创建或打开一个消息队列
- 参数: key值 flag:创建消息队列的权限IPC_CREAT|IPC_EXCL|0666
- 返回值:成功:msgid 失败:-1
- int msgsnd(int msqid, const void *msgp, size_t size, int flag);
- 功能:添加消息
- 参数:
- msqid:消息队列的ID
- msgp:指向消息的指针。常用消息结构msgbuf如下: struct msgbuf{ long mtype; //消息类型 char mtext[N]}; //消息正文
- size:发送的消息正文的字节数
- flag:IPC_NOWAIT消息没有发送完成函数也会立即返回 0:直到发送完成函数才返回
- 返回值:成功:0 失败:-1
- 使用:msgsnd(msgid, &msg,sizeof(msg)-sizeof(long), 0)
- 注意:消息结构除了第一个成员必须为long类型外,其他成员可以根据应用的需求自行定义。
- int msgrcv(int msgid, void* msgp, size_t size, long msgtype, int flag);
- 功能:读取消息
- 参数:
- msgid:消息队列的ID
- msgp:存放读取消息的空间
- size:接受的消息正文的字节数
- msgtype:0:接收消息队列中第一个消息。 大于0:接收消息队列中第一个类型为msgtyp的消息. 小于0:接收消息队列中类型值不小于msgtyp的绝对值且类型值又最小的消息。
- flag:0:若无消息函数会一直阻塞
- IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG
- 返回值:成功:接收到的消息的长度 失败:-1
- int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
- 功能:对消息队列的操作,删除消息队列
- 参数:
- msqid:消息队列的队列ID
- cmd:
- IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。
- IPC_SET:设置消息队列的属性。这个值取自buf参数。
- IPC_RMID:从系统中删除消息队列。
- buf:消息队列缓冲区
- 返回值:成功:0 失败:-1
- 用法:msgctl(msgid, IPC_RMID, NULL)
-
-
- 4.命令
- ipcs -q:查看系统中消息队列
- ipcrm -q msgid:删除系统中的消息队列
-
- 1.特点
- 十一、线程:实现多任务编程
- 1.概念
- 线程是一个轻量级的进程,为了提高系统性能引入的线程 Linux里同样用task_struct来描述一个线程。 线程和进程都参与统一的调度。
- 2.进程与线程的区别
- 共性:都为操作系统提供了并发执行能力。
- 不同点: 调度和资源:线程是系统调度的最小单位,进程是资源分配的最小单位。地址空间方面:同一个进程创建的多个线程共享进程的资源;进程的地址空间相互独立。通信方面:线程通信相对简单,只需要通过全局变量可以实现,但是需要考虑临界资源访问的问题;进程通信比较复杂,需要借助进程间的通信机制(借助3g-4g内核空间)。安全性方面:线程安全性差一些,当进程结束时会导致所有线程退出;进程相对安全。
- 3.线程资源
- 共享的资源:可执行的指令、静态数据、进程中打开的文件描述符、信号处理函数、当前工作目录、用户ID、用户组ID
- 私有的资源:线程ID (TID)、PC(程序计数器)和相关寄存器、堆栈、错误号 (errno)、信号掩码和优先级、执行状态和属性
- 4.函数接口
- 4.1创建线程
- int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
- 功能:创建线程
- 参数:
- thread:线程标识
- attr:线程属性,NULL:代表设置默认属性
- start_routine:函数名:代表线程函数
- arg:用来给前面函数传参
- 返回值:成功:0 失败:错误码
-
-
- 4.2退出线程
- int pthread_exit(void *value_ptr)
- 功能:用于退出线程的执行
- 参数:value_ptr:线程退出时返回的值(任意类型)
- 返回值:成功 : 0 失败:errno
- 4.3线程回收
- int pthread_join(pthread_t thread, void **value_ptr)
- 功能:用于等待一个指定的线程结束,阻塞函数
- 参数:
- thread:创建的线程对象
- value_ptr:指针*value_ptr指向线程返回的参数
- 返回值:成功 : 0 失败:errno
- int pthread_detach(pthread_t thread);
- 功能:让线程结束时自动回收线程资源,让线程和主线程分离
- 参数:thread:线程ID
-
-
- 4.4获取线程号
- pthread_t pthread_self(void);
- 功能: 获取线程号
- 返回值: 成功:调用此函数线程的ID
-
-
- 4.5练习: 通过线程实现数据的交互,主线程循环从终端输入,线程函数将数据循环输出,当输入quit结束程序。(输入一次,打印一次) 标志位:输入是否完成/输出是否完成
-
- 4.1创建线程
- 5.线程同步:信号量
- 5.1linux信号量的分类
- (1). 内核信号量 由内核控制路径使用,类似于自旋锁
- (2). Posix信号量 a. 无名信号量:数据存储在内存中,通常在线程间使用或父子进程间 函数接口:sem_init\sem_wait\sem_post b. 有名信号量:数据存储在文件中,在进程间线程间都可以使用 函数接口:sem_open\sem_wait\sem_post\sem_close
- (3). System V信号量 是信号量的集合,叫信号灯集,属于IPC对象 函数接口:semget\semctl\semop
- 5.2无名信号量基础
- 通过信号量实现线程间同步。
- 信号量:通过信号量实现同步操作;由信号量来决定线程是继续运行还是阻塞等待。信号量代表某一类资源,其值表示系统中该资源的数量,信号量值>0,表示有资源可以用,可以申请到资源,继续执行程序,信号量值<=0,表示没有资源可以用,无法申请到资源,阻塞。
- 信号量是一个受保护的变量,只能通过三种操作来访问:初始化sem_init、P操作(申请资源)sem_wait、V操作(释放资源)sem_post。信号量的值为非负整数。
- 5.3函数接口
- int sem_init(sem_t *sem, int pshared, unsigned int value)
- 功能:初始化信号量
- 参数:
- sem:初始化的信号量对象
- pshared:信号量共享的范围(0: 线程间使用 非0:1进程间使用)
- value:信号量初值 返回值:成功 0 失败 -1
- int sem_wait(sem_t *sem)
- 功能:申请资源 P操作
- 参数:sem:信号量对象
- 返回值:成功 0 失败 -1
- 注:此函数执行过程,当信号量的值大于0时,表示有资源可以用,则继续执行,同时对信号量减1;当信号量的值等于0时,表示没有资源可以使用,函数阻塞。
- int sem_post(sem_t *sem)
- 功能:释放资源 V操作
- 参数:sem:信号量对象
- 返回值:成功 0 失败 -1 注:释放一次信号量的值加1,函数不阻塞
- 5.1linux信号量的分类
- 十二、线程同步
- 1.互斥
- 1.1概念
- 临界资源:一次仅允许一个进程所使用的资源
- 临界区:指的是一个访问共享资源的程序片段
- 互斥:多个线程在访问临界资源时,同一时间只能一个线程访问
- 互斥锁:通过互斥锁可以实现互斥机制,主要用来保护临界资源,每个临界资源都由一个互斥锁来保护,线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止。
- 1.2函数
- int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
- 功能:初始化互斥锁
- 参数:
- mutex:互斥锁
- attr: 互斥锁属性 // NULL表示缺省属性
- 返回值:成功 0 失败 -1
- int pthread_mutex_lock(pthread_mutex_t *mutex)
- 功能:申请互斥锁
- 参数:mutex:互斥锁
- 返回值:成功 0 失败 -1
- 注:和pthread_mutex_trylock区别:pthread_mutex_lock是阻塞的;pthread_mutex_trylock不阻塞,如果申请不到锁会立刻返回
- int pthread_mutex_unlock(pthread_mutex_t *mutex)
- 功能:释放互斥锁
- 参数:mutex:互斥锁
- 返回值:成功 0 失败 -1
- int pthread_mutex_destroy(pthread_mutex_t *mutex)
- 功能:销毁互斥锁
- 参数:mutex:互斥锁
- 1.3练习
- int a[10]={0,1,2,3,4,5,6,7,8,9}; 两个线程:一个线程打印,一个线程倒置
-
- int a[10]={0,1,2,3,4,5,6,7,8,9}; 两个线程:一个线程打印,一个线程倒置
- 1.4死锁
- 1.4.1概念
- 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
- 1.4.2死锁产生的四个必要条件
- (1)、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
- (2)、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
- (3)、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
- (4)、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。 注意:当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。
- 1.4.1概念
- 1.1概念
- 2.条件变量
- 2.1步骤
- pthread_cond_init:初始化
- pthread_cond_wait:阻塞等待条件产生,没有条件产生时阻塞,同时解锁,当条件产生时结束阻塞,再次上锁。
- pthread_mutex_lock(); //上锁
- pthread_cond_wait(cond, lock); //如果没有条件产生时,解锁,当等待到条件产生时,上锁。
- pthread_cond_signal:产生条件,不阻塞
- pthread_cond_wait先执行,pthread_cond_signal再产生条件
- 2.2函数接口
- int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
- 功能:初始化条件变量
- 参数:
- cond:是一个指向结构pthread_cond_t的指针
- restrict attr:是一个指向结构pthread_condattr_t的指针,一般设为NULL
- 返回值:成功:0 失败:非0
- int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
- 功能:等待信号的产生
- 参数:
- restrict cond:要等待的条件
- restrict mutex:对应的锁
- 返回值:成功:0,失败:不为0 注:当没有条件产生时函数会阻塞,同时会将锁解开;如果等待到条件产生,函数会结束阻塞同时进行上锁。
- int pthread_cond_signal(pthread_cond_t *cond);
- 功能:给条件变量发送信号
- 参数:cond:条件变量值
- 返回值:成功:0,失败:非0
- 注:必须等待pthread_cond_wait函数先执行,再产生条件才可以
- int pthread_cond_destroy(pthread_cond_t *cond);
- 功能:将条件变量销毁
- 参数:cond:条件变量值
- 返回值:成功:0, 失败:非0
-
- 2.1步骤
- 1.互斥
- 十三、linux IO模型
- 1.阻塞式IO:是常见,效率低,不浪费CPU
- 2.非阻塞式IO:轮询、耗费CPU、可以处理多路IO
- 2.1通过函数自带参数进行设置
-
- 2.2.通过设置文件描述符属性设置非阻塞
- int fcntl(int fd, int cmd, ... /* arg */ );
- 功能:设置文件描述符属性
- 参数:
- fd:文件描述符
- cmd:设置方式 - 功能选择
- F_GETFL 获取文件描述符的状态信息 第三个参数化忽略
- F_SETFL 设置文件描述符的状态信息 通过第三个参数设置
- O_NONBLOCK 非阻塞
- O_ASYNC 异步
- O_SYNC 同步
- arg:设置的值 in
- 返回值:
- 特殊选择返回特殊值 - F_GETFL 返回的状态值(int)
- 其他:成功0 失败-1,更新errno
- 使用:
- 0为例
- 0-原本:阻塞、读权限 修改或添加非阻塞
- int flags=fcntl(0,F_GETFL);//1.获取文件描述符原有的属性信息
- flags = flags | O_NONBLOCK;//2.修改添加权限
- fcntl(0,F_SETFL,flags); //3.将修改好的权限设置回去
-
- 2.1通过函数自带参数进行设置
- 3.信号驱动IO:异步通知方式,底层驱动的支持
- 异步通知:异步通知是一种非阻塞的通知机制,发送方发送通知后不需要等待接收方的响应或确认。通知发送后,发送方可以继续执行其他操作,而无需等待接收方处理通知。
- (1). 通过信号方式,当内核检测到设备数据后,会主动给应用发送信号SIGIO。
- (2). 应用程序收到信号后做异步处理即可。
- (3).应用程序需要把自己的进程号告诉内核,并打开异步通知机制。
- //1.设置将文件描述符和进程号提交给内核驱动 //一旦fd有事件响应, 则内核驱动会给进程号发送一个SIGIO的信号 fcntl(fd,F_SETOWN,getpid());
- //2.设置异步通知 int flags; flags = fcntl(fd, F_GETFL); //获取原属性 flags |= O_ASYNC; //给flags设置异步 O_ASUNC 通知 fcntl(fd, F_SETFL, flags); //修改的属性设置进去,此时fd属于异步
- //3.signal捕捉SIGIO信号 --- SIGIO:内核通知会进程有新的IO信号可用 //一旦内核给进程发送sigio信号,则执行handler signal(SIGIO,handler);
-
-
- 异步通知:异步通知是一种非阻塞的通知机制,发送方发送通知后不需要等待接收方的响应或确认。通知发送后,发送方可以继续执行其他操作,而无需等待接收方处理通知。
- 4.IO多路复用:select poll epoll
- 4.1
- ●应用程序中同时处理多路输入输出流,若采用阻塞模式,得不到预期的目的;
- ● 若采用非阻塞模式,对多个输入进行轮询,但又太浪费CPU时间;
- ● 若设置多个进程/线程,分别处理一条数据通路,将新产生进程/线程间的同步与通信问题,使程序变得更加复杂;
- ● 比较好的方法是使用I/O多路复用技术。其基本思想是: ○ 先构造一张有关描述符的表(最大1024),然后调用一个函数。 ○ 当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。 ○ 函数返回时告诉进程那个描述符已就绪,可以进行I/O操作。
- 4.2select
- 4.2.1特点
- (1).一个进程最多只能监听1024个文件描述符
- (2).select被唤醒之后要重新轮询,效率相对低
- (3).select每次都会清空未发生响应的文件描述符,每次拷贝都需要从用户空间到内核空间,效率低,开销大
- 4.2.2编程步骤
- (1).先构造一张关于文件描述符的表
- (2).清空表 FD_ZERO
- (3).将关心的文件描述符添加到表中 FD_SET
- (4).调用select函数
- (5).判断式哪一个或者式哪些文件描述符产生了事件 FD_ISSET
- (6).做对应的逻辑处理
- 4.2.3函数接口
- int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
- 功能: 实现IO的多路复用
- 参数: nfds:关注的最大的文件描述符+1 readfds:关注的读表 writefds:关注的写表 exceptfds:关注的异常表 timeout:超时的设置 NULL:一直阻塞,直到有文件描述符就绪或出错 时间值为0:仅仅检测文件描述符集的状态,然后立即返回 时间值不为0:在指定时间内,如果没有事件发生,则超时返回0,并清空设置的时间值 struct timeval { long tv_sec; /* 秒 */ long tv_usec; /* 微秒 = 10^-6秒 */ };
- 返回值: 准备好的文件描述符的个数 -1 :失败: 0:超时检测时间到并且没有文件描述符准备好
- 注意: select返回后,关注列表中只存在准备好的文件描述符
- 操作表: void FD_CLR(int fd, fd_set *set); //清除集合中的fd位 void FD_SET(int fd, fd_set *set);//将fd放入关注列表中 int FD_ISSET(int fd, fd_set *set);//判断fd是否在集合中 是--》1 不是---》0 void FD_ZERO(fd_set *set);//清空关注列表
- 4.2.4练习
- 输入鼠标的时候,响应鼠标事件,输入键盘的时候,响应键盘事件 (两路IO)
-
- 输入鼠标的时候,响应鼠标事件,输入键盘的时候,响应键盘事件 (两路IO)
- 4.2.5超时检测
- (1).概念
- 什么是网络超时检测呢,比如某些设备的规定,发送请求数据后,如果多长时间后没有收到来自设备的回复,那么需要做出一些特殊的处理
- 比如: 链接wifi的时候,等了好长时间也没有连接上,此时系统会发送一个消息: 网络连接失败
- (2).必要性
- (1). 避免进程在没有数据时无限制的阻塞;
- (2).规定时间未完成语句应有的功能,则会执行相关功能
-
- (1).概念
- 4.2.1特点
- 4.3poll
- 特点
- (1).优化文件描述符的限制,文件描述符的限制取决于系统
- (2).poll被唤醒之后要重新轮询一遍,效率相对低
- (3).poll不需要重新构造表,采用结构体数组,每次都需要从用户空间拷贝到内核空间
- 特点
- 4.4epoll:百万级
- 特点
- (1).监听的最大的文件描述符没有个数限制
- (2).异步IO,epoll当有事件产生被唤醒之后,文件描述符主动调用callback函数(回调函数)直接拿到唤醒的文件描述符,不需要轮询,效率高
- (3).epoll不需要重新构造文件描述符表,只需要从用户空间拷贝到内核空间一次。
- 特点
- 4.5总结
-
- 4.1
- 一、标准IO
I/O进程(全)
2025/4/17 7:28:20
来源:https://blog.csdn.net/JCBP_/article/details/147230125
浏览:
次
关键词:I/O进程(全)
版权声明:
本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。
我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com