信号
信号产生
Ctrl + c、Ctrl + z
;kill、raise、abort
;alarm、setitimer
;kill
;linux 查看信号种类man 7 signal
man 2 kill
#include
#include
int kill(pid_t pid, int sig);
简单使用,用子进程结束父进程。
#include
#include
#include
#include
#include
int main() {
int i;
for (i = 0; i < 5; ++i) {
pid_t pid = fork();
if (pid == 0) {
break;
}
}
if (i == 2) {
printf("I am a child process\n");
sleep(5);
kill(getppid(), SIGKILL);
while (1) {
;
}
} else if (i == 5) {
printf("I am a father process\n");
sleep(1);
}
return 0;
}
给自己发信号。
man raise
#include
int raise(int sig);
#include
#include
#include
#include
#include
int main() {
sleep(1);
raise(SIGKILL);
return 0;
}
定时给自己发送 SIGALRM。
man 2 alarm
#include
unsigned int alarm(unsigned int seconds);
#include
#include
#include
#include
int main() {
alarm(6);
for (int i = 0; i < 6; ++i) {
printf("i = %d\n", i);
sleep(1);
}
return 0;
}
程序运行可以看到,的确在第六秒发送了信号。
当有信号的时候调用函数。
man 2 signal
#include
typedef void (*sighandler_t)(int); // 函数指针,返回值void,参数类型int
sighandler_t signal(int signum, sighandler_t handler);
man 2 sigaction
#include
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
sigaction 结构体
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask; // 临时屏蔽的信号集
int sa_flags; // 0使用第一个函数指针, SA_SIGINFO使用第二个函数指针
void (*sa_restorer)(void); // 无效
};
周期性的发送信号。
man 2 setitimer
#include
int setitimer(int which, const struct itimerval *new_value, truct itimerval *old_value)
struct itimerval 结构体定义
struct itimerval {
struct timeval it_interval; /* Interval for periodic timer 周期性时间设置*/
struct timeval it_value; /* Time until next expiration 下次闹钟产生*/
};
struct timeval 结构体定义
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds 微秒*/
};
使用:
#include
#include
#include
#include
void catch_signal(int num) { printf("catch a %d signal\n", num); }
int main() {
signal(SIGALRM, catch_signal);
struct itimerval myit = {{3, 0}, {5, 0}};// it_interval.tv_sec = 3, it_interval.tv_usec = 0...
setitimer(ITIMER_REAL, &myit, NULL);
while (1) {
printf("...\n");
sleep(1);
}
return 0;
}
通过运行结果,我们可以看到,程序的确是按照我们的预期跑的,5 秒后闹钟产生,之后每隔 3 秒发送一次信号。
man 3 sigemptyset
#include
int sigemptyset(sigset_t *set); //清空信号集函
int sigfillset(sigset_t *set); // 填充信号集
int sigaddset(sigset_t *set, int signum); //添加某个信号到信号集
int sigdelset(sigset_t *set, int signum); //从集合中删除某个信号
int sigismember(const sigset_t *set, int signum); //是否为集合里的成员
sigismember()返回 1 表示 signum 在集合中,0 表示不在,-1 失败,设置 error。
其余返回 0 表示成功,-1 表示失败,设置 error。
设置阻塞或解除阻塞信号集。
man 3 sigprocmask
#include
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
获取未决信号集。
man 3 sigpending
#include
int sigpending(sigset_t *set);
利用 SIGCHLD 回收子进程
#include
#include
#include
#include
void catch_sig(int num) {
pid_t wpid = waitpid(-1, NULL, WNOHANG);
if (wpid > 0) {
printf("wait child %d ok\n", wpid);
}
}
int main() {
int i = 0;
pid_t pid;
// 在创建子进程之前屏蔽SIGCHLD信号
sigset_t myset, oldset;
sigemptyset(&myset);
sigaddset(&myset, SIGCHLD);
// oldset保护现场,阻塞信号SIGCHLD,得到阻塞前的信号SIGCHLD
sigprocmask(SIG_BLOCK, &myset, &oldset);
for (i = 0; i < 10; ++i) {
pid = fork();
if (pid == 0) {
break;
}
}
if (i == 10) {
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
act.sa_handler = catch_sig;
sigaction(SIGCHLD, &act, NULL);
sigprocmask(SIG_SETMASK, &oldset, NULL);
while (1) {
sleep(1);
}
} else if (i < 10) {
printf("I am %d child, pid = %d\n", i, getpid());
sleep(i);
}
return 0;
}
通过ps -ajx
可以看到,的确杀死了子进程。
个人理解守护进程,不用占用终端,程序可以一直进行下去 ,守护进程一般以 d 结尾,linux 下有 systemd 系统工具来启动守护进程。
创建守护进程步骤:
kill pid
。创建守护进程:
#include
#include
#include
#include
#include
#include
#include
#include
#include
int i = 0;
void print(int num) { printf("i = %d\n", i++); }
int main() {
// 创建子进程,父进程退出
pid_t pid = fork();
if (pid > 0) {
exit(1);
}
// 当会长
setsid();
// 设置掩码
umask(0);
// 切换目录
chdir(getenv("HOME"));
// 关闭文件描述符
// close(1);
// close(2);
// close(3);
// 执行核心逻辑
struct itimerval myit = {{1, 0}, {1, 0}};
setitimer(ITIMER_REAL, &myit, NULL);
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
act.sa_handler = print;
sigaction(SIGALRM, &act, NULL);
while (1) {
sleep(1);
}
// 退出
return 0;
}
// 每隔一秒打印一个数字,为了方便,没有关闭文件描述符
可以看到,守护进程创建成功。
本来还打算用信号和线程写一个睡眠排序,发现好像有点困难,就放弃了。
linux 下的进程就算粗略学习完了,绝知此事要躬行,还有很多要学习的。