Linux下的进程池(2)

简介

上篇笔记中,记录了无锁的accept结构,但是出现惊群现象。最简单的避免方式是添加一个信号量锁即可,参考这篇笔记即可。

代码

仅仅添加几行改动,客户端使用前一个笔记的。给出代码:

#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <signal.h>
#include <wait.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <fcntl.h>

const int MAX_PROCESS_NUMBER = 15;
const int MAX_BUFFER_SIZE = 1024;

bool stop_server = false;
pid_t* pids = nullptr;
int processNum = 0;
const char* sem_name = "my_sem";
int sem;  // 全局锁

void sig_int (int signo);   // 终止整个线程池
void process (int fd);  // 子进程处理连接
pid_t child_make (int fd);  // 产生新的子进程
void child_main (int fd);   // 子进程的主要操作

int main (int argc, char* argv[]) {
    if (argc != 3) {
        printf ("Usage: %s <port of server> <number of process>\n", argv[0]);
        exit (0);
    }

    int port = atoi (argv[1]);
    if (port < 1024 || port > 65535) {
        perror ("port error\n");
        exit (0);
    }

    processNum = atoi (argv[2]);
    if (processNum < 0 || processNum > MAX_PROCESS_NUMBER) {
        perror ("process number error\n");
        exit (0);
    }

    struct sockaddr_in serv;
    bzero (&serv, sizeof (serv) );
    serv.sin_family = AF_INET;
    serv.sin_port = htons (port);
    serv.sin_addr.s_addr = htonl (INADDR_ANY);

    int listenfd = socket (AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0) {
        perror ("socket() error\n");
        exit (1);
    }

    if (bind (listenfd, (struct sockaddr*) &serv, sizeof (serv) ) < 0) {
        perror ("bind() error\n");
        exit (1);
    }

    if (listen (listenfd, 32) < 0) {
        perror ("listen() error\n");
        exit (1);
    }

    struct sigaction sa;  // 注册终止信号
    bzero (&sa, sizeof (sa) );
    sa.sa_handler = sig_int;
    sa.sa_flags = SA_RESTART;
    if (sigaction (SIGTERM, &sa, NULL) < 0) {
        perror ("sigaction() error\n");
        exit (1);
    }

    sem = sem_open (sem_name, O_CREAT, 0660, 1);  // 在这里创建全局锁

    pids = new pid_t[processNum];
    for (int i = 0; i < processNum; ++i) {
        pids[i] = child_make (listenfd);
    }

    while (!stop_server) {
        sleep (1);
    }

    exit (0);
}

void sig_int (int signo) {
    if (signo != SIGTERM) {
        return;
    }
    for (int i = 0; i < processNum; ++i) {
        kill (pids[i], SIGTERM); // 终止子进程
    }
    if (pids != nullptr) {
        delete[] pids;
    }
    stop_server = true;
}

pid_t child_make (int fd) {
    pid_t pid;
    if ( (pid = fork() ) > 0) {
        return pid;
    }

    child_main (fd);
}

void child_main (int fd) {
    struct sockaddr_in address;
    bzero (&address, sizeof (address) );
    socklen_t addrlen = sizeof (address);

    for (;;) {
        // 只需要在这里加锁即可,这样可以有效避免惊群现象
        sem_post (&sem);
        int connfd = accept (fd, (struct sockaddr*) &address, &addrlen);
        sem_wait (&sem);

        if (connfd < 0) {
            return;
        }
        puts ("accept data");
        process (connfd);
        close (fd);
    }
}

void process (int fd) {
    char buf[MAX_BUFFER_SIZE];
    for (;;) {
        if (recv (fd, buf, MAX_BUFFER_SIZE, 0) <= 0) {
            return;
        }
        printf ("%s", buf);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值