【APUE】守护进程和高级io

生存期较长的一种进程,在启动时自举,在系统关闭时才终止。

父进程id为0的通常是内核进程,进程1通常是init,内核自举时启动的用户层命令
 
编写规则
1)调用umask将文件创建屏蔽字设置为0。由继承而来的屏蔽字可能会拒绝设置某些权限。
2)调用fork,然后使父进程退出,继承父进程的进程组id,但是会有一个新的id,这样保证这个进程不会作为进程组的组长进程。
3)调用setsid以创建一个新会话
4)将当前工作目录更改为根目录
5)关闭不需要的文件描述符
6)某些守护进程打开/dev/null使其具有文件描述符0,1,2,,这样任何的读写都不会有效果。
 
出错记录的处理
1)内核例程可以调用log函数,用户只要打开然后读/dev/klog设备就可以读取信息
2)大多数用户进程调用syslog函数以产生日志消息
3)在此主机上的一个用户进程,或者通过网络连接到此主机上的其他主机的进程可以将日志消息发送向端口514
 
--------------------------------------------------------------------------------------------
 
  • 高级io
 
非阻塞io
使我们可以调用open read 和write这样的io操作,并使这些操作不会永远阻塞。如果这种操作不能完成,则调用立即出错返回,表示该操作如继续执行将阻塞
对于指定的描述符有两种方法对其指定非阻塞io
1)如果调用open获得描述符,则可指定O_NONBLOCK标志
2)对于已经打开的一个描述符,则可以调用fcnl1,由函数打开O_NONBLOCK文件状态标志。
 
 
 
 
  • STREAMS(流)
是系统提供的构造内核设备驱动程序和网络协议包的一种通用方法,
和标准io中的stream不同
 
流在用户进程和设备驱动之间提供了一条全双工通路,流无需和硬件设备直接会话,流也可以用来构造伪设备驱动程序
 
在流首之下可以压入处理模块,这个可以用ioctl命令实现
任意数量的处理模块都可以压入流。
STREAMS模块是作为内核的一部分执行的,类似于设备驱动程序。
 
可以用文件io函数操作流,也有5个新函数对流进行操作
 
STREAM消息
所有的输入和输出都基于消息。
在用户进程和流首之间,消息有三部分组成:len指定缓冲区中的数据长度,可选的控制信息以及可选择的数据。
 
 
 
将消息写到流中
int putmsg(int fieldes, const struct strbuf *ctlptr, const struct strbuf *databuf, int flag)
int putpmsg(int fieldes, const struct strbuf *ctlptr, const struct strbuf *databuf, int band, int flag)
后者可以为消息指定一个优先级
 
判断一个描述符是不是引用一个流
int isastream(int fieldes);
 
#include"apue.h"
#include<fcntl.h>

int main(int argc, char *argv[])
{
    int i, fd;

    for(i=1; i<argc; i++)
    {
        fd = open(argv[i], O_RDONLY);
        if( isastream(fd) == 0)
        {
            printf("%s : not a stream\n", argv[i]);
        }
        else
        {
            printf("%s : streams device\n", argv[i]);
        }
    }
    exit(0);
}

 

 
结果:
$ ./a.out /dev/tty /dev/fb /dev/null /etc/notd
/dev/tty : not a stream
/dev/fb : streams device
/dev/null : not a stream
/etc/notd : streams device
 
 
 
----------------------------------------------------------------------------
 
io多路转接
从一个描述符读入然后将内容写到另一个描述符中
while((n=read(STDIN_FILENO, buf, BUFSIZ))>0)
if(write(STDOUT_FIFLNO, buf, n) != n)
err_sys("write error");
但是当读多个io时,如果使用阻塞io就有可能长时间阻塞在一个描述符上,而另一个描述符虽有很多数据但是不能及时处理。
 
io多路转接技术
先构造一张关于描述符的列表,然后调用一个函数,直到这些描述符中的一个已经准备好进行io时,该函数才返回。在返回时,告诉进程哪些描述符已经准备好。
 
 
 
  • select和pselect函数
我们告诉内核:
我们所关心的描述符、对于每个描述符我们所关心的状态、愿意等待多长时间
返回时内核告诉我们:
已经准备好的描述符的数量
对于读写湖综合异常这三个状态中的每一个,哪些描述符已经准备好
使用这些返回信息,就可以调用相应的io函数,并且知道这个函数不会阻塞
int select(int maxfdp1, fd_set *readfds, fd_set *writefds, fd_set exceptfds, struct timeval *tvptr)
第一个参数是最大描述符的值+1
中间三个是描述符集
 
该函数可能有三个返回值
-1为出错
0为没有描述符准备好
正数返回值表示已经准备好的描述符数,该值为三个描述符集中已经准备好的描述符数之和。
 
 
 
  • poll函数
虽然可以使用于任何类型的文件描述符,但是还是和STREAMS密切相关
int poll (struct pollfd fdarray[], nfds_t nfds, int timeout)
poll不是为每个状态构造一个描述符集,而是构造一个pollfd数组,每个数组元素指定一个描述符编号以及对其所关心的状态。
元素数由nfds参数说明
 
readv函数和writev函数
ssize_t readv(int fieldes, const struct iovec *iov, int iovcnt);
ssize_t writev(int fieldes, const struct iovec *iov, int iovcnt);
用于一次在函数调用中读写多个非连续缓冲区
第二个参数是指向iovec的一个指针
struct iovec{
    void *iov_base;
    size_t iov_len;
}
 
 
 
 
  • readn和writen函数
管道、fifo以及某些设备,特别是终端,网络和STREAMS设备中读写数据可能会出现一次读写没能读写至需要的字节数的情况
这两个函数多次调用读写函数,并且处理返回值小于要求值的情况,直到读写了需要的数据
ssize_t readn(int fieldes, void *buf, size_t nbytes)
ssize_t writen(int fieldes, void *buf, size_t nbytes)
 
 
 
 
  • 存储映射io
使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当操作缓冲区读写数据时,相当于读写文件中的相应字节。
void *mmap(void *addr,  映射存储区的起始地址
     size_t len,   被映射的文件描述符
     int prot,     对映射存储区的保护要求
     int flag,     影响映射区的多种属性
     int fieldes, 
     iff_t off)
 
 
posted @ 2012-09-21 12:21  w0w0  阅读(277)  评论(0编辑  收藏  举报