Linux之并发线程服务器

来源:互联网 发布:技改大修预算软件 编辑:程序博客网 时间:2024/06/05 13:33
使用Linux多进程服务器,这些年虽说使用很好,但使用fork生成子进程存在一些问题。首先,fork占用大量的资源,内存映像要从父进程拷贝到子进程,所有描述符要在子进程中复制等等。虽然当前采用写时拷贝(copy -on-write)技术,将真正的拷贝推迟到子进程有写操作时,但fork任然需要占用大量资源。其次,fork子进程后,需要进程间通信(IPC)在父子进程间传递信息。由于子进程从一开始就有父进程数据空间及所有描述符的拷贝,但是从子进程返回信息给父进程就需要做很多工作。
下面就介绍使用多线程实现并发服务器
#include <pthread.h>
int pthread_create( pthread_t *tid,const pthread_attr_t *attr, void*(*func)(void *), void *arg) ;
  若成功则返回0,否则返回出错编号
  返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于制定各种不同的线程属性。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg,如果需要向start_rtn函数传递的参数不止使用Linux多进程服务器,这些年虽说使用很好,但使用fork生成子进程存在一些问题。首先,fork占用大量的资源,内存映像要从父进程拷贝到子进程,所有描述符要在子进程中复制等等。虽然当前采用写时拷贝(copy -on-write)技术,将真正的拷贝推迟到子进程有写操作时,但fork任然需要占用大量资源。其次,fork子进程后,需要进程间通信(IPC)在父子进程间传递信息。由于子进程从一开始就有父进程数据空间及所有描述符的拷贝,但是从子进程返回信息给父进程就需要做很多工作。
下面就介绍使用多线程实现并发服务器
#include <pthread.h>
int pthread_create( pthread_t *tid,const pthread_attr_t *attr, void*(*func)(void *), void *arg) ;
  若成功则返回0,否则返回出错编号
  返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于制定各种不同的线程属性。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。


头文件 : #include <pthread.h>  
函数定义: int pthread_join(pthread_t thread, void **retval);  
描述 :  pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果进程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。  
参数 :  thread: 线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值。
  返回值 : 0代表成功。 失败,返回的则是错误号。
其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy过程和fork不一样。 copy后的进程和原先的进程共享了所有的变量,运行环境。这样,原先进程中的变量变动在copy后的进程中便能体现出来。


代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行
#include <pthread.h>
pthread_t pthread_self(vid);
函数返回调用函数的线程ID。


#inlcude <pthread.h>
void pthread_exit(void *status);
参数status指向函数的推出状态。这里的status不能指向一个局部变量,因为当前线程终止后其所有的局部变量将被撤销


还有两种方法使线程终止:
1 : 启动线程的函数pthread_create的第三个参数返回,该返回子就是线程终止的终止状态。
2 : 如果进程的main()函数返回或者任何线程调用了exit()函数进程将终止,线程将随之终止。一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。


头文件 : #include <pthread.h>  
函数定义: int pthread_join(pthread_t thread, void **retval);  
描述 :  pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果进程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。  
参数 :  thread: 线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值。
  返回值 : 0代表成功。 失败,返回的则是错误号。
其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy过程和fork不一样。 copy后的进程和原先的进程共享了所有的变量,运行环境。这样,原先进程中的变量变动在copy后的进程中便能体现出来。
代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行
#include <pthread.h>
pthread_t pthread_self(vid);
函数返回调用函数的线程ID。
#inlcude <pthread.h>
void pthread_exit(void *status);
参数status指向函数的推出状态。这里的status不能指向一个局部变量,因为当前线程终止后其所有的局部变量将被撤销
还有两种方法使线程终止:
1 : 启动线程的函数pthread_create的第三个参数返回,该返回子就是线程终止的终止状态。

2 : 如果进程的main()函数返回或者任何线程调用了exit()函数进程将终止,线程将随之终止。


#include <stdio.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <pthread.h>#define PORT 1234#define BACKLOG 5#define MAXDATASIZE 1000void process(int connfd, struct sockaddr_in client);void *function(void *arg);struct ARG{int connfd;struct sockaddr_in client;};int main(){int listenfd,connfd;pthread_t tid;struct ARG *arg;struct sockaddr_in server;struct sockaddr_in client;if( (listenfd = socket(AF_INET, SOCK_STREAM, 0) ) == -1 ){printf("socket fail \n ");_exit(1);}setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const void *)SO_REUSEADDR,sizeof(SO_REUSEADDR) );bzero( (void *)&server,sizeof(server) );server.sin_family = AF_INET;server.sin_port = htons(PORT);server.sin_addr.s_addr = htonl(INADDR_ANY);           //服务器可能有许多网卡,但是INADDR_ANY 设置默认网卡IP  也可设为172.16.1.166(本人pc机上的固定IP)if( bind( listenfd,(struct sockaddr *)&server ,&(socklen_t){sizeof server} ) == -1 ){printf(" bind fail \n");_exit(1);}if( listen(listenfd, BACKLOG) < 0){printf("listen fail  \n");_exit(1);}//-------------开始监听客户端-----------------------------------------------while(1){//int len = sizeof(client);if( (connfd = accept(listenfd, (struct sockaddr *)&client, &(socklen_t){sizeof client} ) ) == -1 )   //如果接受成功,那么client结构体将会存储远程主机的各种信息{printf("accept fail \n");_exit(1);}arg = (struct ARG*)malloc(sizeof(struct ARG) );      //注意这里的arg是传给function函数的形参,一定需要进行内存分配,不然不能实现并发执行arg->connfd = connfd;memcpy(arg->client, &client,sizeof(client) ) ;if( pthread_create(tid,NULL,function,(void *)arg) ){printf("can't create pthread \n");_exit(1);}}close(listenfd);}void process(int connfd, struct sockaddr_in client){int num;char cli_name[MAXDATASIZE];char databuf[MAXDATASIZE];printf("get data from : %s", inet_ntoa( client.sin_addr) );if((num = recv(connfd, cli_name, MAXDATASIZE, 0) ) == 0 ){printf("client disconnected \n");return ;}cli_name[num-1] = '\0';printf("client name is %s \n", cli_name);while( num = recv(connfd, databuf, MAXDATASIZE, 0) ){databuf[num-1] = '\0';printf("get message from client : %s \n",databuf);send(connfd, databuf, strlen(databuf), 0);}return ;}void *function(void *arg){struct ARG *info = (struct ARG*)arg;process(info->connfd,info->client);free(arg);pthread_exit(NULL);}

原创粉丝点击