Live data from Hacker News

In C, how do you know if the dynamic allocation succeeded?

lemire.me

151–160 of 198 posts

Re: In C, how do you know if the dynamic allocation succeeded?

#151
post #99

Earlier quoted context omitted.

Please don’t blindly declare it ‘totally dumb’. If you disallow overcommit, you can end up with a system that can’t fork and run /bin/true, even if there are gigabytes of memory left. Both styles of memory allocation have their uses, and their drawbacks, but please understand them before declaring many OS designers as stupid and dumb.

That's just the thing. fork(2), like much of POSIX, was a mistake. The proper semantics for starting a new process is something like posix_spawn or Win32 CreateProcess, i.e., you specify an executable image to start.

> The proper semantics for starting a new process is..

:) Ah.. I guess when "I was younger" (TM) I was also so dogmatic on most things computer-related.

Anyways, when we need to do something a bit more complex than equivalent of system(), then it quickly becomes evident, that in many cases we need to prepare ourselves for the future execve().

Here's the list of syscalls/c-funcs, which are called in two random projects I maintain, after fork() and before execve() (or execveat() or fexecve()).

  alarm(0); /* disable alarms */
  setenv(); /* A couple of required envs, like MALLOC_PERTURB_ or MALLOC_PERTURB_ */
  prctl(PR_SET_DUMPABLE, 1) /* regarding ptrace()-attach */
  syscall(__NR_personality, ADDR_NO_RANDOMIZE); /* disable ASLR for debugging, if needed */
  socketpair() /* reliable execve success detection, some form of witchcraft */
  setpriority()
  prctl(PR_SET_PDEATHSIG, SIGKILL); /* die upon parents death */
  setrlimit(); /* set of reset rlimits */
  lseek(fd, 0, SEEK_SET); /* rewind input file for this specific subprocess */
  /* prepare arguments (argv) for execve dynamically */
  sysconf(_SC_NPROCESSORS_ONLN); pthread_setaffinity_np(); /* pin subprocess to a list of CPUs */
  /*
  LOTS of functions here
  if we wanted to use net/process/mount namespacing
  e.g:
  assigning IP adddresses to interfaces
  creating custom views of the filesystem tree
  modifying capability sets
  */
  open("/proc/self/oom_score_adj"), write(), close(); /* adjustment of oom score */
  open("/proc/self/fd", O_DIRECTORY); getdents(); fcntl(F_GETFD); fcntl(F_SETFD, FD_CLOEXEC); close() /* closing fds upon exec */
  setsid(); /* new session */
  sigprocmask(empty_set); /* reset signal mask */
  open("/dev/null"); dup2(null, 0..1); /* close fd 0,1,2 */
  prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)
  prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER); /* application of sandboxing */
  and finally execv() or execveat()
Granted, the projects are maintain are probably more on the heavy side of things, when it comes to process manipulation, before execve, but putting all of that in some control structure, would be down to impossible for me. Such structure would have to be so extensible, that it'd have to be some form of VM I guess effectively. So.. having ability to simply call a couple of syscalls from the context of a regular new process, and before execve() is quite good here.

Sure.. maybe we should have some simple form of fork/execv, for those who want to call system() or popen() and not hit the memory overcommit related crashes.

But not as a replacement, rather a new syscall. Even so, debugging while a process creation/execution failed would be madness, given that you simply would get EINVAL, and the failure could be related to any of dozen parameters in a process creation control structure.

Re: In C, how do you know if the dynamic allocation succeeded?

#152

Life is hard for C coders. Kubernetes made writing poor code a breeze. At work we have microservices crashing 20 times a week but SLOs are not affected since traffic is routed to surviving pods. So we can concentrate on churning features fast instead of writing good code.

Better crash hard and early, than crawl forever.

Re: In C, how do you know if the dynamic allocation succeeded?

#153
post #21
post #2

I'm a little disappointed that the article didn't answer the question, or at least try to. A discussion of using read/write vs mincore vs trying to catch a SIGSEGV would've been a nice addition.

The answer is that you kind of can't. You're at the mercy of the OS to give you accurate information, and malloc as an interface isn't set up to distinguish between virtual memory and "actual" memory. We could imagine a separate interface that would allow the OS to communicate this distinction (or hacks like you allude to), but I don't know of any standard approach.

libsigsegv.

Used to catch out of memory errors to start a major GC.

Re: In C, how do you know if the dynamic allocation succeeded?

#154
post #144

Earlier quoted context omitted.

K8s might save you from crashes affecting availability but what about data corruption, logicals errors sound APIs etc.? I'm guessing if code quality is neglected all of these suffer.

Just enough to get a new round of funding, a bonus, then to parachute out and let someone else deal with the bills when they come due

No founding needed, we are corporate owned. The business guys demand features like there's no tomorrow so we have to do some trade offs. If the dev team owned the thing we would have taken other decisions.

Re: In C, how do you know if the dynamic allocation succeeded?

#155
post #144

Earlier quoted context omitted.

K8s might save you from crashes affecting availability but what about data corruption, logicals errors sound APIs etc.? I'm guessing if code quality is neglected all of these suffer.

Code quality is not neglected to the point we have logical errors. Architecture is reasonable and we have an extensive suite of end to end tests and integration tests.

This... sounds better than most companies.

Re: In C, how do you know if the dynamic allocation succeeded?

#157

Earlier quoted context omitted.

wouldn't you just account the COW pages against the parent until they are copied? kicking the can down the road means there isn't any longer a reasonable correction (failing the allocation), but instead we get to drive around randomly trying to find something to kill. this is particularly annoying if you are running a service. there is no hope for it to recover - for example by flushing a cache. instead the OS looks…

> wouldn't you just account the COW pages against the parent until they are copied? That's what Linux does. But how do you return ENOMEM when the copy does happen and now the system is out of memory? Memory writes don't return error codes. The best you could do is send a signal, which is exactly what the OOM killer does.

That's why you ensure you have enough swap to cover the worst case scenario.

You as swap not because you need to actually use it, but on order to be able to guarantee there is enough memory available if the worst case scenario happens. In normal circumstances, swap should never really be utilised.

Re: In C, how do you know if the dynamic allocation succeeded?

#158
This made be curious to check the real and virtual memory of some processes on my laptop (MacBook Air M1).

The real memory size of Safari is ~160MB but virtual memory size is 392GB which doesn't look right. I checked other processes and all the processes have similar virtual memory size which is around ~390GB.

I wonder if this is a bug in Activity Monitor or the virtual memory allocations really are this big for each process.

Re: In C, how do you know if the dynamic allocation succeeded?

#159
post #151
post #99

Earlier quoted context omitted.

That's just the thing. fork(2), like much of POSIX, was a mistake. The proper semantics for starting a new process is something like posix_spawn or Win32 CreateProcess, i.e., you specify an executable image to start.

> The proper semantics for starting a new process is.. :) Ah.. I guess when "I was younger" (TM) I was also so dogmatic on most things computer-related. Anyways, when we need to do something a bit more complex than equivalent of system(), then it quickly becomes evident, that in many cases we need to prepare ourselves for the future execve(). Here's the list of syscalls/c-funcs, which are called in two random project…

I think that in a proper API, we could have:

  int process_handle = spawn("/bin/true", SP_PAUSED); // create process but don't execute, and return a handle
  // most API would take a process handle, thus you could do stuff (such as prctl) on the new process
  unpause(process_handle);
There's a bit of a move in this direction in the Linux API with the PIDFD stuff.
Post reply on HN