《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 + 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)

进程间通信的进程关系可以是如下几种

  1. 父子进程
  2. 拥有相同父进程的两个子进程。
  3. 无亲缘关系的进程。

相关内容 《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(fd1) 写管道的1端,写满会阻塞。

管道的读操作 read(2)

  • read(fd0) 读管道的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;
}
相关推荐
猪脚踏浪1 小时前
linux 拷贝文件或目录到指定的位置
linux
假如让我当三天老蒯1 小时前
回归基本功!前端的解构赋值、扩展运算符、剩余参数
前端·面试
Lee川1 小时前
Memory 模块深度解析(面试向)
人工智能·面试
大树8817 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠17 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
LDR00617 小时前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
霸道流氓气质17 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush417 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行52017 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz18 小时前
Maven依赖冲突
java·服务器·maven