首页 > 学技术 > 技术网文 > C/C++ > 正文

[原创] linux下多進程服務框架


来源 chinaunix.net kuqin整理

提示:改編自tinyproxy,向原作者致敬!

在程序的開頭,可以定義以下几個常量:

#define MAXSERVICES	128	/* 每一個進程最大服務用戶數,防止錯誤積累 */

#define STARTSERVERS 32 /* 初始啟動服務進程數 */
#define MAXSPARESERVERS 32 /* 最大空閒服務進程數 */
#define MINSPARESERVERS 8 /* 最小空閒服務進程數 */


使用者只需要在程序最下面修改handle_connection函數,在裡面實現對客戶請求的處理邏輯即可,信號處理及進程組控制都由框架完成。在RHES 3 2.4kernel和Debian Etch 2.6kernel下測試通過。


歡迎指正。
#include <stdio.h>

#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>

/* 
 * 福瑞哈哥改編自tinyproxy
 */

#define MAXLISTEN 1024 /* 最大監聽數,listen函數用 */
#define MAXCLIENTS 64 /* 最大服務進程數 */
#define MAXSERVICES 128 /* 每一個進程最大服務用戶數,定期更新進程組,防止錯誤積累 */
#define STARTSERVERS 32 /* 初始啟動服務進程數 */
#define MAXSPARESERVERS 32 /* 最大空閒服務進程數 */
#define MINSPARESERVERS 8 /* 最小空閒服務進程數 */

#define PORT  8000 /* 監聽端口號 */

/* 全局變量區 */
int listenfd;  /* 服務socket */
int received_sighup = 0; /* 收到hup信號標誌 */
int quit = 0; /* 退出標誌 */

#define SERVER_COUNT_LOCK()   _child_lock_wait()
#define SERVER_COUNT_UNLOCK() _child_lock_release()

/* 共享變量鎖 */
static struct flock lock_it, unlock_it;
static int lock_fd = -1;

enum child_status_t { T_EMPTY, T_WAITING, T_CONNECTED };

struct child_s {
pid_t tid;
unsigned int connects;
enum child_status_t status;
};

/* 
 * 子進程數組-位於共享內存區 
 */
static struct child_s *child_ptr;

/*
 * 正等待用戶連接的服務進程數
 */
static unsigned int* servers_waiting;

/*
 * 分配一塊共享內存。
 */
static void*
malloc_shared_memory( size_t size )
{
int fd;
void* ptr;
char buffer[32];

static char* shared_file = "/tmp/mps.shared.XXXXXX";

assert( size > 0 );

strncpy( buffer, shared_file, sizeof(buffer) );

if ( (fd = mkstemp(buffer)) == -1 )
return (void *) MAP_FAILED;
unlink(buffer);

if (ftruncate(fd, size) == -1)
return (void *) MAP_FAILED;
ptr = mmap( NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );

return ptr;
}

/*
 * 分配一塊共享內存,並清0。
 */
static void*
calloc_shared_memory( size_t nmemb, size_t size )
{
void* ptr;
long length;

assert( nmemb > 0 );
assert( size > 0 );

length = nmemb * size;

ptr = malloc_shared_memory( length );
if ( ptr == MAP_FAILED )
return ptr;

memset( ptr, 0, length );

return ptr;
}

static void
_child_lock_init(void)
{
char lock_file[] = "/tmp/mps.servers.lock.XXXXXX";

lock_fd = mkstemp(lock_file);
unlink(lock_file);

lock_it.l_type = F_WRLCK;
lock_it.l_whence = SEEK_SET;
lock_it.l_start = 0;
lock_it.l_len = 0;

unlock_it.l_type = F_UNLCK;
unlock_it.l_whence = SEEK_SET;
unlock_it.l_start = 0;
unlock_it.l_len = 0;
}

static void
_child_lock_wait(void)
{
int rc;

while ( (rc = fcntl( lock_fd, F_SETLKW, &lock_it )) < 0 ) {
if (errno == EINTR)
continue;
else
return;
}
}

static void
_child_lock_release(void)
{
if (fcntl(lock_fd, F_SETLKW, &unlock_it) < 0)
return;
}

#define SERVER_INC() do { \
    SERVER_COUNT_LOCK(); \
    ++(*servers_waiting); \
    SERVER_COUNT_UNLOCK(); \
} while (0)

#define SERVER_DEC() do { \
    SERVER_COUNT_LOCK(); \
    assert(*servers_waiting > 0); \
    --(*servers_waiting); \
    SERVER_COUNT_UNLOCK(); \
} while (0)

/*
 * 創建監聽socket
 */
static void 
start_listen_socket( unsigned short port )
{
int sockfd;
int on = 1;
struct sockaddr_in addr;

sockfd = socket( AF_INET, SOCK_STREAM, 0 );
setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );

memset( &addr, 0, sizeof(addr) );
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
/*
 * 需要指定到實際的地址上
 */
addr.sin_addr.s_addr = inet_addr( "0.0.0.0" );

if ( bind( sockfd, (struct sockaddr *) &addr, sizeof (addr) ) < 0 ) {
fprintf( stderr, "Unable to bind listening socket because of %s\n", 
 strerror(errno) );
exit(-1);
}

if ( listen( sockfd, MAXLISTEN ) < 0 ) {
fprintf( stderr, "Unable to start listening socket because of %s\n",
     strerror(errno) );
exit(-1);
}

listenfd = sockfd;
}

void
close_listen_socket(void)
{
close( listenfd );
}

/*
 * 在這個函數中,寫下對客戶請求的處理邏輯。
 * 處理完成後在退出這個函數時,關閉connfd。
 */
void 
handle_connection( int connfd );

/*
 * 子進程主循環
 */
static void
child_main( struct child_s* ptr )
{
int connfd;
struct sockaddr *cliaddr;
socklen_t clilen;

clilen = sizeof( struct sockaddr );
cliaddr = (struct sockaddr*) malloc( clilen );
if ( !cliaddr ) {
fprintf( stderr,
     "Could not allocate memory for child address." );
exit(0);
}

ptr->connects = 0;

while ( !quit ) {
ptr->status = T_WAITING;

connfd = accept( listenfd, cliaddr, &clilen );

/*
 * 保證沒有錯誤發生
 */
if ( connfd < 0 ) {
fprintf( stderr, 
 "Accept returned an error (%s) ... retrying.", 
 strerror(errno) );
continue;
}

ptr->status = T_CONNECTED;

SERVER_DEC();

handle_connection( connfd );
ptr->connects++;

if ( ptr->connects == MAXSERVICES ) {
fprintf( stderr,
 "Child has reached MaxRequestsPerChild (%u). Killing child.\n",
 ptr->connects );
break;
}

SERVER_COUNT_LOCK();
if ( *servers_waiting > MAXSPARESERVERS ) {
/*
 * 有太多空閒服務進程,退出自己
 */
fprintf( stderr,
 "Waiting servers (%d) exceeds MaxSpareServers (%d). Killing child.\n",
 *servers_waiting, MAXSPARESERVERS );
SERVER_COUNT_UNLOCK();

break;
} else {
SERVER_COUNT_UNLOCK();
}

SERVER_INC();
}

ptr->status = T_EMPTY;

free( cliaddr );
exit(0);
}

/*
 * Fork一個子進程並啟動child_main()函數(子進程主循環)
 */
static int
child_make( struct child_s* ptr )
{
pid_t pid;

if ((pid = fork()) != 0)
return pid; /* 父進程 */

/*
 * 重設子進程的信號處理函數
 */
signal( SIGCHLD, SIG_DFL );
signal( SIGTERM, SIG_DFL );
signal( SIGHUP, SIG_DFL );

child_main(ptr); /* 不會返回 */

return -1;
}

int 
child_pool_create(void)
{
/* sleep(10); */
child_ptr = (struct child_s*) 
calloc_shared_memory( MAXCLIENTS, 
  sizeof(struct child_s) );
if ( child_ptr == MAP_FAILED ) {
fprintf( stderr, "Could not allocate shared memory for children.\n" );
return -1;
}

servers_waiting = (unsigned int*) 
malloc_shared_memory( sizeof(unsigned int) );
if ( servers_waiting == MAP_FAILED ) {
fprintf( stderr, "Could not allocate shared memory for child counting.\n" );
return -1;
}
*servers_waiting = 0;

/* 在操作servers_waiting變量之前, 創建加鎖文件 */
_child_lock_init();

int i;
/* 初始化子進程共享數組 */
for ( i = 0; i < MAXCLIENTS; i++ ) {
child_ptr.status = T_EMPTY;
child_ptr.connects = 0;
}

/* fork子進程 */
for ( i = 0; i < STARTSERVERS; i++ ) {
child_ptr.status = T_WAITING;
child_ptr.tid = child_make( &child_ptr );

if ( child_ptr.tid < 0 ) {
fprintf( stderr, 
     "Could not create child number %d of %d\n",
     i, STARTSERVERS );
return -1;
} else {
fprintf( stderr, 
    "Creating child number %d of %d ...\n",
    i + 1, STARTSERVERS );

SERVER_INC();
}
}

return 0;
}

/*
 * 刪除所有服務進程
 */
void
kill_children(void)
{
unsigned int i;

for ( i = 0; i < MAXCLIENTS; i++ ) {
if ( child_ptr.status != T_EMPTY )
kill( child_ptr.tid, SIGTERM );
}
}

/*
 * 監控進程主循環,它負責把服務進程的數量維持在一個合適的數量上。
 */
void 
child_main_loop(void)
{
unsigned int i;

while (1) {
if ( quit ) 
return;

/* 如果空閒服務進程數不足,則創建一些 */
SERVER_COUNT_LOCK();
if ( *servers_waiting < MINSPARESERVERS ) {
fprintf( stderr,
 "Waiting servers (%d) is less than MinSpareServers (%d). Creating new child.",
 *servers_waiting, MINSPARESERVERS );

SERVER_COUNT_UNLOCK();

for ( i = 0; i < MAXCLIENTS; i++ ) {
if ( child_ptr.status == T_EMPTY ) {
child_ptr.status = T_WAITING;
child_ptr.tid = child_make( &child_ptr );
if ( child_ptr.tid < 0 ) {
fprintf( stderr, "Could not create child" );

child_ptr.status = T_EMPTY;
break;
}

SERVER_INC();

break;
}
}
} else {
SERVER_COUNT_UNLOCK();
}

sleep(5);

if ( received_sighup ) {
/* 在收到hup信號後,可作一些維護工作,比如更新備份日志文件等 */
received_sighup = 0;
}
}
}

/*
 * 處理信號
 */
void
takesig( int sig )
{
pid_t pid;
int status;

switch (sig) {
case SIGHUP:
received_sighup = 1;
break;

case SIGTERM:
quit = 1;
break;

case SIGCHLD:
while ( (pid = waitpid(-1, &status, WNOHANG)) > 0 ) 
;
break;
}

return;
}

int 
main( int argc, char ** argv )
{
int godaemon = 1;  /* 是否轉為deamon模式 */
unsigned short port = PORT;  /* 服務端口號 */

assert( MINSPARESERVERS <= MAXSPARESERVERS );
assert( STARTSERVERS <= MAXCLIENTS );
assert( MAXCLIENTS <= MAXLISTEN );

if ( godaemon == 1 ) {
daemon(1, 1);
}

/* 啟動服務socket */
start_listen_socket( port ); 

signal( SIGPIPE, SIG_IGN );

/* 創建服務進程組 */
child_pool_create();

/* 
 * 下面這些信號處理函數只有監控進程才有用
 */
signal( SIGCHLD, takesig );
signal( SIGTERM, takesig );
signal( SIGHUP, takesig );

/* 開始監控工作 */
child_main_loop();

/* 退出前,殺掉服務進程組 */
kill_children();

/* 關閉服務socket */
close_listen_socket();

exit(0);
return 0;
}

/*
 * 在這個函數中,寫下對客戶請求的處理邏輯。
 * 處理完成後在退出這個函數時,關閉connfd。
 * 這只是一個示例!
 */
void 
handle_connection( int connfd )
{
char buf[128] = {};
sprintf( buf, "%u: ", (unsigned int) getpid() );
int len = strlen(buf);
read( connfd, buf + len, sizeof(buf) - len - 1 );
len = strlen(buf);
write( connfd, buf, len );

close( connfd );
}


[ 本帖最后由 福瑞哈哥 于 2007-5-30 15:08 编辑 ]

mps.tar.gz



 converse 回复于:2007-05-29 16:53:47

这个版出现了好几个线程池的框架,今天终于出现了多进程的框架,鼓励一下.


 converse 回复于:2007-05-29 16:57:31

可是为啥福瑞你的字体总是繁体捏?难道不在大陆?~~


 醉卧水云间 回复于:2007-05-29 17:00:06

个人不太喜欢多进程, 不方便移植.


 福瑞哈哥 回复于:2007-05-29 17:02:10

引用:原帖由 converse 于 2007-5-29 16:57 发表
可是为啥福瑞你的字体总是繁体捏?难道不在大陆?~~ 


我在啊,不過前一段時間在搞latex,開源的字體只有几套繁體的,所以換了繁體的輸入法,就喜歡上了。


 福瑞哈哥 回复于:2007-05-29 17:04:23

引用:原帖由 醉卧水云间 于 2007-5-29 17:00 发表
个人不太喜欢多进程, 不方便移植. 


在*nix/bsd係統上進程的移植性比thread好吧。


 wuxiangzhi 回复于:2007-05-30 08:18:33

不错~学习一下!


 duanjigang 回复于:2007-05-30 09:01:30

不是吧,最近好多多进程多线程啊,偶的多线程库还在改进中,也期待发出来啊


 wuxiangzhi 回复于:2007-05-30 10:22:41

缺点:退出和减少进程数的机制有些不够优雅


 福瑞哈哥 回复于:2007-05-30 10:26:43

引用:原帖由 wuxiangzhi 于 2007-5-30 10:22 发表
缺点:退出和减少进程数的机制有些不够优雅 



請指教!


 wuxiangzhi 回复于:2007-05-30 10:53:01

1.用信号的机制来处理子进程退出本来就是不可靠的,如果我没记错的话,child应该不是实时信号
比如一个子进程退出,父进程调用信号处理函数,waitpid后,马上又有子进程退出,这个信号就丢失了。
2.
if ( child_ptr.status != T_EMPTY )
  kill( child_ptr.tid, SIGTERM );
子进程在处理业务的时候,不由分说的被kill掉了。


 福瑞哈哥 回复于:2007-05-30 11:10:06

引用:
1.用信号的机制来处理子进程退出本来就是不可靠的,如果我没记错的话,child应该不是实时信号
比如一个子进程退出,父进程调用信号处理函数,waitpid后,马上又有子进程退出,这个信号就丢失了。


不是實時信號倒不影響。丟失child信號的問題不知是否會造成zomb進程?目前還沒有遇到過。不知有沒有解決辦法?

引用:
2.
if ( child_ptr[ i ].status != T_EMPTY )
  kill( child_ptr[ i ].tid, SIGTERM );
子进程在处理业务的时候,不由分说的被kill掉了。



誠然,不過只是在管理員殺掉服務時才會發生一次。如果要復雜一點,也可以在子進程中安裝信號處理。

[ 本帖最后由 福瑞哈哥 于 2007-5-30 11:26 编辑 ]


 converse 回复于:2007-05-30 11:16:57

>>用信号的机制来处理子进程退出本来就是不可靠的
>>丟失child信號的問題不知是否會造成zomb進程?

没看过福瑞的代码,但是一般处理子进程退出都应该用wait或者waitpid吧?


 wuxiangzhi 回复于:2007-05-30 11:18:08

1.丢失child信号,子进程的一些资源不会被回收而已
2.信号处理程序很实时,安装进去也有效,不够优雅而已。

拙见~~以后切磋o(∩_∩)o...哈哈


 福瑞哈哥 回复于:2007-05-30 11:23:43

引用:原帖由 wuxiangzhi 于 2007-5-30 10:53 发表
1.用信号的机制来处理子进程退出本来就是不可靠的,如果我没记错的话,child应该不是实时信号
比如一个子进程退出,父进程调用信号处理函数,waitpid后,马上又有子进程退出,这个信号就丢失了。



啊,這個不是問題,SIGCHLD信號只是在多個SIGCHLD信號一起來的時候有可能被變為一個信號,但是在信號處理函數中用while(waitpid)不管有多少個子進程都可以收割了!


 wuxiangzhi 回复于:2007-05-30 11:30:27

引用:原帖由 福瑞哈哥 于 2007-5-30 11:23 发表


啊,這個不是問題,SIGCHLD信號只是在多個SIGCHLD信號一起來的時候有可能被變為一個信號,但是在信號處理函數中用while(waitpid)不管有多少個子進程都可以收割了! 




我是说while(waipid) 执行后,发生的信号是会丢失的。


 wuxiangzhi 回复于:2007-05-30 11:45:12

致命的缺陷就是
主进程一直在child_main_loop里跑。
如果主进程在执行什么不可再入的函数,malloc什么的,如果被子进程退出信号中断,后果是不可预知的。
反之子进程被kill掉,一样会有这个问题,业务处理到了一半,被kill掉了。

信号处理函数后没有再次注册信号,这样就算再次发生child,也被忽略掉了,捕捉不到child了。


 福瑞哈哥 回复于:2007-05-30 13:40:44

引用:原帖由 wuxiangzhi 于 2007-5-30 11:30 发表



我是说while(waipid) 执行后,发生的信号是会丢失的。 



還是覺得不會出問題,在信號處理函數內發生的信號,while(waitpid)會收割,信號處理函數外的信號不會丟失並會觸發另一次信號處理。


 wuxiangzhi 回复于:2007-05-30 14:14:26

引用:原帖由 福瑞哈哥 于 2007-5-30 13:40 发表


還是覺得不會出問題,在信號處理函數內發生的信號,while(waitpid)會收割,信號處理函數外的信號不會丟失並會觸發另一次信號處理。 




:P:P你可以测一下撒,用这个框架生成多个子进程,然后一一kill掉,看主进程调用了几次信号处理函数。
child_main_loop中没有再次注册信号处理函数:P

:em06:

:m01:信号处理函数中如果没有再次注册信号的话,有信号发生时会忽略或者缺省处理。:)


 福瑞哈哥 回复于:2007-05-30 14:33:27

引用:
信号处理函数中如果没有再次注册信号的话,有信号发生时会忽略或者缺省处理



在這裡不是問題,信號雖然可能只會收到一個,但是在信號處理函數內用while(waitpid)就沒問題!請仔細思量一下。


 wuxiangzhi 回复于:2007-05-30 14:42:31

引用:原帖由 福瑞哈哥 于 2007-5-30 14:33 发表


在這裡不是問題,信號雖然可能只會收到一個,但是在信號處理函數內用while(waitpid)就沒問題!請仔細思量一下。 




还没明白我的意思

你在信号处理函数返回之前用pause() 试试看,这个时候发生的信号丢失了没有???

还有你压根就没用对,主进程从信号处理函数返回后需要重新调用signal注册信号处理函数。


 wuxiangzhi 回复于:2007-05-30 14:49:43

这个框架用了信号和锁来完成IPC,是一种思路,不过不是什么好思路。
可用性有待研究。

1.信号要处理好(比较复杂),因为信号是非实时的。
2.锁机制。子进程加锁后如果崩溃,要有相应的恢复机制


 trackless 回复于:2007-05-30 14:56:57

嗯,原创好,我支持。
虽然我已经有一套很完善的框架,但作者还是值得学习的,支持。


 思一克 回复于:2007-05-30 15:00:55

贴出来代码多好。ZIP太。。。


 wuxiangzhi 回复于:2007-05-30 15:04:33

版主来了,闪~~~~


 思一克 回复于:2007-05-30 15:14:41

实际是进程池,也即使开一些进程等待处理CLIENT请求?

同时处理的数量=CHILD数量?

我没有实验和深入看。

但觉得不错。


 福瑞哈哥 回复于:2007-05-30 15:19:31

引用:
你在信号处理函数返回之前用pause() 试试看,这个时候发生的信号丢失了没有???


同時你有多個子進程退出了,但是SIGCHLD是不可靠信號,所以父進程有可能只收到了一個SIGCHLD,也就是說信號丟失了。但是請看父進程的信號處理函數

while ( (pid = waitpid(-1, &status, WNOHANG)) > 0 ) {

;

雖然它有可能只會收到一個SIGCHLD信號,但是它會循環收割所有子進程,信號的丟失沒有任何影響!請再仔細思考。

还有你压根就没用对,主进程从信号处理函数返回后需要重新调用signal注册信号处理函数

這是很久以前的事情了,linux不需要重新調用singal!

引用:
信号要处理好(比较复杂),因为信号是非实时的。



信號只是在收割子進程時才用到,非實時不會造成任何問題。


引用:
锁机制。子进程加锁后如果崩溃,要有相应的恢复机制


據我理解,進程退出後鎖就會自動釋放。回頭再做一下試驗。不過父進程碓實沒有處理子進程崩潰的善後工作。如果要作就有點太復雜了,比如定期發信號檢查子進程。


 福瑞哈哥 回复于:2007-05-30 15:25:06

引用:原帖由 思一克 于 2007-5-30 15:14 发表
实际是进程池,也即使开一些进程等待处理CLIENT请求?

同时处理的数量=CHILD数量?
 



是采用進程池模式。
同時處理的數量可由常量MAXCLIENTS定義。


 wuxiangzhi 回复于:2007-05-30 15:47:36

我只用过 SUN OS。

while ( (pid = waitpid(-1, &status, WNOHANG)) > 0 ) {
                        ;
/*有信号发生*/
return;
这个信号如果能捕捉到的话,就是没问题的。

还有就是我以前提到的问题。
可重入函数的问题,
如果主进程在调用不可重入的函数,被子进程退出信号中断,是否不妥?
同样用信号来通知子进程退出,也会野蛮的中断子进程的业务处理,何来优雅之说?


 思一克 回复于:2007-05-30 16:24:42

while ( (pid = waitpid(-1, &status, WNOHANG)) > 0 ) {
                        ;
}

是标准做法。不会因为信号不可靠有任何问题。循环起作用了,一群孩子一起退出,一个信号就OK了。


 wuxiangzhi 回复于:2007-05-30 17:18:55

引用:原帖由 思一克 于 2007-5-30 16:24 发表
while ( (pid = waitpid(-1, &status, WNOHANG)) > 0 ) {
                        ;
}

是标准做法。不会因为信号不可靠有任何问题。循环起作用了,一群孩子一起退出,一个信号就OK了。 



:)的确,我以前研究信号机制的时候,写过这样的例子,也只能这么做,但是我说的是while循环结束后发生的信号是否能被捕捉到。


 思一克 回复于:2007-05-30 17:21:50

可以。

我的SERVER程序也是这种代码。不断FORK CHILD工作,一年也不会有遗漏。

也就是说,是严格没有问题的。

引用:原帖由 wuxiangzhi 于 2007-5-30 17:18 发表


:)的确,我以前研究信号机制的时候,写过这样的例子,也只能这么做,但是我说的是while循环结束后发生的信号是否能被捕捉到。 




 yulc 回复于:2007-05-30 22:55:10

有个问题,每个子进程一fork后,就执行:
"connfd = accept( listenfd, cliaddr, &clilen );"

也就是说多个子进程在同时阻塞在accept(),这个效率我不知道高不高。
但是,在apache的prefork的模型中,架构不是这样的。
它是每个子进程启动后,调用:
lock();
accept();

也就是说,要先得到锁以后才调用accpet(),否则阻塞在lock()处;


 foollan 回复于:2007-05-31 08:26:10

天啊 ,原来好多人都自己写库的啊
厉害厉害,偶啥时候也能写个库出来啊???


 zengc123 回复于:2007-06-05 17:41:17

引用:原帖由 wuxiangzhi 于 2007-5-30 11:45 发表
致命的缺陷就是
主进程一直在child_main_loop里跑。
如果主进程在执行什么不可再入的函数,malloc什么的,如果被子进程退出信号中断,后果是不可预知的。
反之子进程被kill掉,一样会有这个问题,业务处理到了 ... 




多进程是否不需要考虑可重入函数的问题?


 福瑞哈哥 回复于:2007-06-05 17:46:29

引用:原帖由 zengc123 于 2007-6-5 17:41 发表



多进程是否不需要考虑可重入函数的问题? 


基本上不需要。


 hwz_119 回复于:2007-06-05 21:19:15

福阿哥,能给小的说一下为什么不用V IPC 中的共享内存吗? 这个东东不好?谢谢了


 福瑞哈哥 回复于:2007-06-06 09:18:28

引用:原帖由 hwz_119 于 2007-6-5 21:19 发表
福阿哥,能给小的说一下为什么不用V IPC 中的共享内存吗? 这个东东不好?谢谢了 



這個框架中的進程都是相關進程,因此用mmap可以方便地實現一個匿名共享內存。
不過sys v shared memory是一個好東西,很多大型應用(http server, database)都依賴於它。


 hwz_119 回复于:2007-06-06 11:27:11

谢谢,再问个问题,如果在你的架构中实现定时主动发送心跳消息的功能.你会怎么实现呢.会有多个客户端


 福瑞哈哥 回复于:2007-06-06 17:17:17

引用:原帖由 hwz_119 于 2007-6-6 11:27 发表
谢谢,再问个问题,如果在你的架构中实现定时主动发送心跳消息的功能.你会怎么实现呢.会有多个客户端 



kill/signal/semaphore?說說具體的需求吧。


 hwz_119 回复于:2007-06-06 18:48:29

引用:原帖由 福瑞哈哥 于 2007-6-6 17:17 发表


kill/signal/semaphore?說說具體的需求吧。 




就是说,服务端和客户端为了能及时判断出对方系统崩溃等不可预知的情况,会互相发送心跳消息.心跳消息要定时发送,并且要依此向多个客户端发送.是不是要再建一个进程啊?
谢谢


 SupervisedBoy 回复于:2007-06-06 22:50:50

引用:原帖由 yulc 于 2007-5-30 22:55 发表
有个问题,每个子进程一fork后,就执行:
"connfd = accept( listenfd, cliaddr, &clilen );"

也就是说多个子进程在同时阻塞在accept(),这个效率我不知道高不高。
但是,在apache的prefork的模 ... 



lz没有加锁调用accept会出现惊群现象,这个在UNIX网络编程第1卷里有提到。当某一时刻只有一个连接过来时,N个睡眠进程会被同时叫醒,但只有一个进程可获得连接。如果每次唤醒的进程数目太多,会影响一部分系统性能。


 福瑞哈哥 回复于:2007-06-07 08:45:43

引用:原帖由 SupervisedBoy 于 2007-6-6 22:50 发表


lz没有加锁调用accept会出现惊群现象,这个在UNIX网络编程第1卷里有提到。当某一时刻只有一个连接过来时,N个睡眠进程会被同时叫醒,但只有一个进程可获得连接。如果每次唤醒的进程数目太多,会影响一部分系统 ... 



受教了。


 converse 回复于:2007-06-07 10:23:07

关于"惊群"问题,我看到CU一个朋友写的文章:
http://blog.chinaunix.net/u/5251/showart_276792.html


 sunhllu 回复于:2007-06-07 13:29:55

好啊,收藏


 pchotmail 回复于:2007-06-07 15:43:05

引用:原帖由 SupervisedBoy 于 2007-6-6 22:50 发表


lz没有加锁调用accept会出现惊群现象,这个在UNIX网络编程第1卷里有提到。当某一时刻只有一个连接过来时,N个睡眠进程会被同时叫醒,但只有一个进程可获得连接。如果每次唤醒的进程数目太多,会影响一部分系统 ... 




有个商业银行的server: 启动10多个进程accept同端口,每个进程accept到一个客户端就创建一个线程处理业务

[ 本帖最后由 pchotmail 于 2007-6-7 17:40 编辑 ]


 思一克 回复于:2007-06-07 15:50:41

书又在乱说了。

引用:原帖由 SupervisedBoy 于 2007-6-6 22:50 发表


lz没有加锁调用accept会出现惊群现象,这个在UNIX网络编程第1卷里有提到。当某一时刻只有一个连接过来时,N个睡眠进程会被同时叫醒,但只有一个进程可获得连接。如果每次唤醒的进程数目太多,会影响一部分系统 ... 




 Jass 回复于:2007-06-08 09:18:39

学习。。。


 Jass 回复于:2007-06-08 09:20:44

关注这个问题lz是怎么解决的。。。


 zsniper 回复于:2007-06-22 10:44:11

在FreeBSD中,accept加锁阻赛的效率没有阻赛在accept上高,因为accept上加锁是因为linux中accept不是原子操作。。

LZ的进程池,只要设置将MAXSPARESERVERS的值减小,即可降低惊群对效率的影响。。。。。

不知道我的说法对不对。。。。


 福瑞哈哥 回复于:2007-06-22 10:53:49

引用:原帖由 zsniper 于 2007-6-22 10:44 发表
在FreeBSD中,accept加锁阻赛的效率没有阻赛在accept上高,因为accept上加锁是因为linux中accept不是原子操作。。

LZ的进程池,只要设置将MAXSPARESERVERS的值减小,即可降低惊群对效率的影响。。。。。

不 ... 



如果真的有惊群这回事的话。


 思一克 回复于:2007-06-22 11:04:17

LINUX 2.6 上多进程阻塞在同一个sock的accept上,内核会唤醒一个,没有惊群问题。

其他的不得而知了。


 flw 回复于:2007-06-22 11:12:06

引用:原帖由 wuxiangzhi 于 2007-5-30 10:53 发表
1.用信号的机制来处理子进程退出本来就是不可靠的,如果我没记错的话,child应该不是实时信号
比如一个子进程退出,父进程调用信号处理函数,waitpid后,马上又有子进程退出,这个信号就丢失了


你说的不错,所以才有 while(waitpid()) 一说。
因此就这个问题来讲,楼主的做法是正确的。
引用:while ( (pid = waitpid(-1, &status, WNOHANG)) > 0 );



 xuqingsong1981 回复于:2007-06-22 17:05:23

这个aix下面能用吗


 福瑞哈哥 回复于:2007-06-22 17:07:15

没有测试过,如果你给我个帐号,我可以帮你调 :)




原文链接:http://bbs.chinaunix.net/viewthread.php?tid=942096
转载请注明作者名及原文出处



收藏本页到: