Linux高级编程
2025-07-24 15:24:10 3 举报
AI智能生成
快速了解Linux知识, 抓住关键点
作者其他创作
大纲/内容
进程间通信(IPC)
IPC
广义上一切能是进程间相互交流的对象和方法都是IPC,比如文件、管道、socket等
狭义上的IPC特指消息队列】信号量和共享内存三种对象
应用范围
消息队列应用于不同进程之间少量数据的顺序共享
消息量应用于进程间的同步互斥的控制
共享内存应用于进程间大批数据的随机共享访问
条件
必须通过内核
机制
进程1从用户空间拷到内核缓冲区,进程2从内核缓冲区读走
目的
数据传输
共享数据
一个进程改变,所有进程改变
通知事件
一个进程向一个或一组进程发消息通知其发生什么事件(如:进程终止需要通知父进程)
资源共享
涉及到锁、同步机制
进程控制
有些进程希望完全控制另一个进程的执行(如Debug进程)此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变
分类
早期UNIX进程间通信
管道
特点
单向
先进先出
环形队列
无结构
固定大小
简单的流控制体制
分类
无名管道
特性
半双工通信方式,具有固定的读写端
具有亲缘关系的进程
基于文件描述符的通信方式<br>当创建管道时,会创建两个文件描述符<br>fds[0],固定用于读管道, fds[1]固定用于写管道
#include <unistd.h>
int pipe(int filedes[2]);
限制
1.两个进程通过一个管道只能实现单项通信,比如父写子读,如调换需要另开一个管道
2.管道读写端通过打开的文件描述符来传递,所以两进程要通信必须从公共祖先继承文件描述符
fork函数其大作用
特殊情况
1.写端关闭,读完数据再读返回0
2.写端未关闭,但不写数据,读完数据后,再次read就会阻塞
3.读端关闭,再写,进程通常异常终止
4.管道数据满时,写动作会阻塞
标准流管道
基于文件流的模式
popen();
FILE *popen ( char *command, char *type);
type是r与w二者之一,以参数type中第一个字符代表的方式打开
pclose();
int pclose( FILE *stream );
返回系统调用wait4( )的状态,破化管道
命名管道(FIFO)
概念
可以用于两个互不相关进程实现通信,,可以通过路径名哎指出,并且文件系统中可见
两进程可以将FIFO当作文件进行读写
遵循先进先出规则,读从开始处返回,写将数据添加到末尾
特性
在文件系统中作为特殊的设备文件
不同祖先的进程可以通过管道共享数据
可保存于文件系统中
#include <sys/types.h><br>#include <sys/state.h>
int mkfifo(const char *filename,mode_t mode);
FIFO
信号
概念
软件中断
在软件层次上对硬件中断的一种模拟
处理异步事件
特性
信号可直接进行用户空间进程与系统内核进程的交互,也可以内核进程通知用户空间进程发生了什么事件,可以任何时候发送,无需知道进程所处状态。
如果该进程未处于执行态,就由内核保存起来,直至其运行再传递<br>如果该进程处于阻塞,传递就延迟,直到其阻塞取消再传递
产生
通过终端按键产生信号
SIGINT(Ctrl-C)默认动作终止进程<br>SIGQUIT(Ctrl-\)默认动作是终止进程并且Core Dump<br>Ctrl-Z产生SIGTSTP信号,挂起符,内核给所有前台进程组发送该信号
调用系统函数想进程发信号
kill -SIGEGV pid<br>#include<signal.h><br>int kill(pid_t pid, int signo);<br>int raise(int signo);
kill -l 查看系统支持的信号列表
raise 函数允许进程香向自身发送信号
由软件条件产生信号
alarm.c
alarm函数发送的信号SIGALARM默认的系统动作为终止该进程,因此在程序调用pause之后,程序终止
alarm();
int pause(void);
通常用于判断信号是否已到
向读端已关闭的管道写数据的产生SIGPIPE信号
硬件异常产生信号
硬件检测到并通知内核,然后内核发送适当的信号
例如除以0,CPU运算单元会产生异常,<br>内核将这个异常解释为SIGFPE
主要来源
异常
进程运行过程中出现异常
其他进程
一个进程可以向另一个或一组进程发送信号
终端中断
作业控制
前台、后台进程的管理
分配额
CPU超时或文件大小突破限制
通知
通知j进程某事件发生,如I/O就绪等
报警
计时器到期
关联动作
忽略此信号
SIGKILL和SIGTOP永远不能被忽略<br>忽略硬件异常,结果无法想象
执行默认动作
异常终止
在进程当前目录下,把进程的地址空间内容、寄存器内容保存到Core文件中,然后终止程序
退出
直接终止
忽略
停止
挂起
继续
如果该进程被挂起,就恢复其运行,否则,忽略信号
提供一个信号处理函数
要求内核在处理该信号时切换到用户态执行这个处理函数,即捕捉一个信号
内核中的<br>表现形式
状态
递达
未决
信号集sigset_t存储
阻塞
信号集sigset_t存储,阻塞信号集也叫信号屏蔽字
每个信号都有两个标志位分别表示阻塞和未决(0 / 1),还有一个函数指针表示处理动作
linux中有64个信号<br>前32常规信号,它在传递之前产生多次只计一次<br>后32实时信号,它会排队等候
信号集操作函数
#include<signal.h>
int sigemptyser(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
sigprocmask
int sigprocmask(int how, const sigset_t *set,<br>sigset_t *oset);
sigpending
int sigpending(sigset_t *set);
信号处理
主要方式(2种)
使用简单的signal函数
使用时只需把要处理的信号和处理函数列出就ok
主要是对前32种非实时信号的处理,不支持信号传递信息
使用信号集函数
内核实现信号捕捉机制
1.用户注册信号处理函数;
2.当前发生中断或异常,从main函数切换到内核态;
3.在中断处理完毕后要返回main时先检查是否有信号抵达
4.有信号,内核返回用户态时并不是恢复main函数上下文,而是执行sighandler函数,它与main使用不同堆栈空间,不存在调用和被调用的关系,是两个独立的控制流程
5.sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态
6.如果没有新的信号递达,就返回用户态回复main函数上下文继续执行
#include<signal.h>下的常用信号出来函数
signal
void (*signal(int signum, void (*handler)(int)))(int);<br>typedef void (*sighandler_t)(int);<br>sighandler_t signal(int signum, sighandler_t handler);
sigaction
int sigaction(int signo, const struct sigaction *act, struct<br>sigaction *oact);
清除僵尸进程
父进程调用sigaction将SIGCHLD的处理动作置为SIG_IGN,<br>这样fork出来的子进程在终止时会自动清理掉,不会产生僵尸进程,<br>也不会通知父进程。
异步程序
信号处理函数
是单独的控制流程,与主控制流程是异步的,二者不存在调用和被调用关系,使用不同的堆栈空间
引入后使得一个进程具有多个控制流程,<br>如果这些控制流程访问相同的全局资源(全局变量、硬件资源等),就有可能出现冲突
可/不可重入函数
例如链表的insert函数被不同的控制流程调用,有可能再第一次调用还没返回时就再次进入该函数,成为重入
insert函数访问一个全局链表,有可能因为重入而造成错误,成为不可重入函数
条件(符合其一就为不可重入)
调用了malloc或free,因为malloc也是全局链表来管理堆的。
调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构
如果一个函数只访问自己的局部变量和资源,称为可重入函数<br>
竞态条件
系统运行的时序并不像我们写程序时设想的那样。<br>由于异步事件再任何时候都有可能发生(这里的异步事件指出现更高优先级的进程),如果我们写程序时考虑不周密,有可能由于时序问题而导致错误,这叫做竞态条件
sigsuspend
解决竞态条件问题
包含了pause的挂起等待功能,再对时序要求严格的场合下都应该调用sigsuspend而不是pause
#include <signal.h><br>◼ int sigsuspend(const sigset_t *sigmask);
基于System V进程间通信
System V消息队列
定位
用来在进程之间传递消息
特点
似于管道,优于管道<br>
消息队列先进先出
将输出的信息进行了打包处理,保证以每个消息消息为单位进行接收
可以对货物进行分类服务,根据类别出货
是一个链表
允许一个或多个进程向它写消息,一个或多个进程从中读消息<br>具有一定的FIFO特性,但可实现消息的随即查询<br>消息存在于内核中,由队列ID来标识
实现
创建和打开队列
int msgget (key_t key, int flag)
添加消息
读取消息
控制消息队列
System V信号灯
System V共享内存
基于Socket进程间通信
POSIX进程间通信
posix消息队列、 posix信号灯、 posix共享内存
使用较多的通信方式
共享内存
同一内存空间,牵一发动全身
信号量
作为进程间以及同一进程不同线程间同步手段
套接字
可用于不同机器之间的进程通信
初识操作系统
名称
Operating System<br>
角色
魔术师角色/管理者角色
定义
是管理计算机系统的全部硬件资源包括软件资源及数据资源;控制程序运行;改善人机界面;为其它应用软件提供支持等,使计算机系统所有资源最大限度地发挥作用,为用户提供方便的、有效的、友善的服务界面。
关系<br>
操作系统为用户程序提供了一个虚拟机器界面,而应用程序<br>运行在这个界面之上。 <br>
文件I/O<br>
系统调用
定义<br>
操作系统提供给用户程序的一组“特殊”接口<br>用户通过这组接口来获得内核提供的特殊服务<br>
实例
用户可以通过进程控制相关的系统调用<br>来创建进程、实现进程调度、进程管理等
意义<br>
为了更好的保护内核空间,程序运行空间分为内核空间和用户空间
用户进程通常情况不允许访问内核数据,也无法使用内核函数,<br>只能操作用用用户空间
场景<br>
用户需要获得一定的系统服务<br>系统规定用户进程进入内核空间的具位置<br>
API
用户编程接口
系统命令(可执行程序)高于API,实际内部引用了API来实现相应功能
方式
通过软中断向内核提交请求<br>以获得内核服务的接口<br>
操作
错误
errno<br>
strerror();
perror
文件<br>
一切皆文件
1.普通文件
2.目录文件<br>
3.链接文件
4.设备文件
文件描述符<br><unistd.h>
非负整数,索引值,指向内核进程打开文件的记录表
可作为参数传给相应函数
文件描述符的范围是0 ~ OPEN_MAX<br>Linux 下OPEN_MAX为1048576
进程启动所打开的3个文件(描述符)
标准输入0/STDIN_FILENO
标准输出 1/STDOUT_FILENO
标准出错处理 2/STDERR_FILENO
I/O
关于STDOUT_FILENO和标准I/O
区别
1、数据类型不一致<br>stdin等类型为 FILE *<br>STDIN_FILENO等类型为 int<br>使用stdin的函数主要有:fread、fwrite、fclose等,基本上都以f开头<br>使用STDIN_FILENO的函数有:read、write、close等
2、层次不一致<br>stdin等属于标准I/O,高级的输入输出函数。在<stdio.h>。<br>STDIN_FILENO等是文件描述符,是非负整数,一般定义为0, 1, 2,直接调用系统调用,在<unistd.h>。<br><br>stdin等属于标准库处理的输入流,其声明为 FILE 型的,对应的函数为标准库调用等<br>STDIN_FILENO等属于系统API接口库,其声明为 int 型,是一个打开文件句柄,对应的函数为系统级调用。
关系<br>
对于stdin等可以使用fileno()函数(用来取得参数stream指定的文件流所使用的文件描述符)来取得该文件流对应的文件描述符。<br>fileno(stdin) = STDIN_FILENO = 0<br>fileno(stdout) = STDOUT_FILENO = 1<br>fileno(stderr) = STDERR_FILENO = 2
open()
打开或创建文件,可指定文件属性,用户权限等参数
open.c
creat()
创建一个新文件<br>
close()
关闭一个文件,当一进程终止时,所有打开的文件都由内核自动关闭<br>
read()
将指定的文件描述符中读出的数据放到缓冲区,并返回实际读入的字节数<br>
实际读到的字节数少于要求读到的字节数<br>
1.文件已到末尾<br>2.从终端读,一次一行<br>3.从网络读,网络中缓冲机制<br> 可能造成返回值小于要求字节数<br>
write()
向打开的文件写数据,写操作从文件当前指针位置开始<br>
lssek()
在指定的文件描述符中将文件指针定位到相应的位置<br>
lseek.c
堵塞与非堵塞
堵塞
非堵塞
fcntl()
通过fcntl设置的都是当前进程如何设置访问设备和文件的<br>访问控制属性,如读、写、追加、非堵塞、加锁等
不设置文件或设备本身的属性,例如文件的读写权限、<br>串口波特率等。
五大功能<br>
用fcntl改变FileStatusFlag.c
ioctl()
特点<br>
ioctl 函数是I/O操作的杂物箱。不能用本章中其他函数表示的I / O操作<br>通常都能用ioctl表示。
终端I/O是ioctl 的最大使用方面(POSIX.1已经用新的函数代替ioctl进<br>行终端I / O操作)。
ioctl更多的是用于设备控制,比如磁盘的格式化,MODEM设备,磁带<br>的快进、快倒等。
ioctl甚至可以读写一些数据,但是这些数据是不能用read、write读写的,<br>称为out-of-band数据。
read\write读写的是in-band数据,是I/O操作的主体,而ioctl命令传送的<br>是控制信息,其中的数据是辅助的数据。
ioctl.c
对终端窗口大小数据据读取<br>
mmap()
磁盘文件映射到内存空间地址<br>
mmap.c
truncate()
文件截断
sync()
文件同步,把文件直接写入磁盘<br>
不建议使用,因为影响系统性能(加塞)<br>
文件和目录操作函数
exercise
将两个文件中数字相加格式相同
0 条评论
下一页