Linux系统编程--信号与管道

1、信号与管道是什么?

首先了解信号与管道的意义,我们需要了解Linux系统中进程之间是如何通信的。Linux操作系统下,以进程为单位来分配或者管理资源,进程之间不能直接访问资源,因此,要求进程间的资源和信息共享就意味着进程之间能够相互通信。这就是通信机制:信号、管道。

2、进程通信的目的?

数据传输、共享数据、通知事件、资源共享、进程控制

3、何为通信机制?

了解该机制需要明白进程是如何来互相通信的,Linux支持多种进程间的通信,IPC机制,包括共享内存、消息队列、信号量、网络通信。

好了,介绍完这些需要了解的知识后进入正题!

一、信号

信号是一种异步的通知机制,用于在软件层面模拟中断机制。当一个进程收到信号时,其行为类似于处理器接收到一个中断请求,信号属于一种软中断方式

操作:

1.查看信号:kill -l

不可靠信号:1-31

可靠信号:34-64

9 号(SIGKILL),19 号(SIGSTOP) 不允许被忽略,也不允许被改造

杀死进程:Ctrl + C [ (2)、SIGINT ]

                  Ctrl + \  [ (3)、SIGQUIT ]

                KILL [ (9)、SIGKILL ]  kill-sig pid   sig信号

内核:当我们的程序出现一些错误的时候,例如段错误等,内核会给我们的进程发送杀死当前进程的信号。

程序:通过函数发送信号:kill

2、给进程发送信号:kill
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
形参:
        pid -- 你要给哪一个进程发送信号
        sig -- 信号(可以写编号,也可以写后面的宏定义)
返回值:
成功返回 0,失败返回-1
特殊信号:
        9 -- SIGKILL -- 杀死进程,不可以被忽略或者改造;
        14 -- SIGALRM -- 闹钟信号,杀死进程
        17 -- SIGCHLD -- 只要子进程状态发生改变,父进程就能接收到该信号
        18 -- SIGCONT -- 恢复 19 号 信号 暂停 的进程
        19 -- SIGSTOP -- 暂停进程
3、给自己发送信号:raise
#include <signal.h>
int raise(int sig);
形参:
sig -- 信号
等价于:
kill(getpid(),sig);
4、计时杀死进程:alarm
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
形参:
seconds 秒数
功能:
设置闹钟,闹钟事件到,产生闹钟信号,闹钟信号默认杀死当前进程;
如果在该 alarm 之前,已经设置过闹钟,此时闹钟会给更新
返回值:
如果上一次设置的有闹钟,返回上一个闹钟的剩余时间,否则返回 0;
5、产生信号再接触阻塞状态:pause
#include <unistd.h>
int pause(void);
只要运行,就阻塞,直到有一个信号产生,才会解除阻塞
6、信号处理函数:signal
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
形参:
signum -- 要处理的信号
handler -- SIG_DFL -- 默认处理
-- SIG_IGN -- 忽略处理
-- 函数 指针 -- 捕获到该信号,执行函数指针对应的函数
具体用法:
signal(2,SIG_DFL); 默认处理
signal(2,SIG_IGN); 忽略处理
signel(信号,Fun);
捕获到信号,执行 Fun 函数
7、注册退出清理函数:atexit
#include <stdlib.h>
int atexit(void (*function)(void));
//形参:函数指针
void Clean(void)
{
printf(“%s 被运行\n”,__FUNCTION__);
}
int main()
{
atexit(Clean);//注册退出清理函数,在 main 函数退出的时候,直到自动运行 Clean 函数
}

二、管道

管道(Pipe)是一种常见的进程间通信(IPC)机制,它允许两个进程通过一个半双工的通道进行数据传输。它作为两个进程之间的数据传输媒介。管道的一端连接一个写进程,另一端连接一个读进程

创建和使用

  • 创建匿名管道通常使用pipe()系统调用。
  • 使用fork()创建子进程后,父进程写入管道,子进程从管道中读取数据。
  • 写入管道通常使用write()系统调用,而读取管道通常使用read()系统调用。
管道的特点:
属于半双工通信;
管道正常的情况下,管道中没有内容, read 会阻塞
管道文件和普通的文件的区别:
管道的内容,一旦读走,就没有了
管道文件不允许使用 lseek 等光标偏移的函数
管道一定遵循先进先出的原则
管道有大小:
64k         64 * 1024 = 65536 字节
1、无名管道
适用于父子进程之间的通信;
没有名字的管道,没有 真正的介质文件存在 ,仅仅以 文件描述符 的形式可以让我们操作;
创建:pipe
#include <unistd.h>
int pipe(int pipefd[2]);
形参:
pipefd -- 整型数组,2 个元素
pipefd[0]:存放的管道的读端的文件描述符
pipefd[1]:存放的管道的写端的文件描述符
返回值:
成功返回 0,失败返回-1;
用法:
int fd[2] = {0};
pipe(fd);  fd[0]  fd[1]
创建子进程,也得有管道,问题:先创建子进程还是先创建管道?
先创建管道,子进程复制父进程资源(包含管道),

2、有名管道
1. 创建管道文件 .fifo
2. 打开管道文件 open
3. 读写 write read
4. 关闭 close
3、创建管道文件:mkfifo
#include <sys/stat.h>
#include<sys/types.h>
int mkfifo(const char * pathname,mode_t mode)
参数
pathname:要创建的 FIFO 文件的名字(路径+名字 以.fifo 结尾)
mode: 创建的 FIFO 文件的权限
真正的权限:mode &(~umask)
返回值
成功返回 0,失败(如果管道文件原本存在,直接返回失败)返回-1。
4、打开管道文件:open
int fd = open(char *pathname,int flag);
flag 只能在 O_RDONLY 和 O_WRONLY 中选择其一;
open 函数在打开管道文件的时候,必须两方都执行 open,open 才能打开,只有一个进程执行 open,open 会发生阻塞,直到另外的一个进程也运行到 open 的位置,两方同时解除阻塞
5、有名管道的删除:
int unlink(const char * pathname)
#include <unistd.h>
参数
pathname:要删除的 FIFO 文件的名字(带路径)
返回值:
成功返回 0,失败返回-1。

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-29 04:20:01       91 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-29 04:20:01       97 阅读
  3. 在Django里面运行非项目文件

    2024-04-29 04:20:01       78 阅读
  4. Python语言-面向对象

    2024-04-29 04:20:01       88 阅读

热门阅读

  1. 联合国官方统计的十大基本原则是什么

    2024-04-29 04:20:01       37 阅读
  2. PCIE与上位机调试流程

    2024-04-29 04:20:01       57 阅读
  3. 杆塔倾斜测量原理

    2024-04-29 04:20:01       126 阅读
  4. TypeScript 学习笔记

    2024-04-29 04:20:01       185 阅读
  5. 线程池问题

    2024-04-29 04:20:01       28 阅读
  6. 事务与锁机制

    2024-04-29 04:20:01       33 阅读
  7. wpf 按钮禁用样式

    2024-04-29 04:20:01       32 阅读
  8. 模型(model)和扩散模型(diffusion)

    2024-04-29 04:20:01       28 阅读