上期文章提了一下线程池的简单实现,本期顺势也把进程池的学习过程给大家分享一下吧;
/* Process struct */
typedef struct process
{
char name[SIZE_NAME_NORMAL]; /* Process name */
pid_t pid; /* Process id */
int pipefd[2]; /* Connection between master/slave */
size_t score; /* Score record */
} process_t;
/* Program instance */
typedef struct instance
{
char prg_name[SIZE_NAME_LONG]; /* Program name with path */
char cfg_name[SIZE_NAME_LONG]; /* Configure name with path */
u16 process_num; /* Sub process number */
u16 process_idx; /* Current process index */
struct process *proc; /* Process struct */
} instance_t;
int process_pool(instance_t *pinst, u16 process_num)
{
int ret = FAILURE;
int ix = 0;
int status = 0;
if ( !pinst || !process_num ) {
printf("NULL\n");
goto _E1;
}
signal(SIGINT, __sig_quit);
signal(SIGTERM, __sig_quit);
pinst->process_idx = 0;
pinst->process_num = process_num;
pinst->proc = (process_t *)calloc(process_num + 1, sizeof(process_t));
if ( !pinst->proc ) {
printf("Alloc process pool struct failed\n");
goto _E1;
}
for ( ix = 1; ix <= process_num; ix++ ) {
int bufsize = 1;
ret = pipe(pinst->proc[ix].pipefd);
if ( SUCCESS != ret ) {
printf("socketpair\n");
goto _E2;
}
printf("Setup worker#%u\n", ix);
pinst->proc[ix].pid = fork();
if ( pinst->proc[ix].pid < 0 ) {
printf("fork\n");
goto _E2;
}
else if ( pinst->proc[ix].pid > 0 ) {
/* Father */
CLOSE_FD(pinst->proc[ix].pipefd[0]);
continue;
}
else {
/* Child */
CLOSE_FD(pinst->proc[ix].pipefd[1]);
pinst->process_idx = ix;
ret = __worker(pinst);
goto _E2;
}
}
ret = __master(pinst);
/* Waiting workers */
for ( ix = 1; ix <= pinst->process_num; ix++ ) {
waitpid(pinst->proc[ix].pid, &status, WNOHANG);
}
_E2:
for ( ix = 1; ix <= pinst->process_num; ix++ ) {
CLOSE_FD(pinst->proc[ix].pipefd[1]);
CLOSE_FD(pinst->proc[ix].pipefd[0]);
}
FREE_POINTER(pinst->proc);
_E1:
return ret;
}
static int __master(instance_t *pinst)
{
int ret = 0;
int fd = 0;
int ix = 0;
int roll = 0;
char c = 0;
#define __offset(pinst) ((pinst)->proc[(pinst)->process_idx])
#define __round_robin(pinst, roll) \
((pinst)->proc[((roll) % (pinst)->process_num) + 1].pipefd[1])
printf("Master#%u setup\n", pinst->process_idx);
for ( g_enable = 1; g_enable; ) {
/* Get pipe fd by round-robin */
fd = __round_robin(pinst, ++roll);
c = 'A' + roll % 3; // 'A'/'B'/'C'
ret = write(fd, &c, 1);
if ( ret <= 0 ) {
return FAILURE;
}
sleep(1);
}
/* Tell all workers to quit */
for ( ix = 1; ix <= pinst->process_num; ix++ ) {
c = 'Q';
write(__round_robin(pinst, ++roll), &c, 1);
}
printf("Master#%u shutdown\n", pinst->process_idx);
return SUCCESS;
}
static int __worker(instance_t *pinst)
{
int fd = __offset(pinst).pipefd[0];
int ix = 0;
ssize_t read_byte = FAILURE;
char buffer[1024] = {0};
printf("Worker#%u setup\n", pinst->process_idx);
for ( g_enable = 1; g_enable; ) {
read_byte = read(fd, buffer, sizeof(buffer));
if ( read_byte <= 0 ) {
if ( errno == EAGAIN || errno == EINTR ) {
continue;
}
return FAILURE;
}
for ( ix = 0; ix < read_byte; ix++ ) {
switch ( buffer[ix] ) {
case 'A':
case 'B':
case 'C':
__offset(pinst).score += buffer[ix];
printf("Worker#%u Recv command: %c, score: %llu\n",
pinst->process_idx,
buffer[ix], __offset(pinst).score);
break;
case 'Q':
printf("Quit\n");
g_enable = 0;
break;
default:
break;
}
}
}
printf("Worker#%u shutdown\n", pinst->process_idx);
return SUCCESS;
}
最后带一个main方法,考虑了进程释放的问题,在这个demo中使用信号对genable进行控制;
static u8 g_enable; /* Running flag */
static void __sig_quit(int sig)
{
g_enable = 0;
}
int main(int argc, char *argv[])
{
instance_t inst = {0};
if ( argc < 2 ) {
printf("Usage: \n\t%s < process number >\n", argv[0]);
return EXIT_FAILURE;
}
return process_pool(&inst, atoi(argv[1]));
}