发送
-
键盘
ctrl + c SIGINTctrl + \ SIGQUITctrl + z SIGTSTP
-
错误
- 除0
SIGFPE(8) - 非法内存访问
SIGSEGV(11) - 硬件故障
SIGBUS(7)
- 除0
-
命令
- kill -信号 进程
-
函数
-
kill
#include <signal.h> int kill(pid_t pid,int sig); 向pid进程发送sig信号,成功返回0,失败返回-1 pid取值: > 0 - 向pid进程发送sig信号 = 0 - 向同进程组中所有进程发送信号 = -1 - 向所有进程送信号 前提是调用进程有向其发送信号的权限 < -1 - 向绝对值小于|pid|的进程组发送信号 sig取值: 0信号为空信号,则kill函数仍会执行错误检查,但实际上并不会发送信号,经常用来检测确定一个进程是否存在 >0 正常发送信号 向一个不存在的进程发送信号,会返回-1,且errno为ESRCH -
raise
#include <signal.h> int raise(int sig); //向调用进程自身发送sig信号,成功返回0,失败返回-1 -
pause/sleep
#include <unistd.h> int pause(void); unsigned int sleep(unsigned int seconds); int usleep(useconds_t usec); //微秒- 使调用这个函数的进程进入睡眠状态,直到被信号终止或者`捕获
- 只有调用了信号处理函数并从此乃好处理函数中返回以后,该函数才会返回
- 该函数要么不返回,要么返回-1,
errno设置为ENINTR - 相当于没有时间限制的sleep
- sleep函数是的调用进程睡眠seconds秒, 除非有信号终止或者捕获
- 该函数要么返回0(完全睡够了seconds秒),要么返回剩余未睡眠的秒数(被信号中断)
- 相当于有时间限制的pause
-
alarm
#include <unistd.h> unsigned int alarm(unsigned int seconds);-
使内核在seconds秒之后,向调用进程发送SIGALRM(14)闹钟信号
-
SIGALRM信号的默认处理方式是终止进程
-
若之前已设置过alarm但时间还未到,又重新调用alarm函数会重新设置定时,并返回之前定时剩余的秒数
-
seconds参数取0时表示取消之前设过但未超时的定时
-
-
信号集
sigset_t 信号集类型 信号集
-
多个信号的集合类型
-
128个二进制位,
16byte, 每一个二进制位代表一个信号 -
处理信号集需要函数
#include <signal.h> int sigemptyset(sigset_t *set); //把信号集中的信号全部清空 int sigfillset(sigset_t *set); //将信号集中全部的位置1 把所有信号都添加到信号集中 int sigaddset(sigset_t *set,int signum); //将信号signum添加到信号集中 int sigdelset(sigset_t *set,int signum); //将信号signum从信号集中删除 //成功返回0 失败返回-1 int sigismember(const sigset_t *set,int signum); //判断signum是否在set信号集中 //如果signum在set信号集中返回1 否则返回0
信号屏蔽
-
当每个进程都有一个信号掩码(
signal mask)- 信号掩码是一个信号集,其中包括所有需要屏蔽的信号
-
当进程执行诸如更新数据可等铭感任务时,可能不希望被某些信号中断,因此就会屏蔽这些信号。
-
屏蔽!=忽略
- 屏蔽信号是指让信号处于未决状态,忽略是指捕获信号但不处理(进程依然被中断了)
-
当信号产生时,系统内核就会在为其维护的进场表中,位特定的进程设置一个该信号的标志位,这个过程称为递送
-
信号从产生到完成递送存在一定的时间间隔,出于这段时间的信号状态称为未决状态
-
处于未决状态的信号,当取消屏蔽后会被递送,
-
可以通过
sigprocmask函数,检测和修改调用进程的信号掩码 -
可以通过
sigpending函数,获取调用进程当前处于未决状态的信号集
#include <signal.h>
int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);
成功返回0,失败返回-1
how - 修改信号掩码的方式
SIG_BLOCK: 新掩码是当前掩码和set的并集 把set信号集中的所有信号加入到信号掩码中
SIG_UNBLOCK: 新掩码是当前掩码和set补集的交集 从信号掩码中删除在set信号集中的所有信号
SIG_SETMASK: 把set设置为新的信号掩码
set - 输入参数 信号集
NULL 忽略
oldset - 输出参数 备份以前的信号掩码,处理完之后好恢复原样,如果取NULL则不备份
int sigpending(sigset_t *set);
获取调用进程当前处于未决状态的信号集
成功返回0,失败返回-1
set作为输出参数
- 对于不可靠信号,通过
sigprocmask函数设置信号掩码之后,相同信号只会频闭第一个,并且在回复喜好掩码后被递送,其余的直接丢弃 - 对于可靠信号,则会在信号屏蔽时暗器产生的先后栓徐排队,一旦恢复信号掩码,会依次被处理
- 在信号处理函数中,实际上是自动屏蔽当前信号
sigaction/sigqueue
-
sigaction
#include <signal.h> //功能作用signal 只是功能比signal要更加强大 int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact); //注册signum信号的信号处理方式为act,之前的处理方式通过oldact输出 oldact可以为NULL 成功返回0 失败返回-1 struct sigaction{ void (*sa_handler)(int); //信号处理函数指针1 兼容signal void (*sa_sigaction)(int,siginfo_t *,void *); //信号处理函数指针2 sigset_t sa_mask; //信号掩码 调用信号处理函数时屏蔽的信号集 int sa_flags; //信号处理标志 void (*sa_restorer)(void); //保留 NULL }; typedef struct siginfo{ pid_t si_pid; //发送信号的进程 sigval_t si_value; //信号附加值(需要配置sigqueue函数) .... }siginfo_t; //发送信号时附加值 typedef union sigval{ int sival_int; void *sigval_ptr;//附加值如果是指针,只有在本进程中才有意义 }sigval_t;- 缺省情况下,在信号处理函数的执行过程中,会自动屏蔽这个正在处理的信号,而对于其他信号则不会屏蔽,通过
sigaction::sa_mask成员可以自行指定- 在信号处理函数的执行过程中,会把
sa_mask信号集中的信号加入信号掩码中 - 并且在信号处理函数执行完成后,自动解除
sa_mask中信号的屏蔽
- 在信号处理函数的执行过程中,会把
sa_flag信号处理标志,可以是以下的值- SA_ONESHOT/SA_RESETHAND
- 执行完一次信号处理函数后,即将对引信号的处理恢复为默认方式
- SA_NODEFER/SA_NOMASK
- 在信号处理函数执行过程中,不屏蔽这个正在处理的信号
- SA_NOCLDSTOP
- 若signum参数取SIGCHLD,则当子里程暂停时,不通知父进程
- SA_RESTART
- 系统调用一旦被signum参数所表示的信号中断,会自行重启
- SA_SIGINFO
- 使用信号处理函数指针2 sa_sigaction,通过该函数的第二个参数,提供更多的信息
- SA_ONESHOT/SA_RESETHAND
- 缺省情况下,在信号处理函数的执行过程中,会自动屏蔽这个正在处理的信号,而对于其他信号则不会屏蔽,通过
-
sigqueue
#include <signal.h> int sigqueue(pid_t pid,int sig,const union sigval value); //向进程pid发送sig信号并且携带附加值value 成功返回0,失败返回-1
计时器
-
系统为每个进程维护三个计时器
- 真实计时器
- 程序运行的实际时间
- 虚拟计时器
- 程序运行在用户态所消耗的时间
- 实用计时器
- 程序运行在用户态和内核态所消耗的时间之和
- 真实计时器
-
实际时间 == 用户态时间(虚拟计时器)+ 内核时间 + 睡眠时间 + 其他等待
-
三个计时器发送的时钟信号分别为
SIGVLRM- 真实计时器SIGVTALRM- 虚拟计时器SIGPROF- 实用计时器
-
可以用
setitimer为进程设定好计算器,计时器会定时向进程发送时针信号
getitimer
获取计时器
setitimer
设定计时器
#include <sys/time.h>
int getitimer(int which,struct itimerval* curr_value);
//获取计时器设置,成功返回0,失败返回-1
int setitimer(int which,const struct itimerval *new_value,struct itimerval *old_value);
//设置计时器,成功返回0,失败返回-1
参数:
which - 指定哪个计时器
ITIMER_REAL - 真实计时器
ITIMER_VIRTUAL - 虚拟计时器
ITIMER_PROF - 实用计时器
struct itimerval{
struct timeval it_interval; //重复间隔(每两个时针信号的时间间隔) 取0,只发送一个时钟信号
struct timeval it_value; //初始间隔 设置计时器之后多久产生第一个时针信号
};
struct timeval{
long tv_sec; //秒数
long tv_usec; //微秒数
};
守护进程
进程组
-
进程除了进程标识以外, 还有一个进程组标识,进程组ID
-
进程组是一个或多个进程的集合
-
进程组ID是取自一个进程的ID 一般来说是同一个进程内创建的子进程都会同属父进程的进程组中
pid_t getpgrp(void); //POSIX 获得调用进程的进程组ID pid_t getpgrp(pid_t pid); //BSD 获取pid进程的进程组ID int setpgrpid(pid_t pid,pid_t gpid); //给进程pid设置进程组ID为gpid
创建
- 守护进程(daemon)是生存周期长的一种进程
- 往往是操作系统引导装入时启动,仅在系统关闭时终止
- 没有控制终端,在后台运行
- 基本规则(防止不必要的交互)
- 首先是 调用umask将文件权限屏蔽字设置为一个已知的值(通常为0)
- 继承自父进程的权限往往会屏蔽一部分权限
- 调用fork()创建子进程,父进程exit
- 如果守护进程是作为简单的shell命令启动的,那么父进程终止会让shell认为命令已经执行完毕,
- 虽然子进程继承了父进程的继承组ID,但获得了一个新的进程ID,这就保证了子进程不是一个进程组的组长,那么就可以用setsid来创建一个新绘画
- 调用setsid()函数,让当前进程成为新会话的首进程
- 成为了新进程组的组长
- 没有控制终端
- 将当前工作路径更改为根目录
- 保证父进程退出后,能够确保在一个有效的文件系统中
- 当然可以根据需要切换至指定目录
- 关闭没有必要的文件
- close(0) close(1) close(2)
- 某些守护进程打开/dev/null使其具有文件描述符0,1,2这样一个试图读写标准输入、标准输出的代码不会有任何的错误
- 首先是 调用umask将文件权限屏蔽字设置为一个已知的值(通常为0)
//创建守护进程执行cmd命令
void daemon(const char *cmd){
umask(0);
pid_t id = fork();
if(id == -1){
return;
}
if(id != 0){
exit(0);
}
setsit();
chdir("/");
//close(0);close(1);close(2);
/*
struct rlimit rl;
if(getrlimit(RLIMIT_NOFILE,&rl) < 0)
return;
if(rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024;
*/
int i;
for(i=0;i</*rl.rlim_max*/1024;++i){
close(i);
}
//执行守护进程的任务
system(cmd);
}
会话
- 会话是一个或多个进程组的集合
- 如果调用
stesid的进程不是一个进程组的组长,则此函数创建一个新会话,具体的实用如下- 该进程变成新会话 的会话首进程,该进程会是该进程的唯一进程
- 该进程称为一个新进程组的组长
- 该进程没有控制终端,如果在调用setsid之前,该进程有一个控制终端,那么此后会切断这种连系,为了摆脱控制终端的控制,终端关闭也不会被杀死
#include <unistd.h>
pid_t getsid(pid_t pid);
若成功,返回会话首进程的进程组ID,若出错返回-1
pid若为0,获取当前调用getsid()函数的进程的会话首进程的进程组ID 相当于给了getpid()
出于安全考虑,若参数pid并不属于调用者所在的会话,可能获取不到
pid_t setsid(void); //创建一个新session
1667



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



