信号:信号的发送函数/信号集和信号屏蔽/介绍一个sigaction()/信号应用的相关函数:sleep(),usleep() 计时器
进程间通信IPC -总体概念和IPC之间的管道
信号的发送方式:
1.键盘发送(少部分)
ctrl+c 信号2
ctrl+\ 信号3
2.硬件错误/代码问题(少部分)
段错误 信号11
总线错误 信号SIGBUS Linux是信号7
整数除以0 信号8 SIGFPE
3.Unix命令kill发送信号(全部)
kill -信号 进程PID
4.Unix的系统函数发送信号(有些函数是全部,有些是部分)
raise() kill() alarm() sigqueue()...
主要介绍:kill() 结束alarm()
& 如何判断某进程时候有发信号的权限?
信号0 可以用于测试,因为信号0 没有实际意义,所以法过去后没有后果,只是知道是否能发。
raise()只能给本进程发信号,功能可以被kill()完全替代,等价于kill(getpid(),signum).
int kill(pid_t pid,int signum)
功能:给所有有发送权限的进程发信号
参数:pid就是信号的接收进程,包括:
>0 发送给特定的某个进程(进程ID=pid)
0 发送给本组进程
-1 发送给所有的进程(有发送权限的所有进程)
<-1 发送给进程组ID=|pid|的所有进程
返回:成功为0,失败为-1
多个同名进程可以用 killall+进程名 杀掉同名进程
sleep(int seconds)函数会让进程休眠n秒,但当有 未忽略的信号到来的时候,休眠会被终端,返回剩余秒数。
usleep()函数也可以让进程休眠,但以微秒 作为休眠时间单位。
alarm()函数严格来说或不算信号发送函数,只能给自己发特定的信号(闹钟信号)。可以用来定时做任务。alarm(0)取消所有的闹钟。
信号集
一个二进制位能代表一个信号,1代表有,0代表没有。理论上说64位整数(long long int)就能存放所有的信号。但考虑到后期的扩展性,用了一个超级大的整数去代表所有的信号的有无。类型是 sigset_t(信号集)。倒数第n位就代表信号n
信号集是一种数据结构,逻辑数据结构/物理数据结构/运算数据结构。逻辑结构就是 在人的思维中的结构。物理结构就是计算机的底层如何区实现(内存是否连续,连续就是数组方式,不连续就是链式表方式);运算结构就是提供哪些功能,也就是需要提供哪些函数。运算结构一般都会包括:1.内存的分配和回收函数(数据结构的创建和销毁) 2.元素的添加(追加或插入)包括:单添和群添 3.元素的删除(从最后开始删除,从指定位置删除,从开始删除),包括单删和群删 4.元素的修改(不是所有的数据结构都需要) 5.元素的查询和取出
6.特殊的需要,不如排序等。
信号集提供的功能函数:
信号集的创建和销毁系统已经完成,没有函数。信号集的函数有5个:
sigaddset() -添加一个信号(把对应二进制 位 置1)
sigdelset() -删除一个信号(把对应二进制 位 置0)
sigemptyset() -清空信号集(清0)
sigfillset() -填满信号集(全部置1)
sigismember() -是否包含某个信号(查询)
信号屏蔽:
有些关键代码不希望被信号打断,而程序员无法控制信号的到来。因此信号屏蔽技术就用来解决关键代码不被信号打断的问题。信号屏蔽不是屏蔽信号的到来,而是采用:信号可以来,但来了以后暂时不做处理,等关键代码执行完毕后,解除了信号屏蔽再进行处理。(延迟处理)。
如何实现/解除 信号屏蔽?
函数sigprocmask() 可以屏蔽信号或解除屏蔽
屏蔽时把 新的需要屏蔽 的信号传入,同时允许把 旧的屏蔽的信号(信号屏蔽字)带出来;解除屏蔽时,把旧的 信号屏蔽字再次 设置进去即可。
int sigpromask(int how,sigset_t *new,sigset_t * old)
功能:屏蔽信号或是解除屏蔽
参数:how就是屏蔽的方式,包括:
SIG_BLOCK 相当于加法,屏蔽老的 加上 新的
SIG_UNBLOCK 相当于减法,屏蔽老的减去新的
SIG_SETMASK -相当于赋值,屏蔽新的,和老的无关
在使用时,用SIG_SETMASK即可。
new 就是需要屏蔽的信号集
old 就是用来返回之前屏蔽的信号集。用来解除屏蔽
返回值:成功为0,失败为-1
可靠信号在信号屏蔽时不会丢失,不可靠信号如果同一个信号多次到来会丢失;信号9不会被屏蔽。
信号的应用之一 ----计时器(了解)
Linux系统中,每个进程都支持3个计时器:真实/虚拟/实用。其中真实计时器 会记录程序的运行总时间。计时器的功能就是 一段时间之后 每隔一段时间 生产一个信号。系统会处理信号。
计时器的设置和获取函数:setitimer()和getitimer()
进程间通信(IPC)
进程间不能通过内存直接互访。但进程之间有时候需要数据交互,因此进程间通信就非常的重要。linux系统以多进程为核心的操作系统。因此进程间通信在Linux系统中频繁的被使用。
常见的IPC:
1 文件
2 信号
3 管道
4 共享内存
5 消息队列
6 信号量集(semaphore arrays)
7 网络编程(socket编程)
........
其中,共享内存/消息列队和信号量集 遵循相同的规范,在编程上有很多相似之处。通称为XSI IPC。
管道是Unix IPC最古老的方式之一,现在较少使用。管道是以管道文件 作为交互的媒介。管道文件是一种特殊的文件,touch vi open() 都无法创建管道文件。 mkfifo()函数或mkfifo命令可以创建管道文件。管道文件中不会保存数据,只是数据的中转站。因此管道文件在只有读或者只有写的时候,会阻塞,直到读写管道都畅通时才会中断数据。
历史上的管道是半双工的(数据不同时传递,单向)。现在有全双工的(双向)。
管道分为有名管道和无名管道,有名管道可以用于任何进程之间的IPC,而无名管道只能用于fork()创建父子进程之间IPC。有名管道就是程序员建立管道文件,然后IPC。无名管道就是内核处理管道文件,然后IPC。
IPC至少写两个程序 。
有名管道的使用步骤:
1.享用mkfifo命令或mkfifo() 创建管道文件。
2.用open()打开管道文件
3.读写管道文件
4.关闭文件描述符
5.如果必要的化,remove()删除管道文件。
2,3,4与读写普通文件一样。