TCP并发服务器(四)——预创建子进程,accept互斥锁

1.说明

Posix文件上锁可移植到所有Posix兼容系统,但是涉及到文件系统操作,可能比较费时

本次使用线程上锁保护accept,这不仅适用于同一进程中各线程之间上锁,也适用于不同进程之间上锁。

 

2.进程间使用互斥锁要求

(1) 互斥锁变量必须存放在由所有进程共享的内存区

(2) 必须告知线程函数库这是在不同进程之间共享的互斥锁。要求线程支持PTHREAD_PROCESS_SHARED属性。默认属性PTHREAD_PROCESS_PRIVATE, 只允许在单个进程内使用。

 

3.代码

支持SVR4的系统

 互斥锁快于文件上锁

#include "unp.h"
#include "unpthread.h"
#include <vector>
#include <sys/mman.h>        //for mmap()

using std::vector;

//共享内存首地址
static long *cptr;;
//预创建的子进程数目
static int nchildren;
//存放子进程的PID
static vector<int> pids;

static pthread_mutex_t *mptr;        //放在共享内存中

//锁的初始化
void my_lock_init(const char *pathname)
{
    int fd = Open("/dev/zero", O_RDWR, 0);
    mptr = (pthread_mutex_t*)Mmap(0, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE,
                MAP_SHARED, fd, 0);
    Close(fd);

    pthread_mutexattr_t mattr;
    Pthread_mutexattr_init(&mattr);
    Pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
    Pthread_mutex_init(mptr, &mattr);
}

void my_lock_wait()
{
    Pthread_mutex_lock(mptr);
}

void my_lock_release()
{
    Pthread_mutex_unlock(mptr);
}

//中断的信号处理函数
void sig_int(int signo)
{
    for (int i = 0; i < nchildren; ++i) {
        kill(pids[i], SIGTERM);
    }
    while (wait(NULL) > 0) {
        ;
    }
    if (errno != ECHILD) {
        err_sys("wait error");
    }

    DPRINTF("The number of child process accept.");
    for (int i = 0; i < nchildren; ++i) {
        DPRINTF("%ld", cptr[i]);
    }

    void pr_cpu_time(void);
    pr_cpu_time();

    exit(0);
}

int main(int argc, char *argv[])
{
    int listenfd;
    socklen_t addrlen;
    if (argc == 3) {
        listenfd = Tcp_listen(NULL, argv[1], &addrlen);
    } else if (argc == 4) {
        listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
    } else {
        err_quit("Usage: a.out [ <host> ] <port#> <#children>");
    }

    nchildren = atoi(argv[argc - 1]);
    pids.resize(nchildren);
    //查看连接在子进程的分布
    //使用共享内存未每个子进程的连接计数
    //fork之后父进程和子进程共享cptr
    long *meter(int);
    cptr = meter(atoi(argv[argc - 1]));
    //cptr = (long*)Calloc(nchildren, sizeof(long));

    //初始化锁
    my_lock_init("/tmp/lock.XXXXXX");

    //创建子进程
    pid_t child_make(int i, int listenfd, int addrlen);
    for (int i = 0; i < atoi(argv[argc - 1]); ++i) {
        pids[i] = child_make(i, listenfd, addrlen); 
    }

    //SIGCHLD处理函数
    void sig_chld(int);
    Signal(SIGCHLD, sig_chld);
    Signal(SIGINT, sig_int);

    for ( ; ;) {
        ;             //child does everything
    } //end for(;;)

    return 0;
}

void sig_chld(int)
{
    static int cnt = 0;
    pid_t pid;
    int stat;
    //param1: 想要等待的PID;-1: 等待第一个终止的子进程
    //param2: 子进程的终止状态(整数)
    //param3: 附加选项;WNOHANG:没有子进程终止时,不阻塞
    while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) {    //成功:返回进程ID > 0, 出错:0或-1
        DPRINTF("Waitpid for %d child process\n", ++cnt);
        ;
    }

    return;
}

pid_t child_make(int i, int listenfd, int addrlen)
{
    void child_main(int, int, int);
    pid_t pid = Fork();
    if (pid > 0) {        //parent
        return pid;
    }
    child_main(i, listenfd, addrlen);
}

void child_main(int i, int listenfd, int addrlen)
{
    DPRINTF("child %ld starting\n", (long)getpid());
    void web_child(int);
    for ( ; ; ) {
        DPRINTF("Lock");
        //上锁
        my_lock_wait();
        DPRINTF("Wait for a connection");
        int connfd = Accept(listenfd, NULL, NULL);
        DPRINTF("UNLOCK");
        //释放锁
        my_lock_release();
        //共享内存计数增加
        ++cptr[i];
        DPRINTF("Accept a connection.");

        web_child(connfd);
        Close(connfd);
    }
}

long *meter(int nchildren)
{
#ifdef MAP_ANON            //匿名映射
    long *ptr = (long*)Mmap(0, nchildren * sizeof(long), PROT_READ | PROT_WRITE, 
                    MAP_ANON | MAP_SHARED, -1, 0);
#else
    int fd = Open("/dev/zero", O_RDWR, 0);
    long *ptr = (long*)Mmap(0, nchildren * sizeof(long), PROT_READ | PROT_WRITE,
                     MAP_SHARED, fd, 0);
    //已完成文件到进程地址空间的映射,可以关闭原文件
    Close(fd);
#endif

    return ptr;
}

 

posted on 2014-07-25 11:22  hancmhi  阅读(440)  评论(0编辑  收藏  举报