Discussion:
getrlimit RLMIT_NPROC
(too old to reply)
Kenny McCormack
2023-08-29 14:13:37 UTC
Permalink
I have the following C program:

--- Cut Here ---
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>

int main(void)
{
struct rlimit rlim;
printf("Result: %d\n",getrlimit(RLIMIT_NPROC, &rlim));
printf("cur = %lu, max = %lu\n",rlim.rlim_cur,rlim.rlim_max);
printf("RLIM_INFINITY = %ld\n",RLIM_INFINITY);
return 0;
}
--- Cut Here ---

When run on system A (Ubuntu - x64), the output is:

Result: 0
cur = 31411, max = 31411
RLIM_INFINITY = -1

What the heck is 31411???


When run on system B (Raspberry PiOS - ARM32), the output is:

Result: 0
cur = 6807, max = 6807
RLIM_INFINITY = -1

What the heck is 6807???

Neither of these results seem reasonable or tied to anything.

What is going on?
--
Kenny, I'll ask you to stop using quotes of mine as taglines.

- Rick C Hodgin -
Kalevi Kolttonen
2023-08-29 15:24:08 UTC
Permalink
Post by Kenny McCormack
Neither of these results seem reasonable or tied to anything.
On my Fedora 38 amd64 "uname -a" shows e.g.

6.4.12-200.fc38.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Aug 23 17:46:49 UTC 2023 x86_64 GNU/Linux

and the result of running your program is:

Result: 0
cur = 60988, max = 60988
RLIM_INFINITY = -1
Post by Kenny McCormack
What is going on?
You used "%lu" and that is correct based on the Linux
headers. On my Fedora 38 we have these:

struct rlimit {
__kernel_ulong_t rlim_cur;
__kernel_ulong_t rlim_max;
};

#define RLIM64_INFINITY (~0ULL)

struct rlimit64 {
__u64 rlim_cur;
__u64 rlim_max;
};


Using grep, I was unable to find where 60988 is set
on my Fedora 38. So my best guess is that this value
has been set when compiling the kernel.

Have you tried using a recursive grep invocation
to find the values you mentioned?

br,
KK
Kalevi Kolttonen
2023-08-29 15:45:06 UTC
Permalink
Post by Kalevi Kolttonen
Using grep, I was unable to find where 60988 is set
on my Fedora 38. So my best guess is that this value
has been set when compiling the kernel.
I downloaded the current Fedora 38 kernel sources. The
value 60988 is nowhere to be found. Some possible reasons:

1) Maybe 60988 value was given as a command line parameter
when running "make". I suppose this is quite unlikely and
I did not investigate the Makefiles to see whether that
is even supported.

2) Maybe the Linux kernel sets the default value based
on how much RAM is available.

br,
KK
Kalevi Kolttonen
2023-08-29 16:01:38 UTC
Permalink
Post by Kalevi Kolttonen
2) Maybe the Linux kernel sets the default value based
on how much RAM is available.
Inspecting kernel/fork.c, we see:

static void set_max_threads(unsigned int max_threads_suggested)
{
u64 threads;
unsigned long nr_pages = totalram_pages();

/*
* The number of threads shall be limited such that the thread
* structures may only consume a small part of the available memory.
*/
if (fls64(nr_pages) + fls64(PAGE_SIZE) > 64)
threads = MAX_THREADS;
else
threads = div64_u64((u64) nr_pages * (u64) PAGE_SIZE,
(u64) THREAD_SIZE * 8UL);

if (threads > max_threads_suggested)
threads = max_threads_suggested;

max_threads = clamp_t(u64, threads, MIN_THREADS, MAX_THREADS);
}

It seems to me that that eventually sets "max_threads" based on
available RAM, with the limitation that "max_threads_suggested"
must not be exceeded.

Further, fork_init() we have:

init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;

It looks like RLIMIT_NRPOC limits are based on the value of max_threads/2.

In conclusion, I believe the values you and I saw are based on how much
RAM our Linux systems have.

br,
KK
Kaz Kylheku
2023-08-29 19:01:25 UTC
Permalink
Post by Kalevi Kolttonen
Post by Kalevi Kolttonen
Using grep, I was unable to find where 60988 is set
on my Fedora 38. So my best guess is that this value
has been set when compiling the kernel.
I downloaded the current Fedora 38 kernel sources. The
1) Maybe 60988 value was given as a command line parameter
when running "make". I suppose this is quite unlikely and
I did not investigate the Makefiles to see whether that
is even supported.
Pretty much nobody is going to do

#define DEFAULT_THING 60988 // or other weird number.

Plus, we are seeing different numbers on different systems
Post by Kalevi Kolttonen
2) Maybe the Linux kernel sets the default value based
on how much RAM is available.
Precisely. It looks calculated. You have to find the area of the kernel
where the rlimit sysdcalls are implemented. Find out which fields in the
task struct they are coming from, then search where those are set up.

I'm looking at 4.9.211 (a kernel I work with in in my embedded work).

In kernel/fork.c, there is a fork_init() function with these lines:

init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;

max_threads is a tunable global variable initialized earlier
in that same function by call to set_max_threads(MAX_THREADS).
That function only uses the parameter as an upper bound clamp.

It perpetrates a complex calculation from totalram_pages and
thread context size:

static void set_max_threads(unsigned int max_threads_suggested)
{
u64 threads;

/*
* The number of threads shall be limited such that the thread
* structures may only consume a small part of the available memory.
*/
if (fls64(totalram_pages) + fls64(PAGE_SIZE) > 64)
threads = MAX_THREADS;
else
threads = div64_u64((u64) totalram_pages * (u64) PAGE_SIZE,
(u64) THREAD_SIZE * 8UL);

if (threads > max_threads_suggested)
threads = max_threads_suggested;

max_threads = clamp_t(u64, threads, MIN_THREADS, MAX_THREADS);
}
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Kalevi Kolttonen
2023-08-29 19:25:21 UTC
Permalink
Post by Kaz Kylheku
Post by Kalevi Kolttonen
Post by Kalevi Kolttonen
Using grep, I was unable to find where 60988 is set
on my Fedora 38. So my best guess is that this value
has been set when compiling the kernel.
I downloaded the current Fedora 38 kernel sources. The
1) Maybe 60988 value was given as a command line parameter
when running "make". I suppose this is quite unlikely and
I did not investigate the Makefiles to see whether that
is even supported.
Pretty much nobody is going to do
#define DEFAULT_THING 60988 // or other weird number.
If you read closely, that is not what I suggested. I only
suggested that the weird value 60988 was maybe specified
for "make" using command-line. So the #define:d value in the
source tree could have been something saner.

In any case, I pretty much rejected the idea anyway. It
is not likely that Red Hat/Fedora maintainers would
specify anything using "make" command-lines.
Post by Kaz Kylheku
Plus, we are seeing different numbers on different systems
Yes, but that does not disprove my theory above. Different
numbers could easily be explained by different distros
having different maintainers. They could have set different
default values. But again, I did not take that case
very seriously.
Post by Kaz Kylheku
Post by Kalevi Kolttonen
2) Maybe the Linux kernel sets the default value based
on how much RAM is available.
Precisely. It looks calculated. You have to find the area
of the kernel where [...]
I already did and posted the results, but Usenet being
the way it is, my article has not reached you yet.

Your findings seem pretty much identical to mine.

The only question that remains is whether Kenny's
/etc/security/limits.conf has any active settings,
but somehow I doubt it.

br,
KK
Scott Lurndal
2023-08-29 17:08:01 UTC
Permalink
Post by Kalevi Kolttonen
Post by Kenny McCormack
Neither of these results seem reasonable or tied to anything.
On my Fedora 38 amd64 "uname -a" shows e.g.
6.4.12-200.fc38.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Aug 23 17:46:49 UTC 2023 x86_64 GNU/Linux
Result: 0
cur = 60988, max = 60988
RLIM_INFINITY = -1
Post by Kenny McCormack
What is going on?
You used "%lu" and that is correct based on the Linux
struct rlimit {
__kernel_ulong_t rlim_cur;
__kernel_ulong_t rlim_max;
};
#define RLIM64_INFINITY (~0ULL)
struct rlimit64 {
__u64 rlim_cur;
__u64 rlim_max;
};
Using grep, I was unable to find where 60988 is set
on my Fedora 38. So my best guess is that this value
has been set when compiling the kernel.
Have you tried using a recursive grep invocation
to find the values you mentioned?
While the defaults are compiled into the kernel, the
login values are generally specified in
/etc/security/limits.conf on systems that use PAM authentication.

$ man pam_limits
Kalevi Kolttonen
2023-08-29 17:22:52 UTC
Permalink
Post by Scott Lurndal
While the defaults are compiled into the kernel,
I just checked out some of the Linux kernel
sources and I am pretty convinced that the
RLIMIT_PROC defaults are actually dynamically
calculated based on available RAM.
Post by Scott Lurndal
the login values are generally specified in
/etc/security/limits.conf on systems that use PAM authentication.
Yes, I know that very well. But in this case they
are irrelevant to the problem discussed.

br,
KK
Kalevi Kolttonen
2023-08-29 17:30:55 UTC
Permalink
Post by Kalevi Kolttonen
Post by Scott Lurndal
the login values are generally specified in
/etc/security/limits.conf on systems that use PAM authentication.
Yes, I know that very well. But in this case they
are irrelevant to the problem discussed.
That was perhaps too strong a statement, because
I have no access to Kenny's system, so I cannot
be 100% sure whether they are specified there.

But certainly my Fedora 38 gets the values from
the Linux kernel and not from the PAM configuration
files.

br,
KK
Lew Pitcher
2023-08-29 17:39:17 UTC
Permalink
Post by Kalevi Kolttonen
Post by Scott Lurndal
While the defaults are compiled into the kernel,
I just checked out some of the Linux kernel
sources and I am pretty convinced that the
RLIMIT_PROC defaults are actually dynamically
calculated based on available RAM.
Post by Scott Lurndal
the login values are generally specified in
/etc/security/limits.conf on systems that use PAM authentication.
Yes, I know that very well. But in this case they
are irrelevant to the problem discussed.
I would disagree; Scott's point is quite relevant to Kenny's
(very information free) quesion.

More specifically, Kenny only mentioned the values obtained
(31411 and 6807), and asked "What is going on?" Each of those
two values occur in two places: the "curr" limit and the "max"
limit. While others have discussed how the "max" limit is
derived, no one (other than Scott) has mentioned how the "curr"
limit may (or may not) have been set.

AFAICT, Kenny's question was open-ended enough that Scott's
answer is as on-topic as the rest of the discussion.

FWIW, as Kenny asked "What the heck is 31411???" and
"What the heck is 6807???", /I/ was tempted to answer literally,
and tell him that those values are the current (soft-limit) maximum,
and hard maximum of the number of processes/threads a single UID
can launch. But, then again, if Kenny has already coded the
getrlimit() call for RLIMIT_NPROC, he probably already knows this.
--
Lew Pitcher
"In Skills We Trust"
Kalevi Kolttonen
2023-08-29 17:43:06 UTC
Permalink
Post by Lew Pitcher
I would disagree; Scott's point is quite relevant to Kenny's
(very information free) quesion.
More specifically, Kenny only mentioned the values obtained
(31411 and 6807), and asked "What is going on?" Each of those
two values occur in two places: the "curr" limit and the "max"
limit. While others have discussed how the "max" limit is
derived, no one (other than Scott) has mentioned how the "curr"
limit may (or may not) have been set.
Yes, I already posted a new message telling that this
information *could* indeed be relevant in Kenny's case.

But it was not really relevant to my Fedora 38. The values
were not set there, but came from the Linux kernel based
on the amount of available RAM.

So I cannot know for the certain whether Kenny's values
came from the PAM configuration files or from the Linux
kernel, but I suspect the latter.

br,
KK
Kaz Kylheku
2023-08-29 18:39:29 UTC
Permalink
Post by Kenny McCormack
--- Cut Here ---
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
int main(void)
{
struct rlimit rlim;
printf("Result: %d\n",getrlimit(RLIMIT_NPROC, &rlim));
printf("cur = %lu, max = %lu\n",rlim.rlim_cur,rlim.rlim_max);
printf("RLIM_INFINITY = %ld\n",RLIM_INFINITY);
return 0;
}
That's a waste of time; the existing "ulimit" tool reads these
limits. "ulimit -a" gives you all of them; "ulimit -u" gives
us that one ([u]ser processes).
Post by Kenny McCormack
What the heck is 31411???
What the heck is 6807???
Pre-release version of Motrola 6809? ;)

Good question; do these valuas come from some boot scripts on that
system, or are they in the kernel?

Ubuntu 18, 32 bit:

$ ulimit -u
15791

Indeed, weird numbers; it's as if they are concocted by some formula
applied to various other system parameters.

I think you have to hunt down the kernel code where the initial/default
values are prepared.

It could be the work of a boot script, or ... dare I utter it ... systemd.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Kenny McCormack
2023-08-29 21:07:55 UTC
Permalink
In article <***@kylheku.com>,
Kaz Kylheku <864-117-***@kylheku.com> wrote:
...
Post by Kaz Kylheku
That's a waste of time; the existing "ulimit" tool reads these
limits. "ulimit -a" gives you all of them; "ulimit -u" gives
us that one ([u]ser processes).
I think I wrote the C program (it was quite a while ago) because I had been
investigating the shell "ulimit" command and it seemed to be displaying a
bogus result (31411), which I didn't trust. As most of us now see, these
numbers just don't look "normal" (in the computer-ish sense).

So, I wrote the C program, and got the same (bogus-looking) result as the
shell command.
Post by Kaz Kylheku
Post by Kenny McCormack
What the heck is 31411???
What the heck is 6807???
Pre-release version of Motrola 6809? ;)
Good question; do these valuas come from some boot scripts on that
system, or are they in the kernel?
$ ulimit -u
15791
Indeed, weird numbers; it's as if they are concocted by some formula
applied to various other system parameters.
Yup.
Post by Kaz Kylheku
I think you have to hunt down the kernel code where the initial/default
values are prepared.
It could be the work of a boot script, or ... dare I utter it ... systemd.
Heh heh...
--
The randomly chosen signature file that would have appeared here is more than 4
lines long. As such, it violates one or more Usenet RFCs. In order to remain
in compliance with said RFCs, the actual sig can be found at the following URL:
http://user.xmission.com/~gazelle/Sigs/God
Loading...