mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
ext/pcntl: cpu affinity api introduction.
For now, working on Linux, FreeBSD >= 13.x and DragonFlyBSD. Handy wrapper to assign an array of cpu ids or to retrieve the cpu ids assigned to a given process. pcntl_setaffinity inserts valid unique cpu ids (within the range of available cpus). Close GH-13893
This commit is contained in:
parent
c96b975f67
commit
1cf8291c85
1
NEWS
1
NEWS
@ -124,6 +124,7 @@ PHP NEWS
|
||||
|
||||
- PCNTL:
|
||||
. Added pcntl_setns for Linux. (David Carlier)
|
||||
. Added pcntl_getaffinity/pcntl_setaffinity. (David Carlier)
|
||||
|
||||
- PCRE:
|
||||
. Upgrade bundled pcre2lib to version 10.43. (nielsdos)
|
||||
|
@ -445,6 +445,8 @@ PHP 8.4 UPGRADE NOTES
|
||||
- PCNTL:
|
||||
. Added pcntl_setns allowing a process to be reassociated with a namespace in order
|
||||
to share resources with other processes within this context.
|
||||
. Added pcntl_getaffinity to get the cpu(s) bound to a process and
|
||||
pcntl_setaffinity to bind 1 or more cpus to a process.
|
||||
|
||||
- Sodium:
|
||||
. Added the sodium_crypto_aead_aegis128l_*() and sodium_crypto_aead_aegis256l_*()
|
||||
|
@ -7,7 +7,7 @@ if test "$PHP_PCNTL" != "no"; then
|
||||
AC_CHECK_FUNCS([fork], [], [AC_MSG_ERROR([pcntl: fork() not supported by this platform])])
|
||||
AC_CHECK_FUNCS([waitpid], [], [AC_MSG_ERROR([pcntl: waitpid() not supported by this platform])])
|
||||
AC_CHECK_FUNCS([sigaction], [], [AC_MSG_ERROR([pcntl: sigaction() not supported by this platform])])
|
||||
AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork forkx pidfd_open])
|
||||
AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork forkx pidfd_open sched_setaffinity])
|
||||
|
||||
AC_CHECK_TYPE([siginfo_t],[PCNTL_CFLAGS="-DHAVE_STRUCT_SIGINFO_T"],,[#include <signal.h>])
|
||||
|
||||
|
@ -42,8 +42,13 @@
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_UNSHARE
|
||||
#if defined(HAVE_UNSHARE) || defined(HAVE_SCHED_SETAFFINITY)
|
||||
#include <sched.h>
|
||||
#if defined(__FreeBSD__)
|
||||
#include <sys/types.h>
|
||||
#include <sys/cpuset.h>
|
||||
typedef cpuset_t cpu_set_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PIDFD_OPEN
|
||||
@ -1476,6 +1481,123 @@ PHP_FUNCTION(pcntl_setns)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SCHED_SETAFFINITY
|
||||
PHP_FUNCTION(pcntl_getcpuaffinity)
|
||||
{
|
||||
zend_long pid;
|
||||
bool pid_is_null = 1;
|
||||
cpu_set_t mask;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(0, 1)
|
||||
Z_PARAM_OPTIONAL
|
||||
Z_PARAM_LONG_OR_NULL(pid, pid_is_null)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
// 0 == getpid in this context, we're just saving a syscall
|
||||
pid = pid_is_null ? 0 : pid;
|
||||
|
||||
CPU_ZERO(&mask);
|
||||
|
||||
if (sched_getaffinity(pid, sizeof(mask), &mask) != 0) {
|
||||
PCNTL_G(last_error) = errno;
|
||||
switch (errno) {
|
||||
case ESRCH:
|
||||
zend_argument_value_error(1, "invalid process (" ZEND_LONG_FMT ")", pid);
|
||||
RETURN_THROWS();
|
||||
case EPERM:
|
||||
php_error_docref(NULL, E_WARNING, "Calling process not having the proper privileges");
|
||||
break;
|
||||
default:
|
||||
php_error_docref(NULL, E_WARNING, "Error %d", errno);
|
||||
}
|
||||
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
zend_ulong maxcpus = (zend_ulong)sysconf(_SC_NPROCESSORS_CONF);
|
||||
array_init(return_value);
|
||||
|
||||
for (zend_ulong i = 0; i < maxcpus; i ++) {
|
||||
if (CPU_ISSET(i, &mask)) {
|
||||
add_next_index_long(return_value, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PHP_FUNCTION(pcntl_setcpuaffinity)
|
||||
{
|
||||
zend_long pid;
|
||||
bool pid_is_null = 1;
|
||||
cpu_set_t mask;
|
||||
zval *hmask = NULL, *ncpu;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(0, 2)
|
||||
Z_PARAM_OPTIONAL
|
||||
Z_PARAM_LONG_OR_NULL(pid, pid_is_null)
|
||||
Z_PARAM_ARRAY(hmask)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
if (!hmask || zend_hash_num_elements(Z_ARRVAL_P(hmask)) == 0) {
|
||||
zend_argument_value_error(2, "must not be empty");
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
// 0 == getpid in this context, we're just saving a syscall
|
||||
pid = pid_is_null ? 0 : pid;
|
||||
zend_ulong maxcpus = (zend_ulong)sysconf(_SC_NPROCESSORS_CONF);
|
||||
CPU_ZERO(&mask);
|
||||
|
||||
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(hmask), ncpu) {
|
||||
ZVAL_DEREF(ncpu);
|
||||
zend_long cpu;
|
||||
if (Z_TYPE_P(ncpu) != IS_LONG) {
|
||||
if (Z_TYPE_P(ncpu) == IS_STRING) {
|
||||
zend_ulong tmp;
|
||||
if (!ZEND_HANDLE_NUMERIC(Z_STR_P(ncpu), tmp)) {
|
||||
zend_argument_value_error(2, "cpu id invalid value (%s)", ZSTR_VAL(Z_STR_P(ncpu)));
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
cpu = (zend_long)tmp;
|
||||
} else {
|
||||
zend_string *wcpu = zval_get_string_func(ncpu);
|
||||
zend_argument_value_error(2, "cpu id invalid type (%s)", ZSTR_VAL(wcpu));
|
||||
zend_string_release(wcpu);
|
||||
RETURN_THROWS();
|
||||
}
|
||||
} else {
|
||||
cpu = Z_LVAL_P(ncpu);
|
||||
}
|
||||
|
||||
if (cpu < 0 || cpu >= maxcpus) {
|
||||
zend_argument_value_error(2, "cpu id must be between 0 and " ZEND_ULONG_FMT " (" ZEND_LONG_FMT ")", maxcpus, cpu);
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
if (!CPU_ISSET(cpu, &mask)) {
|
||||
CPU_SET(cpu, &mask);
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
if (sched_setaffinity(pid, sizeof(mask), &mask) != 0) {
|
||||
PCNTL_G(last_error) = errno;
|
||||
switch (errno) {
|
||||
case ESRCH:
|
||||
zend_argument_value_error(1, "invalid process (" ZEND_LONG_FMT ")", pid);
|
||||
RETURN_THROWS();
|
||||
case EPERM:
|
||||
php_error_docref(NULL, E_WARNING, "Calling process not having the proper privileges");
|
||||
break;
|
||||
default:
|
||||
php_error_docref(NULL, E_WARNING, "Error %d", errno);
|
||||
}
|
||||
RETURN_FALSE;
|
||||
} else {
|
||||
RETURN_TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void pcntl_interrupt_function(zend_execute_data *execute_data)
|
||||
{
|
||||
pcntl_signal_dispatch();
|
||||
|
@ -994,3 +994,8 @@ function pcntl_forkx(int $flags): int{}
|
||||
#ifdef HAVE_PIDFD_OPEN
|
||||
function pcntl_setns(?int $process_id = null, int $nstype = CLONE_NEWNET): bool {}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SCHED_SETAFFINITY
|
||||
function pcntl_getcpuaffinity(?int $process_id = null): array|false {}
|
||||
function pcntl_setcpuaffinity(?int $process_id = null, array $cpu_ids = []): bool {}
|
||||
#endif
|
||||
|
27
ext/pcntl/pcntl_arginfo.h
generated
27
ext/pcntl/pcntl_arginfo.h
generated
@ -1,5 +1,5 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: e5204cee68c41ff1201992f2572940c8f87980a3 */
|
||||
* Stub hash: a61b0327f5c36ca91e19c5f370377794b7950dee */
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_fork, 0, 0, IS_LONG, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
@ -139,6 +139,19 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_setns, 0, 0, _IS_BOOL, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SCHED_SETAFFINITY)
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_pcntl_getcpuaffinity, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, process_id, IS_LONG, 1, "null")
|
||||
ZEND_END_ARG_INFO()
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SCHED_SETAFFINITY)
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_setcpuaffinity, 0, 0, _IS_BOOL, 0)
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, process_id, IS_LONG, 1, "null")
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, cpu_ids, IS_ARRAY, 0, "[]")
|
||||
ZEND_END_ARG_INFO()
|
||||
#endif
|
||||
|
||||
ZEND_FUNCTION(pcntl_fork);
|
||||
ZEND_FUNCTION(pcntl_waitpid);
|
||||
ZEND_FUNCTION(pcntl_wait);
|
||||
@ -186,6 +199,12 @@ ZEND_FUNCTION(pcntl_forkx);
|
||||
#if defined(HAVE_PIDFD_OPEN)
|
||||
ZEND_FUNCTION(pcntl_setns);
|
||||
#endif
|
||||
#if defined(HAVE_SCHED_SETAFFINITY)
|
||||
ZEND_FUNCTION(pcntl_getcpuaffinity);
|
||||
#endif
|
||||
#if defined(HAVE_SCHED_SETAFFINITY)
|
||||
ZEND_FUNCTION(pcntl_setcpuaffinity);
|
||||
#endif
|
||||
|
||||
static const zend_function_entry ext_functions[] = {
|
||||
ZEND_FE(pcntl_fork, arginfo_pcntl_fork)
|
||||
@ -235,6 +254,12 @@ static const zend_function_entry ext_functions[] = {
|
||||
#endif
|
||||
#if defined(HAVE_PIDFD_OPEN)
|
||||
ZEND_FE(pcntl_setns, arginfo_pcntl_setns)
|
||||
#endif
|
||||
#if defined(HAVE_SCHED_SETAFFINITY)
|
||||
ZEND_FE(pcntl_getcpuaffinity, arginfo_pcntl_getcpuaffinity)
|
||||
#endif
|
||||
#if defined(HAVE_SCHED_SETAFFINITY)
|
||||
ZEND_FE(pcntl_setcpuaffinity, arginfo_pcntl_setcpuaffinity)
|
||||
#endif
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
69
ext/pcntl/tests/pcntl_cpuaffinity.phpt
Normal file
69
ext/pcntl/tests/pcntl_cpuaffinity.phpt
Normal file
@ -0,0 +1,69 @@
|
||||
--TEST--
|
||||
pcntl_getcpuaffinity() and pcntl_setcpuaffinity()
|
||||
--EXTENSIONS--
|
||||
pcntl
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (!function_exists("pcntl_setcpuaffinity")) die("skip pcntl_setcpuaffinity is not available");
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
$mask = [0, 1];
|
||||
var_dump(pcntl_setcpuaffinity(null, $mask));
|
||||
$act_mask = pcntl_getcpuaffinity();
|
||||
var_dump(array_diff($mask, $act_mask));
|
||||
$n_act_mask = pcntl_getcpuaffinity();
|
||||
var_dump(array_diff($act_mask, $n_act_mask));
|
||||
var_dump(pcntl_setcpuaffinity(null, ["0", "1"]));
|
||||
|
||||
try {
|
||||
pcntl_setcpuaffinity(null, []);
|
||||
} catch (\ValueError $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
pcntl_setcpuaffinity(null, ["abc" => "def", 0 => "cpuid"]);
|
||||
} catch (\ValueError $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
pcntl_setcpuaffinity(null, [PHP_INT_MAX]);
|
||||
} catch (\ValueError $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
pcntl_setcpuaffinity(null, [-1024, 64, -2]);
|
||||
} catch (\ValueError $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
pcntl_getcpuaffinity(-1024);
|
||||
} catch (\ValueError $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
pcntl_setcpuaffinity(null, [1, array(1)]);
|
||||
} catch (\ValueError $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
?>
|
||||
--EXPECTF--
|
||||
bool(true)
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
}
|
||||
bool(true)
|
||||
pcntl_setcpuaffinity(): Argument #2 ($cpu_ids) must not be empty
|
||||
pcntl_setcpuaffinity(): Argument #2 ($cpu_ids) cpu id invalid value (def)
|
||||
pcntl_setcpuaffinity(): Argument #2 ($cpu_ids) cpu id must be between 0 and %d (%d)
|
||||
pcntl_setcpuaffinity(): Argument #2 ($cpu_ids) cpu id must be between 0 and %d (-1024)
|
||||
pcntl_getcpuaffinity(): Argument #1 ($process_id) invalid process (-1024)
|
||||
|
||||
Warning: Array to string conversion in %s on line %d
|
||||
pcntl_setcpuaffinity(): Argument #2 ($cpu_ids) cpu id invalid type (Array)
|
Loading…
Reference in New Issue
Block a user