我正在尝试使用生产者/消费者问题,pthreads和互斥/条件变量创建一个简单的服务器。我的服务器接受端口号,要创建的线程数,缓冲区应该有多大,以及它应该做什么类型的调度。
虽然我可以在我的缓冲区大小为1的情况下通过测试(我的作业队列只包含一个任务),但我的缓冲区大小大于1的测试失败。我不知道为什么。
代码相当长,所以我会发布我觉得更多相关片段,但如果被问到,我会完整地发布代码。再次,这不是一切。
Main(包括生产者)从客户端获取连接器描述符,并将其传递给“requestHandle”,后者又从connfd获取信息并将其存储到数据结构中。然后它将数据结构放在“队列”上 - 在我的情况下,我选择一个二叉树,使用不同的排序技术(FIFO =先进先出,先是SFNF =最短文件名,先是SFF =最小文件)。
有三个相关文件:
失败的是什么:
当尝试运行缓冲区大小> 1的代码时(即,我可以在队列中放置多个项目),服务器似乎崩溃了。以下是在服务器上运行的示例测试,如下所示:
class Locks(ServerTest):
name = "locks"
description = "many concurrent requests to test locking"
threads = 8
buffers = 16
schedalg = "FIFO"
num_clients = 20
loops = 20
requests = ["/home.html", "/output.cgi?0.3"]
def many_reqs(self):
for i in range(self.loops):
for request in self.requests:
conn = httplib.HTTPConnection("localhost", self.port, timeout=8)
if self.quiet_get(conn, request):
self.client_run(conn)
def run(self):
serverProc = self.run_server(threads=self.threads, buffers=self.buffers, schedalg=self.schedalg)
clients = [threading.Thread(target=self.many_reqs) for i in range(self.num_clients)]
for client in clients:
client.start()
for client in clients:
client.join()
serverProc.kill()
self.done()
运行上述测试会产生以下结果(除此之外,打印出一百万次):
Client failed with error: timed out
Client failed with error: [Errno 104] Connection reset by peer
unable to send request to server. it may have crashed.
server.c global vars:
//root of "queue"
TREE_NODE *root;
//lock
pthread_mutex_t *m;
//Cond. v for queue is full
pthread_cond_t *full;
//cond. v for queue is empty
pthread_cond_t *empty;
//total buff size
int buff_sz;
//num nodes used (<= buff_sz)
int num_used;
//schedule_type: 0 = FIFO, 1 = SFNF, 2 = SFF)
int sched_num;
主要在server.c(也是生产者线程)
int main(int argc, char *argv[])
{
int listenfd, connfd, port, num_threads, clientlen;
char *sched_type = malloc(8096);
struct sockaddr_in clientaddr;
getargs(&port, &num_threads, &buff_sz, sched_type, argc, argv);
if (strcmp(sched_type, "FIFO") == 0) sched_num = 0;
if (strcmp(sched_type, "SFNF") == 0) sched_num = 1;
if (strcmp(sched_type, "SFF") == 0) sched_num = 2;
//initial set up
root = NULL;
num_used = 0;
m = malloc(sizeof(pthread_mutex_t));
pthread_mutex_init(m, NULL);
full = malloc(sizeof(pthread_cond_t));
pthread_cond_init(full, NULL);
empty = malloc(sizeof(pthread_cond_t));
pthread_cond_init(empty, NULL);
//
// Create some threads...
//
int i;
void *argum = malloc(sizeof(void *));
for(i = 0; i < num_threads; i++) {
pthread_t *pthread = malloc(sizeof(pthread_t));
int err_check = pthread_create (pthread, NULL, thread_handle, argum);
assert(err_check == 0);
}
listenfd = Open_listenfd(port);
while (1) {
clientlen = sizeof(clientaddr);
connfd = Accept(listenfd, (SA *)&clientaddr, (socklen_t *) &clientlen);
//
// In general, don't handle the request in the main thread.
// Save the relevant info in a buffer and have one of the worker threads
// do the work. However, for SFF, you may have to do a little work
// here (e.g., a stat() on the filename) ...
//
//PLACE LOCK
pthread_mutex_lock(m);
//check condition if queue full, T = wait on full
while (buff_sz <= num_used) pthread_cond_wait(full, m);
pthread_mutex_unlock(m);
requestHandle(connfd, sched_num);
pthread_mutex_lock(m);
num_used++;
//signal empty to wake sleeping threads
pthread_cond_signal(empty);
//unlock
pthread_mutex_unlock(m);
}
}
server.c中的消费者线程:
void *thread_handle(void *ptr){
while(1){
//place lock
pthread_mutex_lock(m);
//check condition if empty, T = wait on empty
while (num_used < 1) pthread_cond_wait(empty, m);
//dequeue
runRequest(sched_num);
num_used--;
//signal full
pthread_cond_signal(full);
//unlock
pthread_mutex_unlock(m);
}
return NULL;
}//end thread_handle
在request.c中排队函数
// handle a request
void requestHandle(int fd, int sched_num){ // enqueue and save stuff
struct req_info *job = malloc(sizeof(struct req_info));
job->fd = fd;
struct stat sbuf;
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
char filename[MAXLINE], cgiargs[MAXLINE];
rio_t rio;
Rio_readinitb(&rio, fd);
Rio_readlineb(&rio, buf, MAXLINE);
sscanf(buf, "%s %s %s", method, uri, version);
printf("%s %s %s\n", method, uri, version);
job->method = strdup(method);
job->uri = strdup(uri);
job->version = strdup(version);
if (strcasecmp(method, "GET")) {
requestError(fd, method, "501", "Not Implemented", "CS537 Server does not implement this method");
return;
}
requestReadhdrs(&rio);
job->stat_rio = rio;
job->is_static = requestParseURI(uri, filename, cgiargs);
job->filename = strdup(filename);
job->arguments = strdup(cgiargs);
if (stat(filename, &sbuf) < 0) {
requestError(fd, filename, "404", "Not found", "CS537 Server could not find this file");
return;
}
job->sbuf = sbuf;
job->filename_len = (int)(strlen(filename));
struct stat *file_stat = malloc(sizeof(struct stat));
assert((stat(job->filename, file_stat)) == 0);
//save file size!
job->size = (int)(file_stat->st_size);
//create new node
TREE_NODE *temp = malloc(sizeof(TREE_NODE));
temp->job_node = job;
temp->left = NULL;
temp->right = NULL;
//now add node!
int done = 0;
TREE_NODE *curr = root;
TREE_NODE *prev = NULL;
if (root == NULL){
root = temp;
done = 1;
}
//FIFO: Add to right
else if (sched_num == 0){
while (done != 1){
if (curr->right == NULL){
curr->right = temp;
done = 1;
}
curr = curr->right;
}//end while
}//end FIFO
//SFNF: Shortest file name first
else if (sched_num == 1){
while (done != 1){
if (curr == NULL){
curr = temp;
if ((prev->job_node)->filename_len <= (temp->job_node)->filename_len) prev->right = temp;
else prev->left = temp;
done = 1;
}
else if ((curr->job_node)->filename_len <= (temp->job_node)->filename_len){
prev = curr;
curr = curr->right;
}
else {
prev = curr;
curr = curr->left;
}
}//end while
}//end SFNF else if
//SFF: Smallest file first
else {
while (done != 1){
if (curr == NULL){
curr = temp;
if ((prev->job_node)->size <= (temp->job_node)->size) prev->right = temp;
else prev->left = temp;
done = 1;
}
else if ((curr->job_node)->size <= (temp->job_node)->size){
prev = curr;
curr = curr->right;
}
else {
prev = curr;
curr = curr->left;
}
}//end while
}//end else
}
request.c中的队列函数
//FOR THREADS
void runRequest(int sched_num){ // dequeue and run
struct req_info *job;
TREE_NODE *prev = NULL;
TREE_NODE *child = root;
TREE_NODE *temp = root;
int done = 0;
//FIFO: take root
if (sched_num == 0){
if(root == NULL){
printf("SHIT HAS HIT THE FAN\n");
}
root = root->right;
done = 1;
}
//SFNF & SFF: take node all the way left
else {
while (done != 1){
if (child->left == NULL){
if (child->right == NULL){
temp = child;
prev->left = NULL;
done = 1;
}
else {
temp = child;
prev->left = child->right;
done = 1;
}
}//end if
prev = child;
child = child->left;
}//end SFNF/SFF while
}//end else
job = temp->job_node;
if (job->is_static) {
if (!(S_ISREG((job->sbuf).st_mode)) || !(S_IRUSR & (job->sbuf).st_mode)) {
requestError(job->fd, job->filename, "403", "Forbidden", "CS537 Server could not read this file");
return;
}
requestServeStatic(job->fd, job->filename, (job->sbuf).st_size);
} else {
if (!(S_ISREG((job->sbuf).st_mode)) || !(S_IXUSR & (job->sbuf).st_mode)) {
requestError(job->fd, job->filename, "403", "Forbidden", "CS537 Server could not run this CGI program");
return;
}
requestServeDynamic(job->fd, job->filename, job->arguments);
Close(job->fd);
}
}
最后但并非最不重要的是,request.h的标题:
struct req_info {
char * method;
char * uri;
char * version;
int fd;
int size;
char * filename;
int filename_len;
char * arguments;
int is_static;
rio_t stat_rio;
struct stat sbuf;
} req_info;
typedef struct tree_node TREE_NODE;
struct tree_node {
struct req_info *job_node;
TREE_NODE *left, *right;
};
void requestHandle(int fd, int shed_num);
void runRequest(int sched_num);