`
lixinye0123
  • 浏览: 332214 次
  • 性别: Icon_minigender_1
  • 来自: 温州
社区版块
存档分类
最新评论

Linux操作系统下实现多线程客户/服务器

阅读更多

在传统的Unix模型中,当一个进程需要由另一个实体执行某件事时,该进程派生(fork)一个子进程,让子进程去进行处理。

Unix下的大多数网络服务器程序都是这么编写的,即父进程接受连接,派生子进程,子进程处理与客户的交互。

虽然这种模型很多年来使用得很好,但是fork时有一些问题:

1. fork是昂贵的。内存映像要从父进程拷贝到子进程,所有描述字要在子进程中复制等等。目前有的Unix实现使用一种叫做写时拷贝(copy-on-write)的技术,可避免父进程数据空间向子进程的拷贝。尽管有这种优化技术,fork仍然是昂贵的。

2. fork子进程后,需要用进程间通信(IPC)在父子进程之间传递信息。Fork之前的信息容易传递,因为子进程从一开始就有父进程数据空间及所有描述字的拷贝。但是从子进程返回信息给父进程需要做更多的工作。

线程有助于解决这两个问题。线程有时被称为轻权进程(lightweight process),因为线程比进程“轻权”,一般来说,创建一个线程要比创建一个进程快10~100倍。

一个进程中的所有线程共享相同的全局内存,这使得线程很容易共享信息,但是这种简易性也带来了同步问题。

一个进程中的所有线程不仅共享全局变量,而且共享:进程指令、大多数数据、打开的文件(如描述字)、信号处理程序和信号处置、当前工作目录、用户ID和组ID。

但是每个线程有自己的线程ID、寄存器集合(包括程序计数器和栈指针)、栈(用于存放局部变量和返回地址)、error、信号掩码、优先级。

在Linux中线程编程符合Posix.1标准,称为Pthreads。所有的pthread函数都以pthread_开头。

以下先讲述5个基本线程函数,在调用它们前均要包括pthread.h头文件。然后再给出用它们编写的一个TCP客户/服务器程序例子。

第一个函数:

int pthread_create (pthread_t *tid,const pthread_attr_t *attr,void *(*func)(void *),void *arg);

一个进程中的每个线程都由一个线程ID(thread ID)标识,其数据类型是pthread_t(常常是unsigned int)。如果新的线程

创建成功,其ID将通过tid指针返回。

每个线程都有很多属性:优先级、起始栈大小、是否应该是一个守护线程等等,当创建线程时,我们可通过初始化一个pthread_attr_t

变量说明这些属性以覆盖缺省值。我们通常使用缺省值,在这种情况下,我们将attr参数说明为空指针。

最后,当创建一个线程时,我们要说明一个它将执行的函数。线程以调用该函数开始,然 后或者显式地终止(调用pthread_exit) 或者隐式地终止(让该函数返回)。函数的地址由func参数指定,该函数的调用参数是一个指针arg,如果我们需要多个调用参数,我们必须将它们打包成一 个结构,然后将其地址当作唯一的参数传递给起始函数。

在func和arg的声明中,func函数取一个通用指针(void *)参数,并返回一个通用指针(void *),这就使得我们可以传递一个

指针(指向任何我们想要指向的东西)给线程,由线程返回一个指针(同样指向任何我们想要指向的东西)。

调用成功,返回0,出错时返回正Exxx值。Pthread函数不设置errno。

第二个函数:

int pthread_join(pthread_t tid,void **status);

该函数等待一个线程终止。把线程和进程相比,pthread_creat类似于fork,而pthread_join类似于waitpid。

我们必须要等待线程的tid,很可惜,我们没有办法等待任意一个线程结束。

如果status指针非空,线程的返回值(一个指向某个对象的指针)将存放在status指向的位置。

第三个函数;

pthread_t pthread_self(void);

线程都有一个ID以在给定的进程内标识自己。线程ID由pthread_creat返回,我们可以pthread_self取得自己的线程ID。

第四个函数:

int pthread_detach(pthread_t tid);

线程或者是可汇合的(joinable)或者是脱离的(detached)。当可汇 合的线程终止时,其线程ID和退出状态将保留,直到另外一个线程调用pthread_join。脱离的线程则像守护进程:当它终止时,所有的资源都释放, 我们不能等待它终止。如果一个线程需要知道另一个线程什么时候终止,最好保留第二个线程的可汇合性。

Pthread_detach函数将指定的线程变为脱离的。

该函数通常被想脱离自己的线程调用,如:pthread_detach (pthread_self ( ));

第五个函数:

void pthread_exit(void *status);

该函数终止线程。如果线程未脱离,其线程ID和退出状态将一直保留到调用进程中的某个其他线程调用pthread_join函数。

指针status不能指向局部于调用线程的对象,因为线程终止时这些对象也消失。

有两种其他方法可使线程终止:

1. 启动线程的函数(pthread_creat的第3个参数)返回。既然该函数必须说明为返回一个void指针,该返回值便是线程的终止状态。

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

以下给出一个使用线程的TCP回射客户/服务器的例子,完成的功能是客户端使用线程 给服务器发从标准输入得到的字符,并在主线程中将从服务器端返回的字符显示到标准输出,服务器端将客户端发来的数据原样返回给客户端,每一个客户在服务器 上对应一个线程。利用该程序框架,通过扩展客户端和服务器端的处理功能,可以完成多种基于多线程的客户机/服务器程序。该程序在RedHat 6.0和TurboLinux4.02下调试通过。

共用头文件如下:

<ccid_nobr> </ccid_nobr>
<ccid_code>(head.h) 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 1024
#define SERV_PORT 8000
#define LISTENQ 1024
static int sockfd;
static FILE *fp;</ccid_code>

公用函数如下(common.c):

<ccid_nobr> </ccid_nobr>
<ccid_code>/* 从一个描述字读文本行 */ 
ssize_t readline(int fd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
for (n=1; n0)
{
if ( (nwritten=write (fd, ptr, nleft ) )<=0 )
{
if (errno==EINTR )
nwritten=0;
else
return (-1);
}
nleft-=nwritten;
ptr++=nwritten;
}</ccid_code>

客户端主程序如下:

<ccid_nobr> </ccid_nobr>
<ccid_code>(client.c) 
#include “head.h";
#include “common.c";
/* 在str_cli中定义的要被线程执行的函数 */
void *copyto (void *arg)
{
char sendline[MAXLINE];
while (fgets (sendline,MAXLINE,fp) !=NULL )
writen(sockfd,sendline,strlen(sendline));
shutdown(sockfd,SHUT_WR);
return(NULL);
}

void str_cli(FILE *fp_arg, int sockfd_arg)
{
char recvline[MAXLINE];
pthread_t tid;
sockfd=sockfd_arg;
fp=fp_arg;
pthread_creat(&tid, NULL, copyto, NULL);
while (readline (sockfd,recvline,MAXLINE) >0)
---- fputs(recvline,stdout);
}

int main ( int argc, char **argv )
{
int sockfd;
struct sockaddr_in servaddr;
if (argc!=2 )
printf ( “ usage: tcpcli " );
exit(0);
bzero(&servaddr, sizeof (servaddr)) ;
servaddr.sin_family=AF_INET;
servaddr.sinport=htons(SERV_PORT);
inet_pton (AF_INET, argv[1], &servaddr.sin_addr );
connect (sockfd, (struct sockaddr *)&servaddr,
siziof (servaddr ) );
str_cli (stdin, sockfd );
exit (0 );
}</ccid_code>

服务器端主程序如下:

<ccid_nobr> </ccid_nobr>
<ccid_code>(server.c) 
#include “head.h";
#include “common.c";
void str_echo (int sockfd )
{
ssize_t n;
char line[MAXLINE];
for (; ; )
{
if ( (n=readline (sockfd, line, MAXLINE) )==0)
return;
writen (sockfd, line, n);
}
}

static void *doit ( void *arg)
{
pthread_detach(pthread_self ( ) );
str_echo ( (int ) arg );
close ( (int ) arg );
return ( NULL ) ;
}

int main ( int argc, char **argv )
{
int listenfd, connfd;
socklen_t addrlen,len;
struct sockaddr_in cliaddr, servaddr;
pthread_t tid;
listenfd=socket (AF_INET, SOCK_STREAM, 0 );
bzero (&servaddr, sizeof (servaddr ) );
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl (INADDR_ANY );
servaddr.sin_port=SERV_PORT;
bind (listenfd, ( struct sockaddr * )&servaddr, sizeof
(servaddr ) );
listen (listenfd, LISTENQ );
addrlen=sizeof ( cliaddr );
cliaddr=malloc(addrlen );
for ( ; ; )
{
len=addrlen;
connfd=accept(listenfd, (struct sockaddr * )&cliaddr, &len );
pthread_creat ( &tid, NULL, &doit, ( void * )connfd );
}
}</ccid_code>
 
分享到:
评论

相关推荐

    Linux系统下实现多线程客户/服务器

    在Linux系统下,实现多线程客户/服务器模式是一种高效且灵活的方法,尤其适用于网络服务程序,能够提高并发处理能力并降低系统开销。传统的Unix模型依赖于进程间的通信(IPC),通过`fork()`创建子进程来处理客户端...

    linux下c语言实现多线程web服务器

    在Linux环境下,C语言是一种...综上所述,"linux下c语言实现多线程web服务器"项目涵盖了操作系统、网络编程、线程管理、内存操作、并发控制等多个方面,对于理解系统级编程和Web服务的内部工作原理有着重要的实践价值。

    Linux下基于多线程的服务器程序设计.pdf

    Linux操作系统是当前最流行的开源操作系统之一,它广泛应用于服务器领域。设计一个高性能的服务器程序是非常重要的,然而传统的服务器工作方式存在三个显著的缺点:fork开销大、进程间通信困难、并发执行能力差。...

    Linux系统下的多线程编程入门.pdf

    在Linux系统下进行多线程编程是开发高效并发应用程序的关键技术之一。本文将深入探讨Linux环境中的多线程概念、创建与管理线程的方法、线程同步与通信机制,以及多线程编程中可能遇到的问题和解决策略。 一、多线程...

    Linux下多线程轻量级HTTP服务器

    在Linux操作系统中,开发一款多线程轻量级HTTP服务器是一项技术挑战,它涉及到网络编程、多线程处理以及对HTTP协议的理解。本项目“Linux下多线程轻量级HTTP服务器”旨在实现一个基本的HTTP服务器,能够响应客户端的...

    Linux下基于socket多线程并发通信的实现.pdf

    本文主要探讨Linux操作系统下基于socket多线程并发通信的实现。 socket是UNIX系统开发中的网络通信接口,可以对台计算机之间的通信规范进行合理定义,从而达到通信的目的。本文首先对socket的基本情况进行概述,然后...

    基于Linux的多线程池并发Web服务器设计.pdf

    本文的设计基于 Linux操作系统,使用多线程池技术,实现了高效的Web服务器设计。 在本文中,我们还讨论了基于进程的Web服务器设计的不足之处。传统的基于进程的Web服务器设计使用父子进程机制来处理客户端的请求,...

    LINUX系统下多线程与多进程性能分析.pdf

    本文主要讨论了Linux操作系统下多线程和多进程的性能分析。在Linux系统中,使用多进程处理多个任务,会占用很多系统资源(主要是CPU和内存的使用)。因此,Linux系统对这种弊端进行了改进,在用户态实现了多线程处理...

    Linux系统下基于Tcp的多线程大文件上传实现.pdf

    本文介绍了一种基于Tcp的多线程大文件上传实现方法,在Linux系统下实现大文件上传的解决方案。该方法通过将大文件分块,并使用多线程技术来实现文件传输,提高了文件传输的效率。 知识点1:多线程编程 * 多线程...

    Linux操作系统下的多线程编程详细解析.doc

    在Linux操作系统下,多线程编程是一种常见的编程技术,它允许多个执行流在同一进程中并发运行,从而提高程序的响应速度、充分利用多核处理器资源以及改善程序结构。Linux下的多线程实现基于POSIX线程接口(pthread)...

    C++实现的Linux多线程聊天室

    Linux操作系统提供了pthread库来支持多线程编程,该库包含了创建、管理线程以及同步线程间通信等功能。 在C++中,我们可以使用`&lt;thread&gt;`库来创建和管理线程。例如,我们可以通过`std::thread`类的构造函数创建一个...

    Linux下的多线程编程.pdf

    "Linux下的多线程编程" Linux下的多线程编程是一种高效的程序设计方法,它可以将一个程序的任务划分为多个部分,每个部分是一个顺序控制流。多线程编程可以实现并行计算,高效利用多处理器,并且具有许多优点,如...

    linux下多线程文件服务器

    在Linux环境下构建多线程文件服务器是一项挑战性的任务,它涉及到操作系统、网络编程以及并发处理等多个方面的知识。本文将深入探讨这些关键知识点,并基于提供的链接文章进行解释。 首先,我们来了解一下多线程。...

    libevent 多线程 HTTP post服务器

    描述中提到的“libevent 多线程 HTTP post服务器”表明这是一个利用libevent库开发的服务端程序,该程序能够接收并处理来自客户端的HTTP POST请求,并且采用了多线程技术以实现并发处理,提升系统性能。这通常意味着...

    Linux下多线程编程详解

    在Linux下实现多线程,通常会使用POSIX线程库(简称pthread)。pthread为程序员提供了一系列标准的线程操作函数,包括创建线程、销毁线程、线程同步等。使用pthread,程序员可以不必了解操作系统的线程调度和上下文...

    linux多线程编程.pdf

    Linux多线程编程是操作系统中并发程序设计的一个重要领域,它允许开发者在同一程序中创建多个线程,以实现并行执行,从而提高程序的执行效率和响应能力。Linux下的多线程编程通常基于POSIX线程(pthread)库来实现,...

    Linux多线程高并发服务器

    例如,在多线程服务器中,可能用信号量来限制同时处理的连接数。 6. **性能优化**:为了提高高并发服务器的性能,通常会进行内存优化(如使用内存池减少内存碎片)、减少上下文切换(如使用线程局部存储)、负载...

    server_c_Linux.rar_linux 多线程_多线程 服务器_多线程通信

    本资源"server_c_Linux.rar"包含了实现Linux下多线程服务器的核心代码,包括"server.c"和"server.h"两个文件,旨在帮助开发者理解如何创建并管理多线程来处理来自多个客户端的并发连接。 首先,多线程在服务器应用...

    Linux下用Socket和多线程实现简单聊天室

    在Linux操作系统中,使用Socket和多线程技术可以构建一个简单的聊天室应用。Socket是网络通信的基本接口,它允许不同的进程或计算机之间进行数据交换。而多线程则可以提高程序的并发处理能力,使聊天室能同时处理多...

Global site tag (gtag.js) - Google Analytics