Unix Network Programming Vol2 Part Two

UNIX网络编程 卷2:进程间通信(第2版)

已发布 2017-08-16 11:10:04

第二部分 消息传递

第4章 管道和FIFO

  • 管道
    • 所有unix都提供管道
    • 由pipe函数创建,提供单向数据流
    • 函数返回两个文件描述符,前者打开来读,后者打开来写
    • fork调用后,在父子进程间实现单向通信
    • shell管道也是使用的这个,把读出端复制到相应进程的标准输入,把写入端复制到相应进程的标准输出
    • 如果需要双工通信,则需要创建两个管道
    • 每条数据都要穿越用户-内核接口两次
    • waitpid:子进程终止时,内核给父进程产生一个SIGCHLD信号,父进程默认行为是忽略。如果父进程没有调用waitpid来获取子进程(僵尸进程)的终止状态而直接终止的话,子进程将成为托孤给init进程的孤儿进程,内核将向init进程发送另外一个SIGCHLD信号,init进程将随后取得该僵尸进程的终止状态
    • 全双工管道
      • SVR4的pipe函数、许多内核都提供的socketpair函数
    • popen函数
      • FILE *popen(const char *command, const char *type)
      • PATH环境变量可用于定位command
      • popen在调用进程和所指定的命令之间创建一个管道
      • type参数如果为“r”,那么调用进程读comand参数的标准输出;反之同理
      • int pclose(FILE *stream)
      • pclose函数关闭由popen创建的标准IO流,等待终止并返回终止状态
  • FIFO
    • 无法在无亲缘关系的两个进程间创建一个管道并将它用于IPC通道(不考虑描述符传递)
    • 每个FIFO有一个路径名与之关联,故也称有名管道(named pipe)
    • 由 mkfifo 函数创建 int mkfifo(const char *pathname, mode_t mode); mode是权限位
    • 创建后还需要调用open,并提供读写模式
    • 半双工
    • 以读模式打开FIFO将阻塞,不正当的操作顺序将导致父子进程的死锁
    • write的原子性
    • 只能用在本地文件系统上,不能用在通过NFS安装的文件系统上
      • 只能在单台主机上使用的IPC形式
    • 只发送请求不读取响应,可进行拒绝服务型攻击
      • 超时时钟
      • 子进程限制
  • 内核为管道和FIFO维护一个访问计数器,存储访问同一个管道或FIFO的打开着描述符的格式。有了计数器,客户或服务器就能成功unlink,只要计数器不为0,内核就不会删除引用的管道或FIFO。而其他形式的IPC则不存在这样的计数器。
  • 管道和FIFO的额外属性
    • 设置成非阻塞
      • 调用open时指定O_NONBLOCK标志
      • 如果一个描述符已经打开,那么可以调fcntl启用O_NONBLOCK标志(管道只有这种方式,没有open调用)
    • 系统加于管道和FIFO的唯一限制
      • OPEN_MAX 一个进程在任意时刻打开的的最大描述符数 Posix要求至少16
        • 可通过执行ulimitlimit(C shell)从shell中修改
        • 可通过调用 setrlimit 函数从一个进程中修改
      • PIPE_BUF 可原子地写往一个管道或FIFO的最大数据量 Posix要求至少512
    • 。。。
  • 字节流与消息
    • 消息边界如何区分
      • 带特殊终止序列,例如换行符、一个回车符加上换行符(CR/LF)
      • 显式长度
      • 每次连接表示一个记录,例如HTTP1.0
    • 也可构建更为结构化的消息(加上长度和优先级两个属性)

第5章 Posix消息队列

  • 消息队列可认为是一个消息链表
  • 在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达
  • 随内核的持续性
  • Posix消息队列和System V消息队列的异同点
    • 不同点
      • 优先级上的特性
        • Posix: 总是返回最高优先级
        • System V:可返回任意指定优先级
      • 消息对线程的hook(仅Posix)
    • 共有属性
      • 具有一个无符号整数优先级(Posix)或者长整数类型(System V)
      • 消息的数据部分长度
      • 数据本身
  • 一些API
    • mq_open
    • mq_close
      • 一个进程终止时,它的所有打开着的消息队列都自动关闭。但其消息队列并不从系统中删除
    • mq_unlink
      • 从系统中删除用作mq_open第一个参数的某个name
    • mq_getattr
    • mq_setattr
    • mq_send
    • mq_receive
    • mq_notify
      • int mq_notify(mqd_t mqdes, const struct sigevent *notification);
      • 两种方式
        • 产生一个信号
        • 创建一个线程来执行一个指定的函数
      • 几个规则
        • notification非空,当前进程被注册为接受该队列的通知
        • notification为空指针,如果当前进程正被注册为接受所指定队列的通知,那么已存在的注册将被注销
        • 任何一个时刻,只有一个进程可以被注册为接收某个给定队列的通知
        • 只有不存在正被阻塞的mq_receive调用时,通知才会发出
  • 消息队列的限制
    • mq_mqxmsg 队列中的最大消息数
    • mq_msgsize 给定消息的最大字节数
    • MQ_OPEN_MAX 一个进程能同时拥有的打开着的消息队列的最大数目
    • MQ_PRIO_MAX 任意消息的最大优先级加1
  • Posix 实时信号
    • 可划分为两个大组
      • 值在 SIGRTMIN 和 SIGRTMAX 之间(包括两者)的实时信号,Posix要求至少提供 RTSIG_MAX 种实时信号,该常值的最小值为8
      • 所有其他信号:ISGALRM、ISGINT、SIGKILL等
    • 如果需要实时行为,我们必须使用SIGRTMIN和SIGRTMAX直接的新的实时信号,而且在安装信号处理程序时必须给sigaction指定SA_SIGINFO标志
    • 实时行为
      • 信号是排队的。产生几次,就递交几次。
      • 当有多个SIGRTMIN到SIGRTMAX范围内的解阻塞信号排队时,值较小的信号先于值较大的信号递交
      • 当某个非实时信号递交时,传递给它的信号处理程序的唯一参数是该信号的值。实时信号比其他信号携带更多的信息。
      • 一些新函数定义成使用实时信号工作。例如sigqueue函数用于替代kill函数向某个进程发送一个信号
    • 实时信号的产生
      • SI_ASYNCIO 异步IO请求
      • SI_MESGQ 有一个消息被仿制到某个空消息队列中时产生
      • SI_QUEUE 信号由sigqueue函数发出
      • SI_TIMER 信号由使用timer_settime函数设置的某个定时器的到时产生
      • SI_USER 信号由kill函数发出
  • 使用内存映射IO实现Posix消息队列
    • 内存映射IO+Posix互斥锁+条件变量
    • 。。。

第6章 System V 消息队列

System V消息队列与Posix消息队列类似。新的应用程序应考虑使用Posix消息队列,不过大量的现有代码使用SystemV消息队列。Posix消息队列缺失的主要特性是从队列中读出指定优先级的消息的能力。这两种消息队列不使用真正的描述符,从而造成在消息队列上使用select或poll的能力。

comments powered by Disqus