《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(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;
}
相关推荐
正在努力的小河7 小时前
Linux并发与竞争
linux·运维·服务器
三坛海会大神5557 小时前
正则表达式与grep文本过滤详解
linux·运维·正则表达式
fuyongliang1237 小时前
Linux 正则表达式与grep命令
服务器·数据库·mysql
悠哉悠哉愿意9 小时前
【数学建模学习笔记】数据标准化
笔记·学习·数学建模
软件测试媛9 小时前
14:00面试,15:00就出来了,问的问题过于变态了。。。
面试·职场和发展
码农hbk9 小时前
Linux signal 图文详解(二)信号发送
linux·服务器
東雪蓮☆10 小时前
Shell 编程 —— 正则表达式与文本处理实战
linux·运维·服务器
叁仟叁佰10 小时前
Shell脚本编程:函数、数组与正则表达式详解
运维·服务器·网络·chrome·正则表达式
≮傷£≯√11 小时前
C语言线程之死锁
c语言·线程·系统编程