当前位置:   article > 正文

高级I/O函数

bzero(&address, sizeof(address));

1、pipe函数:

    pipe函数用来创建一个管道,以实现进程之间通信。

#include<unistd.h>

int pipe(int fd[2]);

    pipe函数的参数是一个包含两个int 类型整数的数组指针。该函数成功时返回0,并将一对打开的文件描述符值填入其参数指向的数组。如果失败,则返回-1并设置errno。

    pipe函数创建的这两个文件描述符fd[0],fd[1]分别构成管道的两端,往fd[1]写入的数据可以从fd[0]读出。并且fd[1]只能用于往管道中写入数据,fd[0]只能用于从管道读出数据。而不能反过来使用。若是想实现双向通信,就必须要使用两个管道。

    管道内部传输的数据是字节流,这和TCP字节流的概念相同。但二者又有细微的区别。应用程程序能往一个TCP连接中写入多少字节的数据,取决于对方的接受通告的大小和本端的拥塞窗口的大小。

    socket的基础API中有一个skcketpair函数。它能够方便地创建双向管道。定义如下:

#include<sys/types.h>

#include<sys/socket.h>

int socketpair(int domain,int type,int protocol,int fd[2]);

    socketpair前三个参数的含义与socket系统调用的三个参数完全相同,但domain只能使用unix本地与协议族af——unix,因为我们仅能在本地使用这个双向管道。最后一个参数则和pipe系统调用的参数一样,只不过socketpair创建的这对文件描述符都是即可读有可写的。socketpair成功时返回0失败时返回-1并设置errno。

 2、readv函数和writefv函数。

    readv函数将数据从文件描述符读到分散地内存块中,即分散读:writev函数将多块分散的内存数据一并读写入文件描述符,即集中写。定义如下:

#include<sys/uio.h>

ssize_t readv(int fd,const struct iovec* vector,int count);

ssize_t writev(int fd,const struct iovec* vector,int count);

    fd是被操作的目标文件描述符。vector参数类型是iovec结构数组。我们再第五章讨论过了结构体iovec,该结构体描述一块内存区。count参数式vector数组的长度,既有多少块内存数据需要从fd读出或写到fd。readv和writev在成功时返回读出/写入fd字节数,失败则返回-1并设置errno。

 2、dup函数和dup2函数。

    我们希望把标准输入重定向到一个文件,或者把标准输出重定向到一个网络连接。还可以通过下面的用与复制文件的dup或者dup2函数来实现:

    #include<unistd.h>

int dup(int file_descroptor);

int dup2(int file_descriptor);

    dup函数创建一个新的文件描述符,该描述符和原有文件描述符file_descriptor指向相同的文件,管道,或者网络连接。并且dup返回的文件描述符总是去系统当前可用的最小的整数值。dup2和dup相似,不过他将返回一个不小于file_descriptor_two的整数值。dup和dup2系统调用失败时则返回-1并设置errno。

    通过dup和dup2创建的文件描述符并不继承元文件描述符的属性,比如close-on-exec和non-blocking等。。。

3、什么事CGI服务器?

    在互联网行业中有一个词叫CGI,这个是服务器中的专业术语,所以很多人不知道CGI是什么。CGI是一个用于定Web服务器与外部程序之间通信方式的标准,使得外部程序能生成HTML、图像或者其他内容,而服务器处理的方式与那些非外部程序生成的HTML、图像或其他内容的处理方式是相同的。因此,CGI程序册仅使你能生成表态内容而能生动态内容。使用CGI的原因在于它是一个定义良好并被广泛支持的标准,没有CGI就不可能实现动态的Web页面,除非使用一些服务器中提供的特殊方法(如今,也有除CGI之外的其他技术逐渐在成为标准)。

    在互联网行业中有一个词叫CGI,这个是服务器中的专业术语,所以很多人不知道CGI是什么。CGI是一个用于定Web服务器与外部程序之间通信方式的标准。

CGI 服务器原理:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# include <sys/socket.h>
# include <netinet./ in .h>
# include <arpa/inet.h>
# include <assert.h>
# include <stdio.h>
# include <unistd.h>
# include <stdlib.h>
# include <errno.h>
# include <string.h>
int  main( int  argc,char * argv[])
{
     if (argc<= 2 )
     {
         printf( "usage: %s ip_address port_num " ,basename(argv[ 0 ]));
         return  1 ;
     }
     const  char * p = argv[ 1 ];
     int  port = atoi(argv[ 2 ]);
 
     struct sockaddr_in address;
     bzero(&address,sizeof(address));
 
     address.sin_family=AF_INET;
     inet_pton(AF_INET,ip,&address.sin_addr);
     address.sin_port=htons(port);
 
     int  sock=socket(PF_INET,SOCK_STREAM, 0 );
 
     assert(sock>= 0 );
     int  ret=bind(sock,(struct sockaddr*)&address,sizeof(address));
 
     assert(ret!=- 1 );
     ret=listen(sock, 5 );
 
     struct sockaddr_in client;
 
     socklen_t client_addrlength = sizeof(client);
     int  connfd=accept(sock,(struct sockaddr*)&client,sizeof(client));
 
     if (connfd< 0 )
     {
         printf( "errno is %d " ,errno);
     }
     else
     {
         close(STDOUT_FILENO);
         dup(connfd);
         printf( "abcd\n" );
         close(connfd);
     }
     close(sock);
     return  0 ;
}

    在代码清单中,我们先关闭标准输出文件描述符STDOUT_FILENO(其值是1)然后赋值socket文件描述符connfd。因为dup总是返回系统中最小的可用文件描述符,所以他的返回值实际上是1,即之前关闭的标准输出文件描述符的值。这样一来,服务器输出到标准输出的内容(abcd)就会直接发送到与客户连接对应的socket上,因此printf调用的输出将被客户端得到。这就是CGI服务器的基本工作原理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# include <sys/socket.h>
# include <netinet./ in .h>
# include <arpa/inet.h>
# include <assert.h>
# include <stdio.h>
# include <unistd.h>
# include <stdlib.h>
# include <errno.h>
# include <string.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <fcntl.h>
 
#define buffer_size   1024 ;
 
static  const  char * status_line[ 2 ]=( "200 ok" , "50 internet"  );
 
int  main( int  argc,char * argv[])
{
     if (argc<= 3 )
     {
         printf( "usage: ip_address port_num\n" );
         return  1 ''
     }
     const  char * ip=argv[ 1 ];
     int  port=atoi(argv[ 2 ]);
     .................
}

3、sendfile函数:

    sendfile函数在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,这种被称为零拷贝。sendfile函数定义如下:

#include<sys/sendfile.h>

ssize_t sendfile(int out_fd,int in_fd,off_t* offser,size_t count);

int fd 参数是带读出内容的文件描述符,out_ fd参数是待写入内容的文件描述符。offset参数指定从读入文件流的哪个位置开始读,如果为空,则使用读入文件默认的起始位置count参数指定在文件描述符in_fd 和 out_fd 之间传输的字节数。sendfile成功时返回传输的字节数,失败时返回-1,并设置errno。该函数的man手册明确指出,in_fd必须是一个支持类似mmap函数的文件描述符,即他必须指向真实的文件,不能是socket和管道:而out_fd则必须是一个socket。由此可见,sendfile几乎是专门为在网络上传输文件而设计的。

如下,利用sendfile函数将服务器上的一个文件传送给客户端。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# include <sys/socket.h>
# include <netinet/ in .h>
# include <arpa/inet.h>
# include <assert.h>
# include <stdio.h>
# include <unistd.h>
# include <stdlib.h>
# include <errno.h>
# include <string.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <sys/sendfile.h>
 
int  main( int  argc,char * argv[])
{
     if (argc<= 3 )
     {
         printf( "usage : %s ip_address port_file file" );
         return  1 ;
     }
     const  char * ip=argv[ 1 ];
     int  port=atoi(argv[ 2 ]);
     const  char * file_name=argv[ 3 ];
     int  filefd=open(file_name,O_RDONLY);
     assert(filefd> 0 );
     struct stat stat_buf;
     fstat(filefd,&stat_buf);
 
     struct sockaddr_in address;
     bzero(&address,sizeof(address));
     address.sin_family=AF_INET;
     inet_pton(AF_INET,ip,&address.sin_addr);
     address.sin_port=htons(port);
 
     int  sock= socket(PF_INET,SOCK_STREAM, 0 );
     assert(sock>= 0 );
 
     int  ret=bind(sock,(struct sockaddr* )&address,sizeof(address));
 
     assert(ret!=- 1 );
 
     ret=listen(sock, 5 );
     assert(ret!=- 1 );
 
     struct sockaddr_in client;
     socklen_t client_addrlength=sizeof(client);
 
     int  connfd=accept(sock,(struct sockaddr*)&client,&client_addrlength);
     if (connfd< 0 )
     {
         printf( "errno is : %d \n" ,errno);
     }
     else
     {
         sendfile(connfd,filefd,NULL,stat_buf.st_size);
         close(connfd);
     }
     close(sock);
     return  0 ;
 
}

在代码中,我们将目标文件作为第三个参数传递给服务器程序,客户telnet到该服务器上即可获得该文件。

4、mmap函数和munmao函数。

    mmap函数用于申请一段内存空间。我们可以将这段内存作为进程之间通信的共享内存,也可以将文件直接映射到其中。munmap函数则释放有mmap创建的这段内存空间。他们的定义如下:

1
2
3
# include <sys/mman.h>
void  * mmap( void  * start ,size_t length,  int  prot,  int  flags,  int  fd, off_t offset);
int  munmap( void  * start,size_t  length);

start参数允许用户使用某个特定的地址作为这段内存的起始地址。如果他被指为NULL,则系统自动分配一个地址。length参数指定内存段的长度。prot参数用来设置内存的访问权限。它可以取以下几个值的按位或:

PROT_READ,内存段可读

PROT_WRITE,内存段可写

PROT_EXEC,内存段可执行

PROT_NONE,内存段不能被访问

flags参数控制内存段内容被修改后程序的行为。它可以被设置为下标中的某些值的按位或。

麦库截图20171029105433118.jpg 

fd参数是被映射文件对应的文件描述符。它一般通过open系统调用获得。offset参数设置从文件的何处开始映射(对于不需要读入整个文件的情况)。

    mmap函数成功时返回指向目标内存区域的指针,失败则返回MAP_FAILED(void * )-1)并设置errno。munmap函数成功时返回0,失败则返回-1并设置errno。

我们将在以后,在讨论如何利用mmap函数实现进程间的共享内存。

5、solice函数:

splice函数用于在两个文件描述符之间移动数据,也是零拷贝操作。splice函数定义如下:

#include<fcnt1.h>

ssize_t splice(int fd_in , loff_t * off_in  , int fd_out ,  loff_t * off_out , size_t len, unsigned int flages );

fd_in 参数是待输入数据的文件描述符。如果fd_in 是一个管道文件描述符,那么off_in参数必须设置为NULL。如果fd_in 不是一个管道文件描述符(比如scoket),那么off_in表示从输入数据流的何处开始读取数据。此时,若off_in 被设置为NULL,则表示从输入数据流的当前偏移位置读入;若off_inn部位NULL,则它将指出具体的偏移位置,fd_out/off_out参数的含义与fd_in/off_in相同,不过用于输出数据流。len参数指定移动数据长度;flags参数则控制数据如何移动,它可以被设置为下标内容中的某些值的按位或。

麦库截图20171129111005011.jpg 

使用splice函数时,fd_in和fd_out必须至少有一个是管道文件描述符。splice函数调用成功时返回移动字节的数量。他可能返回0,表示没有数据需要流动,这发生在管道中读取数据(fd_in是管道文件描述符)而该管道没有被写入任何数据时。splice函数失败时则返回-1并设置一个errno。常见的errno如表:

麦库截图20171129111300855.jpg 

下面我们使用splice函数来实现一个零拷贝的回射服务器,它将客户端发送的数据原样返回给客户端,具体实现如下:

 

                                      

 

转载于:https://www.cnblogs.com/yjds/p/8597331.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/721846
推荐阅读
相关标签
  

闽ICP备14008679号