pthread_create allocates a lot of memory in Linux? - linux

Here's a simple example
#include <iostream>
#include <thread>
#include <vector>
#include <chrono>
void* run(void*)
{
while (true)
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::vector<pthread_t> workers(192);
for (unsigned i = 0; i < workers.size(); ++i)
pthread_create(&workers[i], nullptr, &run, nullptr);
pthread_join(workers.back(), nullptr);
}
top shows 1'889'356 KiB VIRT! I know this isn't resident memory, but still, this is huge amount of memory for a single thread creation.
Is it really so memory-expensive (8MiB in this case) to create a thread? Is this configurable?
Or, maybe and most probably, I have some misunderstanding what virtual memory is?
Details:
I double quadruple-checked the memory usage, using:
generated a core dump of the running exe, it's also 1.6GB;
valgrind --tool=massif also confirms this size;
pmap -x <pid> also confirms the size.
As this size matches the max size of a stack (also confirmed by /proc/<pid>/limits), I tried to make the max size of the stack smaller. Tried with 1 MiB, but this didn't change anything.
Please, put aside the creation and usage of 192 threads, it has a reason behind it.
Sorry for the mixed C and C++ - initially tried with std::thread and the results are the same.

pthread_attr_setstacksize() function is available to set stack size.
This function have to be used with an thread attribute object.
The thread attribute object has to be passed as 2nd argument of pthread_create().
#include <iostream>
#include <thread>
#include <vector>
#include <chrono>
void* run(void*)
{
while (true)
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::vector<pthread_t> workers(192);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 16384);
for (unsigned i = 0; i < workers.size(); ++i)
pthread_create(&workers[i], &attr, &run, nullptr);
pthread_join(workers.back(), nullptr);
}

Related

Crash system when the module is running

I need to write a module that creates a file and outputs an inscription with a certain frequency. I implemented it. But when this module is running, at some point the system crashes and no longer turns on.
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/timer.h>
MODULE_LICENSE("GPL");
#define BUF_LEN 255
#define TEXT "Hello from kernel mod\n"
int g_timer_interval = 10000;
static struct file *i_fp;
struct timer_list g_timer;
loff_t offset = 0;
char buff[BUF_LEN + 1] = TEXT;
void timer_rest(struct timer_list *timer)
{
mod_timer(&g_timer, jiffies + msecs_to_jiffies(g_timer_interval));
i_fp = filp_open("/home/hajol/Test.txt", O_RDWR | O_CREAT, 0644);
kernel_write(i_fp, buff, strlen(buff), &offset);
filp_close(i_fp, NULL);
}
static int __init kernel_init(void)
{
timer_setup(&g_timer, timer_rest, 0);
mod_timer(&g_timer, jiffies + msecs_to_jiffies(g_timer_interval));
return 0;
}
static void __exit kernel_exit(void)
{
pr_info("Ending");
del_timer(&g_timer);
}
module_init(kernel_init);
module_exit(kernel_exit);
When the system crashes, you should get a very detailed error message from the kernel, letting you know where and why this happened (the "oops" message):
Read that error message
Read it again
Understand what it means (this often requires starting over from step 1 a couple of times :-) )
One thing that jumps out at me is that you're not going any error checking on the return value of filp_open. So you could very well be feeding a NULL pointer (or error pointer) into kernel_write.

Implementing a system call for CPU hotplug on RPI3/ModelB

My goal is to implement a system call in linux kernel that enables/disables a CPU core.
First, I implemented a system call that disbales CPU3 in a 4-core system.
The system call code is as follows:
#include <linux/kernel.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <linux/cpumask.h>
asmlinkage long sys_new_syscall(void)
{
unsigned int cpu3 = 3;
set_cpu_online (cpu3, false) ; /* clears the CPU in the cpumask */
printk ("CPU%u is offline\n", cpu3);
return 0;
}
The system call was registered correctly in the kernel and I enabled 'cpu hotplug' feature during kernel configuration ( See picture )
Kernel configuration:
The kernel was build . But when I check the system call using test.c :
#include <stdio.h>
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
long new_syscall(void)
{
return syscall(394);
}
int main(int argc, char *argv[])
{
long int a = new_syscall();
printf("System call returned %ld\n", a);
return 0;
}
The OS frezzes !
What am I doing wrong ?
why would you want to implement a dedicated syscall? the standard way of offlining cpus is through writes to sysfs. in the extremely unlikely case there is a valid reason to create a dedicated syscall you will have to check how offlining works under the hood and repeat that.
set_cpu_online (cpu3, false) ; /* clears the CPU in the cpumask */
your own comment strongly suggests this is too simplistic. for instance what if the thread executing this is running on said cpu? what about threads which are queued on it?
and so on
This is kind of an old topic, but you can put a CPU up/down in kernel land by using the functions cpu_up(cpu_id) and cpu_down(cpu_id), from include/linux/cpu.h.
It seems that set_cpu_online is not exported since it doesn't seems to be safe from other kernel parts stand point (it doesn't consider process affinity and other complexities, for example).
So, your system call could be written as:
asmlinkage long sys_new_syscall(void)
{
unsigned int cpu3 = 3;
cpu_down(cpu3) ; /* clears the CPU in the cpumask */
printk ("CPU%u is offline\n", cpu3);
return 0;
}
I have an example module using those methods here: https://github.com/pappacena/cpuautoscaling.

Shared Memory Fork Process Learning in Linux

Good day to all! I am just trying to learn more about parent and child processes in Linux using the fork () function.
I am trying to make a very simple program where after setting up the shared memory segment, i can get a result from a child and output it in the parent .
My problem is it does not seem to work. Here is what i have so far
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#define SZ 20
typedef struct
{
int size;
int cz[SZ];
}shared_data;
shared_data* Collatz(int);
int main (void)
{
pid_t pid;
int seg_id,size=sizeof(shared_data);
seg_id=shmget(IPC_PRIVATE,size,S_IRUSR | S_IWUSR);
shared_data *sd=(shared_data *)shmat(seg_id,NULL, 0);
int usr=-1,count,i;
while(usr<1 ||usr >9)
{
printf("Please Enter a Number between 1-9:");
scanf("%d",&usr);
}
pid=fork();
if(pid<0)
{
printf("Fork Failed");
return 1;
}
if(pid==0)
{
sd=Collatz(usr);
shmdt(sd);
}
else
{
wait(NULL);
printf("\nThe Sequence is: %d ",count);
for(i=0;i<sd->size;i++)
{
printf(" %d ",sd->cz[i]);
}
printf("\n");
}
return 0;
}
shared_data* Collatz(int val)
{
int i=0;
shared_data *data=malloc(sizeof(shared_data));
data->cz[i]=val;
while(val!=1)
{
i++;
if(val%2==0)
val=val/2;
else
val=(3*val)+1;
data->cz[i]=val;
}
data->size=i;
return data;
}
You are assigning to the memory allocated with malloc, not the memory allocated with shmget/shmat. I'm not 100% sure what you intended, but it may be that simply changing the assignment in the child to the following would do the trick. (This will overlay the shared memory with the mallocd content that you initialized in Collatz().)
*sd=Collatz(usr);
[Edit: I should add that your current code sd=Collatz(usr) is instead overwriting the pointer value you got back from the shmat() call rather than the pointed-to memory area.]

Reserve a virtual address range in dynamic linker

Is there a way to reserve a particular range of virtual address space in a process memory map to stop ld.so (dynamic linker) from loading any shared objects into that range. Something like a system wide configuration option that reserves a particular range.
I want to be able to map a region of shared memory into exactly the same virtual address space in several processes so that my pointers in my data-structures will still work. I know I could redesign to use offsets instead of pointers but I don't want to do that.
You can do this by creating a simple shared object and running it via LD_PRELOAD. Compile the following code:
#include <sys/mman.h> // for mmap, munmap, and related constants
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
void my_program_init() __attribute__((constructor));
void *const address = ((void*)0x10000000);
const int size = 0x1000;
void my_program_init() {
printf("Hello from my_program_init!\n");
int fd = shm_open("/mysharedmem", O_CREAT | O_RDWR, 0666);
if (fd == -1) {
printf("shm_open\n");
return;
}
if (ftruncate(fd, size) == -1) {
printf("ftruncate\n");
return;
}
void* shared_mem = mmap(address, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
if (shared_mem == MAP_FAILED) {
printf("mmap\n");
return;
}
return;
}
with the following options:
gcc -shared -fPIC -o libmylib.so myso.c
Then you can run your program like this:
LD_PRELOAD=./libmylib.so ./your_prog
The so is then loaded before any runtime linking happens in your program. The function in the so tagged as a constructor runs immediately and uses mmap to reserve the memory you want for your shared block.
You can see this working with the following example program:
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
int main() {
char *data = (char*)0x10000000;
const char *message = "Hello, world!\n";
memcpy(data, message, strlen(message));
printf("Wrote %ld bytes to memory at address %p %s\n", strlen(message), data, data);
return 0;
}
If you run this without the LD_PRELOAD it will segfault, but if you include the preload the shared block of memory is available as expected.
$ LD_PRELOAD=./libmylib.so ./a.out
Hello from my_program_init!
Wrote 14 bytes to memory at address 0x10000000 Hello, world!
You can construct your own tests to validate that the memory block is actually shared but the easiest check is to recompile the test program again without the memcpy and see that the string is still there from the first run of the program.

linux ptrace() get function information

i want to catch information from user defined function using ptrace() calls.
but function address is not stable(because ASLR).
how can i get another program's function information like gdb programmatically?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <dlfcn.h>
#include <errno.h>
void error(char *msg)
{
perror(msg);
exit(-1);
}
int main(int argc, char **argv)
{
long ret = 0;
void *handle;
pid_t pid = 0;
struct user_regs_struct regs;
int *hackme_addr = 0;
pid = atoi(argv[1]);
ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
if(ret<0)
{
error("ptrace() error");
}
ret = waitpid(pid, NULL, WUNTRACED);
if(ret<0)
{
error("waitpid ()");
}
ret = ptrace(PTRACE_GETREGS, pid, NULL, &regs);
if(ret<0)
{
error("GETREGS error");
}
printf("EIP : 0x%x\n", (int)regs.eip);
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return 0;
}
ptrace is a bit ugly, but it can be useful.
Here's a ptrace example program; it's used to make I/O-related system calls pause.
http://stromberg.dnsalias.org/~strombrg/slowdown/
You could of course also study gdb, but ISTR it's pretty huge.
You might also check out strace and ltrace, perhaps especially ltrace since it lists symbols.
HTH
You probably want to call a function that resides in a specific executable (probably, a shared object). So, first, you will have to find the base address this executable is mapped on using
/proc/pid/maps
After that, you need to find the local offset of the function you are interested in, and you can do this in two ways:
Understand the ELF file format (Linux native executable format), and searching the desired function using the mapped file (This requires some specialty)
Using a ready to use elfparser (probably readelf tool) to get the function offset under the executable. Note that you will have to figure out the real local offset since this tool usually gives you the address as if the executable was mapped to a specific address

Resources