1、基本概念
进程阻塞:
进程执行条件得不到满足,就自动放弃CPU资源而进入休眠状态,以等待条件满足,当条件满足时,系统就将控制权还给该进程进行未完成的操作
共享资源:
进程间协调使用的系统资源
锁定:
当某个进程使用共享资源时,可能需要防止别的进程对该资源的使用。Linux提供一些方法保证共享资源被某个进程使用时,其他进程不能使用,就称为共享资源的锁定
2、信号
信号的作用是通知一个或多个进程异步事件的发生,也可以用来处理某种严重的错误,可以从内核发往进程,也可以从一个进程发往另一个进程,
例如:用户在后台启动了一个程序,现在想要终止此进程的执行,就可以使用Kill命令将SIGTERM信号发送给这个进程,SIGTERM将终止该进程的执行
例如:执行exit()时,就向子进程的父进程发送SIGCHLD信号(子进程结束信号),若此时父进程在执行wait(),则父进程会被唤醒,若不是在执行wait(),则此父进程不会捕捉到SIGCHLD信号,该信号就不起作用,子进程进入过渡状态(如果父进程忽略SIGCHLD,子进程就会结束而不会进入过渡状态)
在大多数情况下,当进程收到一个信号时,会被正常终止,相当于进程执行了一个临时加入的exit()调用。
信号SIGQUIT、SIGILL、SIGTRAP、SIGFPE会导致一个非正常终止,它们将发生核心转贮,也就是把进程的内存映像写入进程当前目录的core文件中,core文件以二进制形式记录了终止时程序中全部变量值、硬件寄存器值和内核中的控制信息,非正常终止进程的退出状态除了其低端第7位置位外,其他与通过信号正常终止时一样
3、信号的复位
当一个信号的信号处理函数执行时,该进程又收到了同样的信号,那么该信号将会被存储而不会中断信号处理函数,直到信号处理函数执行完后重新调用相应处理函数,但是如果接收到了其他类型的信号,那么该信号处理函数将会被中断
例如:
#include <signal.h> int interrupt() { printf(“Interrupt called ”); sleep(3); printf(“Interrupt Func Ended. ”); } main() { signal(SIGINT,interrupt); printf(“Interrupt set for SIGINT ”); sleep(10); printf(“Program NORMAL ended. ”); return; }
执行结果就是
Interrupt set for SIGINT <ctrl+c> Interrupt called <ctrl+c> Func Ended Interrupt called Func Ended Program NORMAL ended.
而代码:
#include <signal.h> int interrupt() { printf(“Interrupt called ”); sleep(3); printf(“Interrupt Func Ended. ”); } int catchquit() { printf(“Quit called ”); sleep(3); printf(“Quit ended. ”); } main() { signal(SIGINT,interrupt); signal(SIGQUIT,catchquit); printf(“Interrupt set for SIGINT ”); sleep(10); printf(“Program NORMAL ended. ”); return; }
执行的结果是
Interrupt set for SIGINT <ctrl+c> Interrupt called <ctrl+> Quit called Quit ended. Interrupt Func Ended. Program NORMAL ended.
还有就是同一种信号不能累积,如:
Interrupt set for SIGINT <ctrl+c> Interrupt called <ctrl+c><ctrl+c><ctrl+c> Func Ended Interrupt called Func Ended Program NORMAL ended.
如果两个信号同时产生,系统不保证进程接收的顺序,因此,信号处理的顺序就不可控
4、进程间发送信号
一个进程可以通过signal()调用来处理其他进程发送来的信号,同时也可以通过kill(pid_t pid,int sig)来向其他进程发送信号。pid指定了信号发送的对象进程。可以是进程标识符,也可以是以下的值
1)pid = 0, 则信号被发送到当前进程所在的进程组的所有进程
2)pid = -1, 则信号按进程标识符从高到低的顺序发送给全部的进程
3)pid < -1,则信号被发送给标识符为pid 绝对值的进程组里的所有进程
信号发送的限制:普通用户进程只能向具有与其相同的用户标识符的进程发送信号,也就是说一个用户的进程不能向另一个用户的进程发送信号,只有root用户的进程才能给任何进程发送信号。
由于Kill需要知道目标进程标识符,所以一般在父子进程之间进行信号发送
举个例子:两个进程,通过发送SIGUSER1实现同步,两个进程都处于死循环中,接收对方信号前,处于暂停等待中,也就是pause(),使得程序暂停一直到信号达到,然后进程输出信息,并用kill发送一个信号给对方,若用户按了中断键,两个进程终止
#include <signal.h> int ntimes = 0; main() { int pid,ppid; int p_action(), c_action(); /* 设定父进程的SIGUSR1 */ signal(SIGUSR1,p_action); switch(pid = fork()) { case -1: /*fork 失败*/ perror("synchro"); exit(1); case 0: /*子进程模块*/ /* 设定子进程的SIGUSR1 */ signal(SIGUSR1,c_action); /* 获得父进程的标识符 */ ppid = getppid(); for(;;) { sleep(1); kill(ppid,SIGUSR1); pause(); } /*死循环*/ break; default: /*父进程模块*/ for (;;) { pause(); sleep(1); kill(pid,SIGUSR1); } /*死循环*/ } } int p_action() { printf("Patent caught signal #%d ",++ntimes); } int c_action() { printf("Child caught signal #%d ",++ntimes); }
运行结果就是
Patent caught signal #1 Child caught signal #1 Patent caught signal #2 Child caught signal #2 Patent caught signal #3 Child caught signal #3 Patent caught signal #4 Child caught signal #4 <ctrl+c>