linux守护进程中多线程实现,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下调试通过。
共用头文件如下:(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;
公用函数如下(common.c):
/* 从一个描述字读文本行 */
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;
}
客户端主程序如下:(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 );
}
----服务器端主程序如下:
(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 );
}
}
linux守护进程中多线程实现,Linux下实现多线程客户/服务器相关推荐
- linux守护进程原理及创建详解
在linux或者unix操作系统中在系统的引导的时候会开启很多服务,这些服务就叫做守护进程.为了增加灵活性,root可以选择系统开启的模式,这些模式叫做运行级别,每一种运行级别以一定的方式配置系统. ...
- .NET跨平台实践:再谈用C#开发Linux守护进程 — 完整篇
Linux守护进程是Linux的后台服务进程,相当于Windows服务,对于为Linux开发服务程序的朋友来说,Linux守护进程相关技术是必不可少的,因为这个技术不仅仅是为了开发守护进程,还可以拓展 ...
- Linux 守护进程,编写(转载)
设置守护进程过程: 1.创建子进程,父进程退出 2.在子进程中创建新会话 3.改变当前目录为根目录 4.重设文件权限掩码 5.关闭文件描述符 *6.守护进程里,忽略SIGCHLD信号 (即使fork两 ...
- Linux 守护进程的原理与实现
一.守护进程概述 在linux或者unix操作系统中在系统的引导的时候会开启很多服务,这些服务就叫做守护进程.为了增加灵活性,root可以选择系统开启的模式,这些模式叫做运行级别,每一种运行级别以一定 ...
- 深入理解Linux守护进程
深入理解Linux守护进程Linux服务器在启动时需要启动很多系统服务,它们向本地和网络用户提供了Linux的系统功能接口,直接面向应用程序和用户.提供这些服务的程序是由运行在后台的守护进程(daem ...
- 如何正确编写linux守护进程
1.守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.如果想让某个进程不因为用户或终 ...
- linux+守护进程+php,【转载】Linux 守护进程的编程方法
[转载]Linux 守护进程的编程方法 原文见: http://www.linuxdevelop.org/tingxx/show.php?table=c&id=3 Linux 守护进程的编程方 ...
- linux 守护进程_网络工程师之linux守护进程
Linux守护进程就是通常所说的DEAMON进程,linux后台服务多种多样,每一个服务都运行一个对应的程序,这些后台程序对应的进程就是守护进程.系统中可以看到很多如DHCPD和HTTPD之类的进程, ...
- Linux多任务编程之七:Linux守护进程及其基础实验(转)
来源:CSDN 作者:王文松 转自Linux公社 ------------------------------------------------------------------------- ...
最新文章
- C#Winform窗体实现服务端和客户端通信例子(TCP/IP)
- 【渝粤教育】国家开放大学2018年春季 0007-22T文书档案管理 参考试题
- 记录一次SQL优化,增加索引,随便写的当笔记了
- 将Notepad++配置为Python编译器
- 问题四十一:怎么用ray tracing画任意圆柱面(generalized cylinder)
- mysql 截取 效率,MySQL 随机函数获取数据速度和效率分析
- mac上的实用工具(mac常用工具)
- Verilog实现数字时钟
- html 下划线居中,Word里下划线上内容怎么在下划线范围内居中?
- 情人节 放出了 我囤积的 部分碎片 论点
- 计算机专业男人喜欢什么样的女人,说实在的,其实男人真正喜欢的女人,就三个特征...
- 苹果CMSV10本地DPLAYER播放器自动下一集设置教程
- criterial查询(2014-05-29 03:51)续-----Example
- Centos7防火墙常用操作打开关闭端口
- 浅谈Selenium之WebUI自动化
- 阿里内核数据库文章-目录
- matlab用RNN预测股票,使用RNN预测股票价格系列一
- 惠普Z820安装win10系统攻略(固态作为系统盘)——思小瓜
- Typescript 笔记
- 与设备无关的彩色空间