🔝🔝🔝
管道
管道分为匿名管道和命名管道
匿名管道
你怎样理解匿名管道?
这里管道我理解为内核里的一串缓存,一个进程从一边写,另一个进程从另一边读
匿名管道有什么特点呢
1.适用于父子进程之间的通信
2.只适用于单边通信
3.管道传输的数据是无格式并且大小受限的
匿名管道函数?
int pipe(fd[2]); //fd[0]是读端,fd[1]是写端
匿名管道原理?
我们可以让父进程通过fork函数创建出子进程后,子进程会写时拷贝一份和父进程一模一样的PCB,其中肯定就包括了相同的文件描述符,文件描述符一样后肯定就会指向相同的文件。那么这时比较混乱,因为同时有两个读端两个写端指向同一个文件嘛,所以我们通常会选择保留父进程写端,和子进程读端,就实现出匿名管道了。
匿名管道通信的四种情况?
读正常 && 写满:
- 写操作会被阻塞,直到管道中有空间可用。而读操作正常进行时,它会从管道中读取数据,每读取一部分数据,管道中就会空出相应的空间,当管道中有空间时,被阻塞的写操作就会继续执行,继续向管道中写入数据。
写正常 && 读空:
- 当管道为空且读操作试图从管道中读取数据时,读操作会被阻塞,直到有数据被写入管道中。而写操作正常进行时,一旦有数据写入管道,被阻塞的读操作就会被唤醒,开始从管道中读取数据。
写关闭 && 读正常:
- 当写端关闭后,读端在读取完管道中剩余的数据后,再进行读操作时,read 函数会返回 0,表示管道中已经没有数据可读,且写端已经关闭。这是一种正常的通信结束的情况,读端可以根据返回值 0 来判断通信是否结束。
读关闭 && 写正常:
- 当读端关闭后,如果写端继续向管道中写入数据,会引发 SIGPIPE 信号。默认情况下,进程接收到 SIGPIPE 信号后会终止。
使用管道如何双方通信?
建立两个管道。
命名管道
如何理解命名管道?
命名管道的本质就是一种特殊类型的文件
命名管道有什么特点呢?
1.适用于任意两个进程之间
2.对传输的数据没有格式要求,但有大小限制
3.单边通信
命名管道函数?
int mkfifo(const char* filename,mode_t mode);
消息队列
如何理解消息队列?
消息队列本质是内核中的消息链表。
它很想发电子邮件,A进程往B的消息队列中发入信息,A进程就可以走了,B进程需要数据时再去读取即可。
消息队列有什么特点呢?
1.附件大小有限制,每个消息体的大小固定,发送前两个进程就已经约定了消息体的格式
2.通信不及时,用户态和数据态之间会发生多次数据拷贝,影响效率。
共享内存
如何理解共享内存?
本质就是两个进程的分别的某块虚拟内存映射到同一块物理内存上
共享内存的特点
1.效率快,进程间的数据传递无需执行进入内核的系统调用
2.可能发生多个进程同时修改物理内存中的数据的问题
共享内存函数
shmget
功能:⽤来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存⼤⼩
shmflg:由九个权限标志构成,它们的⽤法和创建⽂件时使⽤的mode模式标志是⼀样的
取值为IPC_CREAT:共享内存不存在,创建并返回;共享内存已存在,获取并返回。
取值为IPC_CREAT | IPC_EXCL:共享内存不存在,创建并返回;共享内存已存在,出
错返回。
返回值:成功返回⼀个⾮负整数,即该共享内存段的标识码;失败返回-1
shmat
功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回⼀个指针,指向共享内存第⼀个节;失败返回-1
说明:
shmaddr为NULL,核⼼⾃动选择⼀个地址
shmaddr不为NULL且shmflg⽆SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会⾃动向下调整为SHMLBA的整数倍。
公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表⽰连接操作⽤来只读共享内存
shmdt
功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
shmctl
功能:⽤于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向⼀个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1
| 命令 | 说明 |
|---|---|
| IPC_STAT | 把 shmid_ds 结构中的数据设置为共享内存的当前关联值 |
| IPC_SET | 在进程有足够权限的前提下,把共享内存的当前关联值设置为 shmid_ds 数据结构中给出的值 |
| IPC_RMID | 删除共享内存段 |
信号量
信号量是什么?
信号量本质相当于一个表示可使用资源数量的计数器,它的作用是实现进程间的互斥与同步,本质是对资源的一种预定机制
信号量具体操作?
信号量有两个操作:P操作和V操作
P操作,计数器++,申请资源
V操作,计数器–,释放资源
信号
信号是什么?
信号是程序发生异常时的一种异步响应机制
信号产生的原因有哪些?
1.硬件中断 除零,内存越界
2.软件条件
如果读端进程关闭了管道的读端,而写端进程还在向管道写入数据,那么写端进程就会收到 SIGPIPE 信号。
3.通过终端按键产生信号 e.g. CTRL+C
4.调用系统命令 kill -9 [pid]
5.使用函数产生信号 kill函数 raise 函数 abort 函数
收到信号和怎么处理?
1.执行默认动作
3,自定义捕捉信号
3.忽略信号
其中SIGKILL和SIGSTOP不能被自定义或者忽略
进程如何保存信号的?
几个概念
信号递达:
实际执行处理信号的动作叫做信号的递达
信号的未决状态(pending):
在信号产生到信号递达之间的状态叫做信号的未决状态
信号阻塞(block):
一旦一个信号被设置了阻塞之后,那么此进程就不会收到此信号
信号忽略:
一旦一个信号被设置为忽略,那么当这个信号来临后,进程不会对此信号做处理
阻塞和忽略的区别:
一个信号被阻塞后,它就不会递达,而忽略是指信号递达后,执行的动作是什么都不做
进程如何保存信号的?
![![[Pasted image 20250320173527.png]]](/https://i-blog.csdnimg.cn/direct/e893bd918b7f430c853e2512cc2f5b42.png)
在进程的PCB中,存在三张和信号相关的表(前两张是位图,最后一张是数组)
block位图:
这个位图代表,在这个进程中,有哪些信号是被阻塞了的?这个位图又被称为信号屏蔽字
pending位图:
这个位图是进程存储信号的结构,位图中的第n个位置为0/1代表是否收到了n号信号,如若收到,会在后续进行处理,这个位图又被称为信号集
handler数组:
首先,这个数组是一个函数指针数组,里面存储的是函数的地址,数组的n号元素存储的函数地址代表收到n号信号之后,要去处理信号时,需要调用的函数.
在上图中,SIG_DFL宏代表这个函数就是此信号的默认处理函数,SIG_IGN宏代表收到这个信号后,直接忽略此信号,当然我们也可以自己写一个函数来充当信号的处理方法,
进程如何捕捉信号的(重点)?
从内核态返回用户态时,会进行信号的检测和处理。
[[Linux系统编程/用户态和内核态]]
- 当前正在执⾏ main 函数,这时发⽣中断或异常切换到内核态执行系统调用
- 当执行完系统调用后,会回到用户态
- 在这期间会进行信号的检测和处理
- 如若此时检测到有信号到来,那么会把代码直接跳转到信号处理的函数处
- 当信号处理函数返回时还会执行特殊的系统调用,再回到内核态
![![[Pasted image 20250320174642.png]]](/https://i-blog.csdnimg.cn/direct/ec10e8a1f4f0454ca2e56b380c362353.png)
![![[Pasted image 20250320175752.png]]](/https://i-blog.csdnimg.cn/direct/86282792dd2840529f5ad2eefd92340a.png)
信号相关函数
sighandler 函数返回后自动执行特殊的系统调用 sigreturn 再次进入内核态,主要有以下几方面原因:
- 恢复信号屏蔽字:在
sighandler函数被调用时,内核会自动将当前信号加入进程的信号屏蔽字,防止在处理该信号过程中,相同信号再次被处理而导致混乱。当sighandler函数返回后,需要通过sigreturn系统调用进入内核态,来自动恢复原来的信号屏蔽字,以保证进程后续信号处理的正常逻辑 。比如进程在处理信号 A 时,不想被其他的信号 A 打断,就会在处理期间屏蔽信号 A,处理完后就需要恢复原来的信号屏蔽状态。 - 检查新信号:进入内核态后,内核可以检查是否有新的信号递达。如果有新的信号,内核会按照相应的规则(如根据信号的处理动作是默认、忽略还是自定义处理函数等)来进行处理;如果没有新的信号要递达,内核才会恢复之前进程被中断处(如 main 函数)的上下文,让进程继续执行后续指令。

465

被折叠的 条评论
为什么被折叠?



