Я думаю, что вы можете достичь этого только с помощью внешней сущности, поскольку разветвленные процессы не имеют ничего общего (кроме родителей).
Давайте использовать файл.Мы можем получить файл, используя tmpfile()
.Нам понадобится некоторая блокировка, используя flock(fileno(FILE *), ...)
.Каждый ребенок записывает один байт во временный файл.После того, как все дочерние элементы запущены, я могу получить размер файла - таким образом, я получу количество дочерних элементов:
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <sys/file.h>
#include <sys/types.h>
#include <unistd.h>
typedef FILE cnt_t;
/**
* Create interprocess counter.
* Should be created once (and only once) by the parent process.
*/
cnt_t *cnt_new(void)
{
return tmpfile();
}
/**
* Remove interprocess counter.
* Should be called by all childs and parent
*/
void cnt_delete(cnt_t *t)
{
fclose(t);
}
void _cnt_lock(cnt_t *t)
{
for (int ret; (ret = flock(fileno(t), LOCK_EX)) != 0;) {
assert(ret == EWOULDBLOCK);
}
}
void _cnt_unlock(cnt_t *t)
{
if (flock(fileno(t), LOCK_UN) != 0) {
assert(0);
}
}
/**
* Increments counter by 1.
*/
void cnt_inc(cnt_t *t) {
assert(t != NULL);
_cnt_lock(t);
if (fwrite((char[1]){'X'}, sizeof(char), 1, t) < 0) {
assert(0);
}
if (fflush(t) != 0) {
assert(0);
}
_cnt_unlock(t);
}
void cnt_println(cnt_t *t)
{
_cnt_lock(t);
if (fseek(t, 0L, SEEK_SET) < 0) {
assert(0);
}
char buf[124];
size_t cnt = fread(buf, sizeof(char), 124, t);
printf("cnt(%p) = %ld '%.*s'\n", cnt, (void*)t, cnt, buf);
_cnt_unlock(t);
}
/**
* Get's counter value.
*/
long cnt_getCount(cnt_t *t)
{
assert(t != NULL);
_cnt_lock(t);
if (fseek(t, 0L, SEEK_END) < 0) {
assert(0);
}
const long sz = ftell(t);
if (sz < 0) {
assert(0);
}
_cnt_unlock(t);
return sz;
}
/* ----------------------------------------------------------- */
int main()
{
srand(0);
cnt_t *cntobj = cnt_new();
bool child = false;
for (int i = 0; i < 5; ++i) {
const int ret = fork();
switch (ret) {
case 0:
cnt_inc(cntobj);
child = true;
break;
case -1:
fprintf(stderr, "fork error!\n");
exit(-1);
break;
default:
fprintf(stderr, "%d -> %d\n", getpid(), ret);
break;
}
}
while (wait(NULL) != -1) continue;
if (child) {
cnt_delete(cntobj);
exit(0);
}
const long cnt = cnt_getCount(cntobj);
cnt_delete(cntobj);
fprintf(stderr, "childs %ld\n", cnt);
return 0;
}
Возможно, я слишком много занимался запутыванием (typedef FILE cnt_t
выглядит странно), нокод работает и возвращает правильное число 31. Живая версия доступна на jdoodle .
А вот решение, использующее каналы:
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <sys/file.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct {
int p[2];
} cnt_t;
/**
* Create interprocess counter.
* Should be created once (and only once) by the parent process.
*/
cnt_t *cnt_new(void)
{
cnt_t *t = malloc(sizeof(*t));
assert(t != NULL);
if (pipe(t->p) < 0) {
assert(0);
}
if (fcntl(t->p[0], F_SETFL, O_NONBLOCK) < 0) {
assert(0);
}
return t;
}
/**
* Remove interprocess counter.
* Should be called by all childs and parent
*/
void cnt_delete(cnt_t *t)
{
close(t->p[0]);
close(t->p[1]);
t->p[0] = 0;
t->p[1] = 0;
free(t);
}
/**
* Increments counter by 1.
*/
void cnt_inc(cnt_t *t)
{
assert(t != NULL);
if (write(t->p[1], (char[1]){'X'}, 1 * sizeof(char)) < 0) {
assert(0);
}
}
/**
* Get's counter value.
*/
long cnt_getCount(cnt_t *t)
{
assert(t != NULL);
char c;
long cnt = 0;
ssize_t tmp;
errno = 0;
while ((tmp = read(t->p[0], &c, 1)) == 1) {
++cnt;
}
if (tmp < 0 && errno != EWOULDBLOCK) {
assert(0);
}
const long ret = cnt;
while (cnt--) {
if (write(t->p[1], (char[1]){'X'}, 1) < 0) {
assert(0);
}
}
return ret;
}
/* ----------------------------------------------------------- */
int main()
{
srand(0);
cnt_t *cntobj = cnt_new();
bool child = false;
for (int i = 0; i < 5; ++i) {
const int ret = fork();
switch (ret) {
case 0:
cnt_inc(cntobj);
child = true;
break;
case -1:
fprintf(stderr, "fork error!\n");
exit(-1);
break;
default:
fprintf(stderr, "%d -> %d\n", getpid(), ret);
break;
}
}
while (wait(NULL) != -1) continue;
if (child) {
cnt_delete(cntobj);
exit(0);
}
const long cnt = cnt_getCount(cntobj);
cnt_delete(cntobj);
fprintf(stderr, "childs %ld\n", cnt);
return 0;
}
Работает хорошо и, вероятно,это намного быстрее.Живая версия по-прежнему jdoodle .
В этих примерах используется глупая обработка ошибок с использованием assert
, и они служат только для демонстрации метода и его работы.
Вероятно, мы могли бы также создать решение с использованием posix shemaphores и некоторого межпроцессного взаимодействия, некоторых shmget
и semop
и, например, подсчета количества семафоров в очереди.