Краткий ответ:для ядра 4.4.0 TCP PACING включен по умолчанию и установлен на ~34,36 Гбит/с на сокет. Начиная с ядра 4.13.0 -rc1, TCP PACING по умолчанию отключен. В обоих случаях, поскольку TSO включен, TCP PACING не будет иметь никакого эффекта, если для частоты стимуляции установлено значение по умолчанию.
TCP PACING устанавливается для сокета с помощью функции sock _setsockopt ()(net/core/sock.c ). Флагом для этого является SO _MAX _PACING _RATE, а код обработки выглядит следующим образом:
case SO_MAX_PACING_RATE:
sk->sk_max_pacing_rate = val;
sk->sk_pacing_rate = min(sk->sk_pacing_rate,
sk->sk_max_pacing_rate);
break;
При создании сокета соответствующие поля устанавливаются в ~0U в sock _init _data()(net/core/sock.c):
sk->sk_max_pacing_rate = ~0U;
sk->sk_pacing_rate = ~0U;
Таким образом, значениями по умолчанию для частоты стимуляции и максимальной частоты стимуляции является значение max unsigned int, которое составляет (2^32 -1 )или 4 294 967 295 байт в секунду, что составляет 34,36 Гбит/с.
Однако можно достичь более высоких скоростей даже с одним сокетом, используя разгрузку сегментации TCP (TSO ). TSO принимает во внимание частоту стимуляции,но на самом деле не ограничен скоростью по умолчанию. В tcp _tso _autosize ()function (net/ipv4/tcp _output.c )скорость синхронизации может уменьшить размер сеанса TSO, но поскольку значение по умолчанию ~4,3 ГБ, и даже после сдвига вправо на 10 вы все равно получите ~4 МБ, это намного больше, чем типичный сеанс TSO.
static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now)
{
u32 bytes, segs;
bytes = min(sk->sk_pacing_rate >> 10,
sk->sk_gso_max_size - 1 - MAX_TCP_HEADER);
/* Goal is to send at least one packet per ms,
* not one big TSO packet every 100 ms.
* This preserves ACK clocking and is consistent
* with tcp_tso_should_defer() heuristic.
*/
segs = max_t(u32, bytes / mss_now, sysctl_tcp_min_tso_segs);
return min_t(u32, segs, sk->sk_gso_max_segs);
}
Я попытался на самом деле достичь ограничения в 34 Гбит/с, но, к сожалению, без загрузки ЦП TSO на стороне TX ограничение полосы пропускания одного сокета до ~23 Гбит/с.
Патч Эрика Думазе tcp :внутренняя реализация для стимуляции был представлен в ядре 4.13.0 -rc1. Этот патч расширяет возможности TCP PACING. В кои-то веки это позволяет выполнять стимуляцию без использования TC -FQ. Он также полностью отключает стимуляцию, если в поле скорости sk _pacing _установлено значение 0 или ~0U (по умолчанию ). См. обложку патча и код для более подробной информации.
Это не совсем то, что вам нужно, но должно подойти для вашего варианта использования. Считайте строку в шесть переменных, разделенных пробелом и символом табуляции, определяемым переменной IFS
.
Пример с измененной записью crontab:
$ line='0 22 * * 1-5 echo "foo bar baz"'
$ IFS=$'\t ' read -r a b c d e cmd <<<"$line"
$ echo "$a $b $c $d $e"
0 22 * * 1-5
$ echo "$cmd"
echo "foo bar baz"
Обратите внимание, что несколько пробелов (или табуляции )в первых пяти полях сжимаются до одного символа пробела, но пробелы/табуляции в команде остаются нетронутыми. При необходимости вы можете присвоить "$a $b $c $d $e"
одной переменной.
Я думаю, вы могли бы сделать это с помощью awk
, в частности, поскольку ваши теги указывают, что вы хотите использовать это в сценарии оболочки. Взгляните на следующий фрагмент кода, где я, как и другие ответчики, ради аргумента назначу переменную bash с содержимым, которое вы, вероятно, обработаете:
#!/bin/bash
# fill 'line' with example content
line='0 22 * * 1-5 echo foo bar baz -o outfile.txt *'
# split 'line' into "scheduling" and "command" part:
SCHED=$(awk '{match($0, "^([0-9* -]*)([[:print:]]*)", a); print a[1]}' <<< "$line")
echo "$SCHED"
CMD=$(awk '{match($0, "^([0-9* -]*)([[:print:]]*)", a); print a[2]}' <<< "$line")
echo "$CMD"
Если вы проверите это, он должен вывести
user@host $./split_test.sh
0 22 * * 1-5
echo foo bar baz -o outfile.txt *
Объяснить
$(... )
части (, см., например. Advanced Bash Scripting Guide для примеров ), в котором я передаю содержимое переменной $line
, защищенной от подстановки двойными круглыми скобками("... "
' )до awk
(, так что, среди прочего,белое -пространство сохраняется "как есть" ). awk
использует внутреннюю функцию match()
для поиска определенных шаблонов в строке, в нашем случае это одна группа (... )
в начале строки, содержащая любую комбинацию цифр, звездочек, пробелов и тире, до неограниченного количества, за которым следует группа, содержащая любые печатные символы, также до неограниченного количества. Поскольку регулярные выражения жадны, обеим частям будет соответствовать максимально длинная подстрока -, поэтому группа 1 действительно продолжается до первого символа, который не помещается в нее (, то есть до первого буквенно-цифрового символа или даже ./
). ] находится. Вторая группа — это просто оставшаяся часть строки, которая может содержать все печатные символы, включая цифры и специальные символы. awk
's match
заключается в том, что если вы указали подгруппы -, заключив их в круглые скобки, вы можете указать awk
заполнить фактическое значение шаблонов в массив, в данном примере a
, такой, что a[1]
содержит фактическое значение группы 1 (часть «планирования» ), а a[2]
фактическое значение группы 2 («команда часть ). awk
выводя первую часть в первом вызове, а вторую часть во втором вызове, чтобы присвоить значения отдельным переменным bash.. Если вы хотите узнать больше об этом, вы можете взглянуть на Руководство пользователя GNU Awk .
В Crontab есть пять полей, описывающих время, остальные — ваши команды. См. здесь .
Итак, если вы хотите отделить поля времени (1 -5 )от команды, вам просто нужно разделить по 5-му разделителю полей.
sed "s/\s+/\n/5" <<<'1 2 3 * * echo hello * 7 8 9'
1 2 3 * *
echo hello * 7 8 9
Использование новой строки \n
позволяет читать две строки в массив без цикла
readarray cron <<< $(sed 's/ /\n/5' <<<'1 2 3 * * echo hello * 7 8 9')
Это зависит от того, что \n
не появляется в качестве escape-последовательности или внутри строки в вашем crontab
, хотя вы всегда можете объединить элементы cron[1..n]
для повторной сборки команды, если это произойдет.
Вы можете использовать cut для разделения значений, разделенных пробелом, например:
#/bin/sh
var='0 22 * * 1-5 echo hello'
first=$(echo -n "$var" | cut -d' ' -f1)
second=$(echo -n "$var" | cut -d' ' -f2)
third=$(echo -n "$var" | cut -d' ' -f3)
fourth=$(echo -n "$var" | cut -d' ' -f4)
fifth=$(echo -n "$var" | cut -d' ' -f5)
restofvar=$(echo -n "$var" | cut -d' ' -f6,7)
E :Как было указано в комментарии, если в строке несколько пробелов, вы должны заменить их одним пробелом:
var=$(echo "$var" | tr -s " ")
Это можно сделать только с помощью Расширение параметров оболочки и Сопоставление шаблонов
$ line='0 22 * * 1-5 echo hello'
$ prefix=${line%%[[:alpha:]]*} #(a)
$ echo "$prefix"
0 22 * * 1-5
$ suffix=${line#"$prefix"}. #(b)
$ echo "$suffix"
echo hello
a :с конца, найти самую длинную подстроку, соответствующую альфе, за которой следует любой символ, и удалить ее из $line
b :с самого начала удалите префикс test из $line.