《LINUX系统编程》笔记p8
信号集
由多个信号构成的一个数据集合(可以理解含有多个信号的数组)
信号集合的类型: 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 + x4. 修改这个结果。 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;
}