PIPE、SIGNAL(day11)
一、管道 管道分为两种: 无名管道 有名管道 无名管道用于具有亲缘关系的进程间通讯。无名管道是单工的。 有内核管理的一块内存空间。 使用管道,系统提供了pipe(2) #include <unistd.h> int pipe(int pipefd[2]); 功能:创建管道 参数: pipefd[2]:用于返回管道的两端。pipefd[0]指向管道的读端。 pipefd[1]指向管道的写端。 返回值: 0 成功。 -1 错误 errno被设置 使用管道实现两个进程间的通讯。 步骤: (一)、父进程创建管道 (二)、父进程创建子进程(子进程继承了父进程的文件描述符) (三)、父进程负责的工作 1、关闭读端 2、通过管道的写端文件描述符,写数据到管道空间。 3、阻塞等待子进程的结束。子进程结束的时候,收尸 (四)、子进程负责的工作 1、关闭写端 2、从管道中读取数据 3、将读取到的数据,输出到显示器 4、结束进程。 代码的实现 pipe.c 有名管道 有名管道其实是一个文件,这个文件只能用于两个进程间通讯的桥梁。不存储任何数据内容。 如何创建一个有名管道的文件。使用mkfifo(3) #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 功能:创建一个有名管道文件 参数: pathname:指定了有名管道文件的名字 mode:指定了管道文件的权限 mode & ~umask 返回值: 0 成功 -1 错误 errno被设置 编码实现管道文件的创建。 文件名字由命令行第一个参数传入,权限为0664. 代码参见 mkfifo.c 编写代码向有名管道写数据。代码参见PA.c 编写代码实现从有名管道读取数据 代码参见PB.c 进程结束了。 尝试将管道添加到psh中 二、信号的基础 什么是信号? 信号就是软中断。 软中断就是软件模拟的中断机制。 中断又是什么? 正常的执行流程、中断处理程序 正常的执行流程、信号处理程序是两条执行路线,但是属于同一个进程。 系统为我们提供了哪些信号呢? kill -l 信号有名字和编号。 信号的产生到消失的过程。 信号的产生、信号阻塞、信号递达进程、信号处理 信号的未决状态 就是信号产生了,但是信号还没有被进程处理,这期间,信号的状态为未决状态。 进程可以设置对信号的阻塞。 三、改变信号的处理函数 进程对信号的处理有默认动作。默认处理是终止进程。 除此之外,还有两种 忽略信号 用户自定义 进程从父进程继承信号处理函数。 SIG_DFL 默认 SIG_IGN 忽略 doit 用户自定义的信号处理函数 系统提供了signal(2) 用来改变信号的处理函数 #include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 功能: 参数: signum:指定了信号的编号 handler:指定了signum信号的处理函数 SIG_IGN, SIG_DFL, 用户自定义函数 返回值: SIG_ERR 错误 返回的是旧的信号处理函数的地址 typedef void (*sighandler_t)(int); 举例说明 编码实现进程忽略2号信号 代码参见signal2.c 编码实现进程对2号信号的处理采用用户自定义的函数。 代码参见 signal_2u.c 四、信号的产生 信号产生的三种形式: 1、硬件产生信号 ctrl+c ctrl+\ 2、使用命令为进程发送信号 kill -信号编号 pid 3、使用库函数或者系统调用为进程发送信号 kill(2) #include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig); 功能:给一个进程发送信号 参数: pid:指定了接收信号的进程的pid sig:指定了具体的信号 返回值: 0 成功 -1 错误 errno被设置 举例说明 编写代码实现kill命令的功能。代码参见pkill.c raise(3) #include <signal.h> int raise(int sig); 功能:发送信号给当前进程 参数: sig:指定信号的编号 返回值: 0 成功 非0 错误 举例说明,使用raise给当前进程发送信号 代码参见 raise.c alarm(2) 产生SIGALRM信号 #include <unistd.h> unsigned int alarm(unsigned int seconds); 功能:产生SIGALRM信号,将这个信号发送给当前进程 参数: seconds:指定了闹钟的时间。如果这个参数为0.取消闹钟。 返回值: 返回剩余的没执行的时间值。 举例说明 编写代码实现每一秒钟输出的数字。代码参见 count.c 五、信号阻塞和未决信号 信号集类型 sigset_t 系统对sigset_t 类型进行了封装 #include <signal.h> int sigemptyset(sigset_t *set); 功能:初始化信号集为空,不包含任何信号 参数: set:指定要初始化的信号集 返回值: 0 成功 -1 错误 int sigfillset(sigset_t *set); 功能:初始化信号集为满,包含所有的信号 参数: set:指定要初始化的信号集 返回值: 0 成功 -1 错误 int sigaddset(sigset_t *set,int signum); 功能:添加指定的信号到信号集 参数: set:指定信号集 signum:指定信号 返回值: 0 成功 -1 错误 int sigdelset(sigset_t *set,int signum); 功能:从指定信号集删除指定的信号 参数: set:指定信号集 signum:指定信号 返回值: 0 成功 -1 错误 int sigismember(const sigset_t *set, int signum); 功能:测试信号是否是集合的一员 参数: set:指定信号集 signum:指定信号 返回值: -1 错误 1 是 0 不是 通过sigprocmask(2)设置信号集为进程的屏蔽字。 #include <signal.h> int sigprocmask(int how,const sigset_t *set,\ sigset_t *oldset); 功能:检查或者改变阻塞信号 参数: how: SIG_BLOCK:原来的set和set的并集 SIG_UNBLOCK:将set集合中的信号从当前进程的set中移除。 SIG_SETMASK:将set设置为当前进程的信号屏蔽字 set:新的信号屏蔽字。 oldset:保存进程原来的信号屏蔽字。如果为NULL,不保存。 返回值: 成功 0 -1 错误 举例说明 编写代码实现对2号信号的阻塞。 代码参见 blocked2.c 多次发送2号信号,进程对2号信号阻塞,在解除阻塞的时候,信号处理函数只执行一次,造成了信号的丢失。这样的信号叫不可靠信号。 1~31 34~64 称为可靠信号,不会有信号丢失。 检测进程的未决信号 sigpending(2) #include <signal.h> int sigpending(sigset_t *set); 功能:检测未决信号 参数: set:未决信号掩码被存放在这个集合中 返回值: 成功 0 错误 -1 举例说明 检测进程的未决信号集 代码参见 pending.c 总结: 一 管道 (无名管道 有名管道) 二、信号基础 三、信号的处理 signal(2) 四、信号的产生 五、信号阻塞和未决信号