Why does my parent process also crash after exec()? - c

I am trying to use clone() to create a child process to exec() some programs. I know that exec() replaces the original process and the process that calls it should end with it, so I use a child process to call exec(). However, for some reasons, after exec(), my parent process crashes also. Could someone tell me why is this happening? (if i replace clone with fork or vfork, it works)
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sched.h>
void delNL(char * arry){ //A function to delete the newline at the end of the array
char * position;
position = strchr(arry,'\n');
*position = '\0';
}
int mySysC(char * command){ //clone funtion
char *cmd[10]={" "};
int nb=0;
int cnb=0;
while(command[nb]!='\0'){
char coa[10];
int cici=0;
while(command[nb]!=' ' && command[nb]!='\0'){
coa[cici]=command[nb];
nb++;
cici++;
}
coa[cici]='\0';
char *nad=(char *)malloc(10);
strcpy(nad,coa);
cmd[cnb]=nad;
cnb++;
if(command[nb]==' '){
nb++;
}
}
cmd[cnb]=NULL;
execvp(cmd[0],cmd);
exit(0);
}
void my_system_c(char * command){ //clone version
void * stack = (void *)malloc(10000);
void * stackTop = stack + 100000;
pid_t pid = clone((void *)mySysC(command),stackTop,CLONE_THREAD,NULL); //clone
waitpid(pid,NULL,0);
}
int main(){
char commdd[100];
char ex[10]="os_exit";
while(1){
printf("Please enter your command or enter \"os_exit\" to exit:\n");
fgets(commdd,100,stdin);
delNL(commdd);
if(strlen(commdd)>0 && strcmp(commdd,ex)!=0){
my_system_c(commdd); //select version
}
else if(strcmp(commdd,ex)==0) break;
else printf("Empty command\n");
}
return 0;
}
execvp(cmd[0],cmd); this is what crashed the whole program. I add two prints one before and one after, the later one never runs. I don't understand because of I thought clone works just like fork, which creates a new process, and the end of the chile process won't affect the parent?
Thanks!!!

clone(…CLONE_THREAD…) is not the clone() you're looking for. It creates a new process in the same thread group as the parent process, and:
If any of the threads in a thread group performs an execve(2), then all threads other than the thread group leader are terminated, and the new program is executed in the thread group leader.
If you are looking for a way to start a process without using fork(), consider using posix_spawn() instead.
Additionally, the stack pointer you are passing to clone() is invalid. The stack you allocate is 10,000 bytes large, but the stack pointer is 100,000 bytes beyond the start of the stack -- and 90,000 bytes beyond its end.

This happens because you never actually call clone. This part:
clone((void *)mySysC(command), ...);
is equivalent to:
int result = mSysC(command);
void* first = (void*) result;
clone(first, ...);
so it calls your function before it ever calls clone. You need to pass it as a function pointer instead.
In addition to that, you should remove one zero from your stackTop to match the malloc, and avoid passing CLONE_THREAD since you want a new process:
void my_system_c(char * command){ //clone version
void * stack = (void *)malloc(10000);
void * stackTop = stack + 10000;
pid_t pid = clone(mySysC,stackTop,0,command); //clone
waitpid(pid,NULL,0);
}

Related

exec causes the heap and stack and other parts of the memory space of the program re-initialized

I am reading the Operating Systems: Three Easy Pieces book, Chapter 5.
It says:
The fork() system call is strange; its partner in crime, exec(), is
not so normal either. What it does: given the name of an executable
(e.g., wc), and some arguments (e.g., p3.c), it loads code (and static
data) from that executable and overwrites its current code segment
(and current static data) with it; the heap and stack and other parts
of the memory space of the program are re-initialized.
Then I have a question with this sample code in this chapter:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>
int
main(int argc, char *argv[])
{
int rc = fork();
if (rc < 0) {
// fork failed; exit
fprintf(stderr, "fork failed\n");
exit(1);
} else if (rc == 0) {
// child: redirect standard output to a file
close(STDOUT_FILENO);
open("./p4.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
// now exec "wc"...
char *myargs[3];
myargs[0] = strdup("wc"); // program: "wc" (word count)
myargs[1] = strdup("p4.c"); // argument: file to count
myargs[2] = NULL; // marks end of array
execvp(myargs[0], myargs); // runs word count
} else {
// parent goes down this path (original process)
int wc = wait(NULL);
assert(wc >= 0);
}
return 0;
}
According to man strdup, margs[0] and margs[1] are created with malloc on the heap. So when execvp reinitialize the heap and stack the child's memory space, won't they be cleared or destroyed so as a result using margs[0] and margs[1] would be undefined behaviour?
The newly created process makes a copy of the arguments from the myargs array before the old process memory is zapped, precisely so there is no problem with memory accesses.
The POSIX specification for excevp() et al says:
The arguments specified by a program with one of the exec functions shall be passed on to the new process image in the corresponding main() arguments.
That page specifies a lot of other key behaviours of the exec() family of functions. You'll probably find that the Linux equivalent page specifies even more things that are affected (or not affected) by the exec() family of functions.
Note that if a function from the exec() family succeeds, it does not return. If it returns, it failed. There's no need to check the return value (because it will always be -1). But there is usually a need to report that the execution failed and most often, a process exits with a non-zero status after a failed exec().

Strange behavior of clone

This is fairly simple application which creates a lightweight process (thread) with clone() call.
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#define STACK_SIZE 1024*1024
int func(void* param) {
printf("I am func, pid %d\n", getpid());
return 0;
}
int main(int argc, char const *argv[]) {
printf("I am main, pid %d\n", getpid());
void* ptr = malloc(STACK_SIZE);
printf("I am calling clone\n");
int res = clone(func, ptr + STACK_SIZE, CLONE_VM, NULL);
// works fine with sleep() call
// sleep(1);
if (res == -1) {
printf("clone error: %d", errno);
} else {
printf("I created child with pid: %d\n", res);
}
printf("Main done, pid %d\n", getpid());
return 0;
}
Here are results:
Run 1:
➜ LFD401 ./clone
I am main, pid 10974
I am calling clone
I created child with pid: 10975
Main done, pid 10974
I am func, pid 10975
Run 2:
➜ LFD401 ./clone
I am main, pid 10995
I am calling clone
I created child with pid: 10996
I created child with pid: 10996
I am func, pid 10996
Main done, pid 10995
Run 3:
➜ LFD401 ./clone
I am main, pid 11037
I am calling clone
I created child with pid: 11038
I created child with pid: 11038
I am func, pid 11038
I created child with pid: 11038
I am func, pid 11038
Main done, pid 11037
Run 4:
➜ LFD401 ./clone
I am main, pid 11062
I am calling clone
I created child with pid: 11063
Main done, pid 11062
Main done, pid 11062
I am func, pid 11063
What is going on here? Why "I created child" message is sometimes printed several times?
Also I noticed that adding a delay after clone call "fixes" the problem.
You have a race condition (i.e.) you don't have the implied thread safety of stdio.
The problem is even more severe. You can get duplicate "func" messages.
The problem is that using clone does not have the same guarantees as pthread_create. (i.e.) You do not get the thread safe variants of printf.
I don't know for sure, but, IMO the verbiage about stdio streams and thread safety, in practice, only applies when using pthreads.
So, you'll have to handle your own interthread locking.
Here is a version of your program recoded to use pthread_create. It seems to work without incident:
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#define STACK_SIZE 1024*1024
void *func(void* param) {
printf("I am func, pid %d\n", getpid());
return (void *) 0;
}
int main(int argc, char const *argv[]) {
printf("I am main, pid %d\n", getpid());
void* ptr = malloc(STACK_SIZE);
printf("I am calling clone\n");
pthread_t tid;
pthread_create(&tid,NULL,func,NULL);
//int res = clone(func, ptr + STACK_SIZE, CLONE_VM, NULL);
int res = 0;
// works fine with sleep() call
// sleep(1);
if (res == -1) {
printf("clone error: %d", errno);
} else {
printf("I created child with pid: %d\n", res);
}
pthread_join(tid,NULL);
printf("Main done, pid %d\n", getpid());
return 0;
}
Here is a test script I've been using to check for errors [it's a little rough, but should be okay]. Run against your version and it will abort quickly. The pthread_create version seems to pass just fine
#!/usr/bin/perl
# clonetest -- clone test
#
# arguments:
# "-p0" -- suppress check for duplicate parent messages
# "-c0" -- suppress check for duplicate child messages
# 1 -- base name for program to test (e.g. for xyz.c, use xyz)
# 2 -- [optional] number of test iterations (DEFAULT: 100000)
master(#ARGV);
exit(0);
# master -- master control
sub master
{
my(#argv) = #_;
my($arg,$sym);
while (1) {
$arg = $argv[0];
last unless (defined($arg));
last unless ($arg =~ s/^-(.)//);
$sym = $1;
shift(#argv);
$arg = 1
if ($arg eq "");
$arg += 0;
${"opt_$sym"} = $arg;
}
$opt_p //= 1;
$opt_c //= 1;
printf("clonetest: p=%d c=%d\n",$opt_p,$opt_c);
$xfile = shift(#argv);
$xfile //= "clone1";
printf("clonetest: xfile='%s'\n",$xfile);
$itermax = shift(#argv);
$itermax //= 100000;
$itermax += 0;
printf("clonetest: itermax=%d\n",$itermax);
system("cc -o $xfile -O2 $xfile.c -lpthread");
$code = $? >> 8;
die("master: compile error\n")
if ($code);
$logf = "/tmp/log";
for ($iter = 1; $iter <= $itermax; ++$iter) {
printf("iter: %d\n",$iter)
if ($opt_v);
dotest($iter);
}
}
# dotest -- perform single test
sub dotest
{
my($iter) = #_;
my($parcnt,$cldcnt);
my($xfsrc,$bf);
system("./$xfile > $logf");
open($xfsrc,"<$logf") or
die("dotest: unable to open '$logf' -- $!\n");
while ($bf = <$xfsrc>) {
chomp($bf);
if ($opt_p) {
while ($bf =~ /created/g) {
++$parcnt;
}
}
if ($opt_c) {
while ($bf =~ /func/g) {
++$cldcnt;
}
}
}
close($xfsrc);
if (($parcnt > 1) or ($cldcnt > 1)) {
printf("dotest: fail on %d -- parcnt=%d cldcnt=%d\n",
$iter,$parcnt,$cldcnt);
system("cat $logf");
exit(1);
}
}
UPDATE:
Were you able to recreate OPs problem with clone?
Absolutely. Before I created the pthreads version, in addition to testing OP's original version, I also created versions that:
(1) added setlinebuf to the start of main
(2) added fflush just before the clone and __fpurge as the first statement of func
(3) added an fflush in func before the return 0
Version (2) eliminated the duplicate parent messages, but the duplicate child messages remained
If you'd like to see this for yourself, download OP's version from the question, my version, and the test script. Then, run the test script on OP's version.
I posted enough information and files so that anyone can recreate the problem.
Note that due to differences between my system and OP's, I couldn't at first reproduce the problem on just 3-4 tries. So, that's why I created the script.
The script does 100,000 test runs and usually the problem will manifest itself within 5000-15000.
I can't recreate OP's issue, but I don't think the printf's are actually a problem.
glibc docs:
The POSIX standard requires that by default the stream operations are
atomic. I.e., issuing two stream operations for the same stream in two
threads at the same time will cause the operations to be executed as
if they were issued sequentially. The buffer operations performed
while reading or writing are protected from other uses of the same
stream. To do this each stream has an internal lock object which has
to be (implicitly) acquired before any work can be done.
Edit:
Even though the above is true for threads, as rici points out, there is a comment on sourceware:
Basically, there's nothing you can safely do with CLONE_VM unless the
child restricts itself to pure computation and direct syscalls (via
sys/syscall.h). If you use any of the standard library, you risk the
parent and child clobbering each other's internal states. You also
have issues like the fact that glibc caches the pid/tid in userspace,
and the fact that glibc expects to always have a valid thread pointer
which your call to clone is unable to initialize correctly because it
does not know (and should not know) the internal implementation of
threads.
Apparently, glibc isn't designed to work with clone if CLONE_VM is set but CLONE_THREAD|CLONE_SIGHAND are not.
Your processes both use the same stdout (that is, the C standard library FILE struct), which includes an accidentally shared buffer. That's undoubtedly causing problems.
Ass everyone suggests: it really seems to be a problem with, how shall I put it in case of clone(), process-safety? With a rough sketch of a locking version of printf (using write(2)) the output is as expected.
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#define STACK_SIZE 1024*1024
// VERY rough attempt at a thread-safe printf
#include <stdarg.h>
#define SYNC_REALLOC_GROW 64
int sync_printf(const char *format, ...)
{
int n, all = 0;
int size = 256;
char *p, *np;
va_list args;
if ((p = malloc(size)) == NULL)
return -1;
for (;;) {
va_start(args, format);
n = vsnprintf(p, size, format, args);
va_end(args);
if (n < 0)
return -1;
all += n;
if (n < size)
break;
size = n + SYNC_REALLOC_GROW;
if ((np = realloc(p, size)) == NULL) {
free(p);
return -1;
} else {
p = np;
}
}
// write(2) shoudl be threadsafe, so just in case
flockfile(stdout);
n = (int) write(fileno(stdout), p, all);
fflush(stdout);
funlockfile(stdout);
va_end(args);
free(p);
return n;
}
int func(void *param)
{
sync_printf("I am func, pid %d\n", getpid());
return 0;
}
int main()
{
sync_printf("I am main, pid %d\n", getpid());
void *ptr = malloc(STACK_SIZE);
sync_printf("I am calling clone\n");
int res = clone(func, ptr + STACK_SIZE, CLONE_VM, NULL);
// works fine with sleep() call
// sleep(1);
if (res == -1) {
sync_printf("clone error: %d", errno);
} else {
sync_printf("I created child with pid: %d\n", res);
}
sync_printf("Main done, pid %d\n\n", getpid());
return 0;
}
For the third time: it's only a sketch, no time for a robust version, but that shouldn't hinder you to write one.
As evaitl points out printf is documented to be thread-safe by glibc's documentation. BUT, this typically assumes that you are using the designated glibc function to create threads (that is, pthread_create()). If you do not, then you are on your own.
The lock taken by printf() is recursive (see flockfile). This means that if the lock is already taken, the implementation checks the owner of the lock against the locker. If the locker is the same as the owner, the locking attempt succeeds.
To distinguish between different threads, you need to setup properly TLS, which you do not do, but pthread_create() does. What I'm guessing happens is that in your case the TLS variable that identifies the thread is the same for both threads, so you end up taking the lock.
TL;DR: please use pthread_create()

Basic Mutex's causing program to lock up in C

I've got a simple C program that uses mutex's to collect a char from the standard input on one thread and print it out on another thread. Both threads start correctly (the printf in the below saying that the thread started runs), but then neither of the while's get run since I introduced Mutex'. Does anyone have an idea as to why? (I collect two chars in my char array as I am collecting the return char as well.)
Thanks!
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
struct thread_info { /* Used as argument to thread_start() */
pthread_t thread_id;/* ID returned by pthread_create() */
char passedChar[2];
pthread_mutex_t passedCharMutex;
pthread_cond_t conditionalSignal;
};
static void *thread_1_start(void *arg) {
struct thread_info *myInfo = arg;
printf("Started thread id: %d\n", myInfo->thread_id);
while (1) {
printf("thread1 while ran");
pthread_mutex_lock(&myInfo->passedCharMutex);
int rid = read(0,myInfo->passedChar,2);
pthread_mutex_unlock(&myInfo->passedCharMutex);
}
pthread_exit(0);
}
int main() {
struct thread_info tinfo;
printf("Main thread id: %d\n", tinfo.thread_id);
int s = pthread_create(&tinfo.thread_id,
NULL, // was address of attr, error as this was not initialised.
&thread_1_start,
&tinfo);
pthread_join(tinfo.thread_id,NULL);
while (1) {
printf("thread2 while ran");
pthread_mutex_lock(&tinfo.passedCharMutex);
write(1,tinfo.passedChar,2);
pthread_mutex_unlock(&tinfo.passedCharMutex);
}
}
pthread_mutex_t and pthread_cond_t must be intitialized before you can use them.
struct thread_info tinfo;
pthread_mutex_init(&tinfo.passedCharMutex, NULL);
pthread_cond_init(&tinfo.conditionalSignal, NULL);
In this case you could initialize them when initializing the tinfo variable too:
struct thread_info tinfo = {
.passedCharMutex = PTHREAD_MUTEX_INITIALIZER,
.conditionalSignal = PTHREAD_COND_INITIALIZER
};
You also have a
pthread_join(tinfo.thread_id,NULL);
after you create your first thread, that will cause you wait until your thread ends, which it never does since thread_1_start() runs an infinite loop - you'll never reach the while loop in main().
While not part of your question, there's additional problems:
There's no synchronization of the logic of your two threads. As it currently stands, they both run without any regard to eachother, so your main() might print out passedChar many times before your thread_1_start() reads anything.
Likewise, thread_1_start() might read a lot of data before your main() thread have a chance to print it.
What you probably want is:
Thread A: read 2 chars
Thread A: signal Thread B that there is 2 chars to process
Thread A: wait until Thread B has processed the 2 chars.
Thread B: wait for thread A to signal that there's 2 chars to process
Thread B: process the 2 chars
Thread B: signal Thread A that we're done process
When thread 1 comes to this line,
int rid = read(0,myInfo->passedChar,2);
it holds the lock and then blocks for input. Tread 2 waits for the thread 1 to exit. And if no input ever comes neither thread will make progress.

Understanding forks in C

I am having some trouble understanding the following simple C code:
int main(int argc, char *argv[]) {
int n=0;
fork();
n++;
printf("hello: %d\n", n);
}
My current understanding of a fork is that from that line of code on, it will split the rest of the code in 2, that will run in parallel until there is "no more code" to execute.
From that prism, the code after the fork would be:
a)
n++; //sets n = 1
printf("hello: %d\n", n); //prints "hello: 1"
b)
n++; //sets n = 2
printf("hello: %d\n", n); //prints "hello: 2"
What happens, though, is that both print
hello: 1
Why is that?
EDIT: Only now it ocurred to me that contrary to threads, processes don't share the same memory. Is that right? If yes, then that'd be the reason.
After fork() you have two processes, each with its own "n" variable.
fork() starts a new process, sharing no variables/memory locations.
It is very similar to what happens if you execute ./yourprogram twice in a shell, assuming the first thing the program does is forking.
At fork() call's end, both the processes might be referring to the same copy of n. But at n++, each gets its own copy with n=0. At the end of n++; n becomes 1 in both the processes. The printf statement outputs this value.
Actually you spawn a new process of the same progarm. It is not the closure kind of thing. You could use pipes to exchange data between parent and child.
You did indeed answer your own question in your edit.
examine this code and everything should be clearer (see the man pages if you don't know what a certain function does):
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int count = 1;
int main(int argc, char *argv[]) {
// set the "startvalue" to create the random numbers
srand(time(NULL));
int pid;
// as long as count is <= 50
for (count; count<=50; count++) {
// create new proccess if count == 9
if (count==9) {
pid = fork();
// reset start value for generating the random numbers
srand(time(NULL)+pid);
}
if (count<=25) {
// sleep for 300 ms
usleep(3*100000);
} else {
// create a random number between 1 and 5
int r = ( rand() % 5 ) + 1;
// sleep for r ms
usleep(r*100000);
}
if (pid==0) {
printf("Child: count:%d pid:%d\n", count, pid);
} else if (pid>0) {
printf("Father: count:%d pid:%d\n", count, pid);
}
}
return 0;
}
happy coding ;-)
The system call forks more than the execution thread: also forked is the data space. You have two n variables at that point.
There are a few interesting things that follow from all this:
A program that fork()s must consider unwritten output buffers. They can be flushed before the fork, or cleared after the fork, or the program can _exit() instead of exit() to at least avoid automatic buffer flushing on exit.
Fork is often implemented with copy-on-write in order to avoid unnecessarily duplicating a large data memory that won't be used in the child.
Finally, an alternate call vfork() has been revived in most current Unix versions, after vanishing for a period of time following its introduction i 4.0BSD. Vfork() does not pretend to duplicate the data space, and so the implementation can be even faster than a copy-on-write fork(). (Its implementation in Linux may be due less to speed reasons than because a few programs actually depend on the vfork() semantics.)

Would this be considered a memory leak?

Consider this pointless program:
/* main.c */
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
int i;
for (i = 0; i < 1024; i++) {
int pid = fork();
int status;
if (pid) {
wait(&status);
}
else {
char *ptr = (char *)malloc(1024*sizeof(char));
char *args[2] = {"Hello, world!", NULL};
execve("/bin/echo", args, NULL);
}
}
}
Would not freeing ptr constitute a memory leak for either main.c or the other program, or is it going to be freed anyway when execve is called?
No.
This is not a memory leak. exec*() will make a local copy of the string data in the args array, then blow away the child process memory image and overlay it with the memory image used by /bin/echo. Essentially all that remains after the exec() is the pid.
Edit:
User318904 brought up the case of exec() returning -1 (i.e., failure). In this case, the child process that has forked but failed to exec does, indeed technically have a memory leak, but as the usual response to a failed exec is to just exit the child process anyways, the memory will be reclaimed by the OS. Still, freeing it is probably a good habit to get into, if for no other reason than that it will keep you from wondering about it later on.
When execve returns -1, yes. Otherwise, maybe.
The allocated memory should be freed by exec. After the call completes you can't access it anyway.

Resources