信号集
由多个信号构成的一个数据集合(可以理解含有多个信号的数组)
信号集合的类型: sigset_t
操作信号集的函数
#include <signal.h>
// 清空信号集内的全部信号,成功返回0,出错返回-1
int sigemptyset(sigset_t *set);
// 设置(添加)全部信号到信号集内,成功返回0,出错返回-1
int sigfillset(sigset_t *set);
// 添加某个信号signum到信号集,成功返回0,出错返回-1
int sigaddset(sigset_t *set, int signum);
// 从信号集删除某个信号signum,成功返回0,出错返回-1
int sigdelset(sigset_t *set, int signum);
// 判断某个信号signum是否在信号集 set 中。
// 在信号集中返回1, 不再返回0,出错返回-1。
int sigismember(const sigset_t *set, int signum);
信号屏蔽sigprocmask(2)
作用:设置屏蔽和解除屏蔽,当前代码段屏蔽信号集中的信号的中断。
#include <signal.h>
/* Prototype for the glibc wrapper function */
成功返回0,出错返回-1,同时设置errno
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
// 参数: how:
SIG_BLOCK 设置当前信号集阻塞信号。
SIG_UNBLOCK 设置当前信号集从阻塞信号中删除。
SIG_SETMASK 设置阻塞信号。
// set 新的信号集
// oldset 返回旧的信号集.
原子操作
示例:取银行存钱
步骤:
张三存钱
1. 银行确定钱数 x = 10000;
2. 查询你之前的钱数 money
3. 计算出最总的结果 result = money + x
4. 修改这个结果。 money = result。
张三的女朋友给张三存钱
1. 银行确定钱数 x = 50;
2. 查询你之前的钱数 money
3. 计算出最总的结果 result = money + x
4. 修改这个结果。 money = result。
sigprocmask 示例
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
void sig_handle(int sig) {
printf("收到信号:%d\n", sig);
}
void do_important_thing(void) {
int ret;
printf("开始做重要的事情\n");
ret = sleep(10);
if (ret != 0)
printf("此事情被中断打断.\n");
printf("结束做重要的事情\n");
}
int main(int argc, char * argv[]) {
sigset_t newset; // 新的信号集
sigset_t oldset; // 用于保存之前的旧的信号集
printf("PID:%d\n", getpid());
signal(SIGINT, sig_handle);
sigemptyset(&newset); // 清空 newset;
sigaddset(&newset, SIGINT); // 将 SIGINT加入信号集合
// 设置屏蔽newset中的信号,如果发生,则处于阻塞状态
sigprocmask(SIG_BLOCK, &newset, &oldset);
do_important_thing();
// 解除阻塞,恢复之前的屏蔽状态。
sigprocmask(SIG_SETMASK, &oldset, NULL);
printf("做其他的事情\n");
sleep(10);
printf("程序退出\n");
return 0;
}
sigsuspend 函数
作用:临时设置信号屏蔽字的原子操作。
函数格式
#include <signal.h>
int sigsuspend(const sigset_t *mask);
示例
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
void sig_handle(int sig) {
printf("收到信号:%d\n", sig);
}
void do_important_thing(void) {
sigset_t temp_set;
int ret;
printf("开始做重要的事情\n");
ret = sleep(5);
if (ret != 0)
printf("此事情被中断打断.\n");
// 希望有信号就能够得到及时的响应。
sigemptyset(&temp_set); // 清空
sigsuspend(&temp_set); // 处理中断
ret = sleep(5);
if (ret != 0)
printf("此事情被中断打断.\n");
printf("结束做重要的事情\n");
}
int main(int argc, char * argv[]) {
sigset_t newset; // 新的信号集
sigset_t oldset; // 用于保存之前的旧的信号集
printf("PID:%d\n", getpid());
signal(SIGINT, sig_handle);
sigemptyset(&newset); // 清空 newset;
sigaddset(&newset, SIGINT); // 将 SIGINT加入信号集合
// 设置屏蔽newset中的信号,如果发生,则处于阻塞状态
sigprocmask(SIG_BLOCK, &newset, &oldset);
do_important_thing();
// 解除阻塞,恢复之前的屏蔽状态。
sigprocmask(SIG_SETMASK, &oldset, NULL);
printf("做其他的事情\n");
sleep(10);
printf("程序退出\n");
return 0;
}
abort 函数
abort函数
作用:让程序异常终止。
函数格式
#include <stdlib.h>
void abort(void);
sigaction 函数
作用:代替signal 函数,让信号处理更可靠,并可以获取更多的信号信息,包括信号的发送进程ID等信息。
函数格式
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
进程间通信(IPC)
IPC(Inter-Process Communitation)
进程间通信的进程关系可以是如下几种
- 父子进程
- 拥有相同父进程的两个子进程。
- 无亲缘关系的进程。
相关内容 《APUE》第十五章
UNIX 进程间通信的机制
-
信号
-
管道
- 匿名管道(PIPE)
- 有名管道(FIFO)
-
XSI IPC
- 消息队列(Message Queue)
- 信号量数组(semaphore Array)
- 共享内存(Shared Memory)
管道
管道是Linux 内核中的一段内存,类似于循环队列,一个管道可以存储一段数据信息
创建管道的打开方式
int pipe(int fds[2]);
单工
| 进程1 | ----> | 进程2 |
半双工(同一时刻只能一个方向传递数据)
| 进程1 | -->--<-- | 进程2 |
全双工
+ ---- + +-------+
| 进程1 | -----> | 进程2 |
+ ---- + <------- +-------+
管道的写操作 write(2)
- write(fd[1]) 写管道的1端,写满会阻塞。
管道的读操作 read(2)
- read(fd[0]) 读管道的0端,读空时会阻塞。
管道容量
65536字节,默认是16个页(page),每个页4K。
管道发送的信号
管道读取端关闭,写管道会出发SIGPIPE信号。如果进程忽略此信号。则write(2) 返回-1并设置 errno为 EPIPE。
管道写端关闭,读取是read(2) 返回0.
示例代码
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
void wait_child(int sig) {
int status;
pid_t pid;
pid = wait(&status);
printf("进程 pid: %d已经回收\n", pid);
}
void sig_handle(int sig) {
printf("sig:%d\n", sig);
}
int main(int argc, char * argv[]) {
int fds[2]; // 用户存储管道打开的文件描述符
int ret;
char buf[1024];
pid_t pid;
signal(SIGPIPE, sig_handle);
signal(SIGCHLD, wait_child);
ret = pipe(fds); // 打开管道
if (-1 == ret) {
perror("pipe");
return 1;
}
pid = fork();
if (pid == -1) {
perror("fork");
return 2;
}
if (0 == pid) {
close(fds[1]); // 关闭写端
while(1) {
ret = read(fds[0], buf, 1024);
printf("ret:%d\n", ret);
buf[ret] = 0;
printf("buf:%s\n", buf);
if (0 == strcmp(buf, "exit")) {
close(fds[0]);
return 0;
}
}
}
close(fds[0]); // 关闭读端
while(1) {
printf("请输入文字:");
fflush(NULL); // 清空所有缓冲区。
ret = read(0, buf, 1024); // "abc\n"
buf[ret-1] = 0;
write(fds[1], buf, ret-1);
// if (0 == strcmp(buf, "exit")) {
// close(fds[1]);
// break;
// }
}
printf("程序退出\n");
return 0;
}