wr_index = 824
wr_index = 2226
wr_index = 3566
wr_index = 3620
wr_index = 5022
wr_index = 6424
wr_index = 7662
wr_index = 7818
wr_index = 9220
wr_index = 10622
wr_index = 11758
wr_index = 12016
wr_index = 13418
wr_index = 14820
wr_index = 15854
wr_index = 16451
wr_index = 16517
で、このコールバックはどのタイミングでどこから呼ばれるのだろう?と思ってstraceしてみたらこんな感じでした。
sendto(3, "GET / HTTP/1.1\r\nHost: www.yahoo."..., 54, MSG_NOSIGNAL, NULL, 0) = 54
poll([{fd=3, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
poll([{fd=3, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
poll([{fd=3, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 1000) = 1 ([{fd=3, revents=POLLIN|POLLRDNORM}])
poll([{fd=3, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 1 ([{fd=3, revents=POLLIN|POLLRDNORM}])
recvfrom(3, "HTTP/1.1 200 OK\r\nServer: nginx\r\n"..., 16384, 0, NULL, NULL) = 1402
write(2, "wr_index = 824\n", 15wr_index = 824
) = 15
poll([{fd=3, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 1000) = 1 ([{fd=3, revents=POLLIN|POLLRDNORM}])
poll([{fd=3, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 1 ([{fd=3, revents=POLLIN|POLLRDNORM}])
recvfrom(3, "e:inherit;font:100%;}\npre,code,k"..., 16384, 0, NULL, NULL) = 1402
write(2, "wr_index = 2226\n", 16wr_index = 2226
) = 16
poll([{fd=3, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 1000) = 1 ([{fd=3, revents=POLLIN|POLLRDNORM}])
poll([{fd=3, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 1 ([{fd=3, revents=POLLIN|POLLRDNORM}])
recvfrom(3, "http://auctions.yahoo.co.jp/\" al"..., 16384, 0, NULL, NULL) = 1402
write(2, "wr_index = 3566\n", 16wr_index = 3566
) = 16
"poll" というシステムコールが使われていました。
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/poll.2.html
poll() は select(2) と同様の仕事を行う、つまり、ファイルディスクリプタ集合のいずれか一つが I/O を実行可能な状態になるのを待つ。
ということだそうです。
まとめ
- libcurlのCURLOPT_WRITEFUNCTIONコールバックは1リクエストで複数回呼ばれる。
- pollシステムコールが発行されたタイミングで呼ばれる
- libcurlはHTTPコンテンツを受信する際にpollシステムコールを使っている。
実験に使ったソースコード
/*
* Simple curl application to do HTTP GET
* forked from http://www.ibm.com/developerworks/jp/opensource/library/os-curl/#N100C4
*/
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#define MAX_BUF 65536
char wr_buf[MAX_BUF+1];
int wr_index;
/*
* callback function (called many times in one request)
*/
size_t mycallback(void *buffer, size_t size, size_t num_of_members, void *userp)
{
int segsize = size * num_of_members;
/* Check to see if this data exceeds the size of our buffer. If so,
* set the user-defined context value and return 0 to indicate a
* problem to curl.
*/
if (wr_index + segsize > MAX_BUF) {
*(int *)userp = 1;
return 0;
}
/* Copy the data from the curl buffer into our buffer */
memcpy((void *)&wr_buf[wr_index], buffer, (size_t)segsize);
wr_index += segsize; /* Update the write index */
wr_buf[wr_index] = '\0'; /* Null terminate the buffer */
fprintf(stderr, "wr_index = %d\n", wr_index);
return segsize; /* Return the number of bytes received, indicating to curl that all is okay */
}
int main(int argc, char *argv[])
{
char *url;
CURL *curl;
CURLcode ret;
int wr_error;
if (argc < 2) {
fprintf(stderr, "argument not given\n");
return 1;
}
url = argv[1];
wr_error = 0;
wr_index = 0;
/* First step, init curl */
curl = curl_easy_init();
if (!curl) {
printf("couldn't init curl\n");
return 0;
}
/* Tell curl the URL of the file we're going to retrieve */
curl_easy_setopt(curl, CURLOPT_URL, url);
/* Tell curl that we'll receive data to the function mycallback, and
* also provide it with a context pointer for our error return.
*/
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&wr_error);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, mycallback);
/* Allow curl to perform the action */
ret = curl_easy_perform(curl);
if (ret == 0) {
/* Emit the page if curl indicates that no errors occurred */
printf("%s\n", wr_buf);
} else {
printf("ret = %d (write_error = %d)\n", ret, wr_error);
}
curl_easy_cleanup(curl);
return 0;
}