I'm trying the code from Tanenbaum's 3e "Modern Operating Systems" and I get compiler errors and warnings:
$ LANG=en_US.UTF-8 cc thread.c
thread.c: In function ‘main’:
thread.c:19:63: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
thread.c:25:1: warning: passing argument 1 of ‘exit’ makes integer from pointer without a cast [enabled by default]
/usr/include/stdlib.h:544:13: note: expected ‘int’ but argument is of type ‘void *’
/tmp/ccqxmMgE.o: In function `main':
thread.c:(.text+0x57): undefined reference to `pthread_create'
collect2: ld returned 1 exit status
This is the code I'm trying
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUMBER_OF_THREADS 10
void *print_hello_world(void *tid)
{
//printf("Hello World. Greetings from thread %d0", tid);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
pthread_t threads[NUMBER_OF_THREADS];
int status, i;
for(i=0; i<NUMBER_OF_THREADS; i++) {
//printf("Main here creating thread %d0", i);
status = pthread_create(&threads[i], NULL, print_hello_world, (void *)i);
if (status != 0) {
//printf("Oops. pthread_create returned error code %d0", status);
exit(-1);
}
}
exit(NULL);
}
Can you help me improve the state of the code so that it will run? There appears to be some errata since the exact code from the book doesn't compile. Thanks
Please link to pthread library, by specifying -lpthread option to your linker.
Also, you should be using pthread_join to wait for all the created threads to complete.
$gcc thread.c -lpthread
This is to link the pthread shared library.
1) You have to link to libpthread to get rid of the linker error:
gcc ..... -lpthread
(note that the -lpthread option must be the last one)!
2) exit(NULL); is wrong; NULL is for pointer types whereas exit wants an int to be supplied; use simply
exit(0);
instead.
The other warnings are just system-dependent pointer and integer size warnings; they can safely be ignored in most cases.
Pl. do not use the exit statement in your main function in this case, since the main may get exited and your threads also will terminate and you may not get the outputs of the print statement in the thread function.
Pl. use pthread_exit instead of exit in main so that even your main thread terminates the other threads can continue.
Related
I have a C program that I am running on my MacOS terminal. All command line tools and GCC compiler have been installed. However for using functions like getpid() or execv() it gives the following error:
execv-test.c:7:35: error: implicit declaration of function 'getpid' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
printf("Pid before execv: %d\n", getpid());
^
execv-test.c:8:2: error: implicit declaration of function 'execv' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
execv("print",NULL);
^
2 errors generated.
The code:
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
printf("The game is never over, John. But there may be some new players now.\n");
printf("Pid before execv: %d\n", getpid());
execv("print",NULL);
printf("Returned from execv call.\n");
return 0;
}
The following Stack Overflow exchange suggested that I write helper functions for the ones that were taken as implicit declarations. However, I am not sure you could do the same with getpid() or execv(). What should I do to make sure this doesn't happen?
PLEASE NOTE: "print" is just another helper file that is supposed to be run once execv() is called.
Note that you are using system calls that are defined in the unistd.h header file. Therefore calling them without including the std library #include <unistd.h> amounts to "implicit declaration" = "calling a function without defining it first".
Having this files:
plusone.c
int op(int i){ return i+1; }
main.c
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **argv){
if (argc<3){
printf("usage %s <library> <number>\n",argv[0]);
exit(1);
}
char *lname = argv[1];
int num = atoi(argv[2]);
void *handle = dlopen(lname, RTLD_LAZY);
if(!handle)
perror("dlopen");
int (*opp)(int);
opp=dlsym(handle, "op");
if(!opp)
perror("dlsym");
printf("number before:%i\nnumber after:%i\n",num,opp(num));
dlclose(handle);
}
Compiled as:
$cc -fPIC -shared -o plusone.so -ldl plusone.c
$cc -o main.exe -ldl -Wpedantic main.c
warning: ISO C forbids assignment between function pointer and ‘void *’ [-Wpedantic]
$ls
main.c main.exe plusone.so main.exe
$main.exe
usage main.exe <library> <number>
$main plusone.so 1
dlopen: Success
dlsym: Success
Segmentation fault
Why is segfault?
As could be seen from the bash output, both the dlopen and dlsym give success (but they should not even output, otherwise that mean, the condition was true, and the returned values from those functions was NULL? - as from condition). But even of the "success" the perror returned, I cannot reproduce the segfault, since do not know where is the bug.
Why is segfault?
Most likely because opp equals NULL the moment opp(num) is trying to be called.
You do not handle errors correctly for the calls to dlopen() and dlysym(), although the code tests the results it does not take the correct actions on failure of those two functions.
This code
void *handle = dlopen(lname, RTLD_LAZY);
if(!handle)
perror("dlopen");
correctly branches on dlopen() returning NULL which indicated an error, but then the code takes the wrong actions.
dlopen() does not set errno, so using perror() to log an error makes no sense, as perror() relies on errno indicating an error, which is does not. So on failure of dlopen() you see perror() printing
dlopen: Success
which is misleading and contractionary to the fact that perror() was called at all, which in fact only happened if dlopen() returned NULL, indicating a failure. If dlopen() would have succeeded, perror() would not have been called at all and nothing would have been printed.
The same mistake appears with the call to dlsym().
To retrieve error info on failure of a member of the dl*() family of functions use dlerror().
For an example on how to correctly and completely implement error handling see below:
void *handle = dlopen(...);
if (!handle)
{
fprintf(stderr, "dlopen: %s\n", dlerror());
exit(EXIT_FAILURE); /* Or do what ever to avoid using the value of handle. */
}
#ifdef DEBUG
else
{
fputs("dlopen: Success\n", stderr);
}
#endif
The same approach should be taken to handle the outcome of dlsym().
Aside of all this and unrelated to the observed behaviour the code misses to call dlclose() when done with using a valid handle.
I was doing a research into the contents of another StackOverflow question and I thought it was a good time to brush up my knowledge of unix system calls.
While experimenting with execvp (WITHOUT fork on purpose) I ran into something that confuses me
I wrote 4 test programs
Program 1
#include <stdio.h>
int main() {
//printf("Doge\n");
execvp("ls");
printf("Foo\n");
return 0;
}
The program works as expected, the contents of the directory are printed and the Foo print statement is not
Program 2
However when I uncomment the first print statement and have the program be this
#include <stdio.h>
int main() {
printf("Doge\n");
execvp("ls");
printf("Foo\n");
return 0;
}
execvp returns a -1 and both print statements are issued. why?
Program 3
I vaguely remember having to use unistd.h when experimenting with unix system calls from college.
So I included it, but not execvp has a different signature and it needed some more args than just the name of the program. So I did this
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Doge\n");
char *const parmList[] = {"ls", NULL};
execvp("ls", parmList);
printf("Foo\n");
return 0;
}
And this works. This has confused me. Why did exec work in the first program?
I also used This as a reference to the system calls.
Finally I wrote
Program 4
#include <stdio.h>
//#include <unistd.h>
int main() {
printf("Doge\n");
char *const parmList[] = {"ls", NULL};
execvp("ls", parmList);
printf("Foo\n");
return 0;
}
Which also works as expected.
Can someone explain what's going on?
With this snippet
#include <stdio.h>
int main() {
execvp("ls");
printf("Foo\n");
return 0;
}
you're invoking undefined behaviour. You're not providing the prototype for execvp which requires an argument list (null terminated) as a second parameter.
Using gcc without any warning option silently uses execvp as implicitly declared, and doesn't check parameters. It just calls the function. The function then looks for a second parameter and encounters... whatever is left of the call stack (or registers, depending on call conventions), that's why a previous printf call can change the behaviour.
Using gcc -Wall gives the following warning:
test.c:5:9: warning: implicit declaration of function 'execvp' [-Wimplicit-function-declaration]
execvp("ls");
Including the proper include (#include <unistd.h>) leads to:
test.c:6:9: error: too few arguments to function 'execvp'
execvp("ls");
^~~~~~
That's why you've got strange behaviour. Don't look further. Use execvp with 2 arguments, period. In your case "Program 3" is the way to go, and always set warning level to the maximum, if possible (gcc and clang: -Wall -Wextra -pedantic -Werror)
I am trying to make sense of a program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
int main(int argc, char** argv) {
volatile double fShared = 0.0;
// create pthread structures - one for each thread
pthread_t t0;
pthread_t t1;
//Arbitrary pointer variables - void* is a pointer to anything
void *thread_res0;
void *thread_res1;
int res0,res1;
//create a new thread AND run each function
// we pass the ADDRESS of the thread structure as the first argument
// we pass a function name as the third argument - this is known as a FUNCTION pointer
// you might want to look at the man pages for pthread_create
res0 = pthread_create(&t0, NULL, do_this, (void*)"");
res1 = pthread_create(&t1, NULL, do_that, (void*)"");
// Two threads are now running independently of each other and this main function
// the function main is said to run in the main thread, so we are actually running
// The main code can now continue while the threads run - so we can simply block
res0 = pthread_join(t0, &thread_res0);
res1 = pthread_join(t1, &thread_res1);
printf ("\n\nFinal result is fShared = %f\n",fShared);
return (EXIT_SUCCESS);
}
It should be noted that do_this and do_that are functions created earlier in the program. I am getting the following errors:
seed#ubuntu:~/Programs$ Homework2OS.c
/tmp/cceiRtg8.O: in function 'main'
Undefined reference to 'pthread_create'
Undefined reference to 'pthread_create'
Undefined reference to 'pthread_join'
Undefined reference to 'pthread_join'
We were given this bit of code to fix. I have found elsewhere the format for the pthread_create constructor. Do I need to actually define it above the main? I was under the impression it was imported with the library.
I would also venture to guess that it has something to do with the fourth parameter? I understand that the first parameter is the location of the thread (defined above), the second is NULLed, the third is the function call but I do not quite understand what the fourth parameter is supposed to be.
What's wrong?
all the code imports is the header for the pthread library. (pthread.h)
What is missing is the linker needs to actually include the library for gcc the parameter -pthread is needed (AT THE END of the list of parameters to gcc.)
It needs to be at the end because the linker processes parameters in the order given on the command line and if the object files are not already processed, then there will not be any unresolved references for the linker to try to resolve via browsing the pthread library.
I.E. this is wrong:
gcc -g -o myprogram -lpthread myprogram.o
This is correct:
gcc -g -o myprogram myprogram.o -lpthread
I have used the following code to create two threads:
//header files
#include <pthread.h>
struct thread_arg
{
int var1;
int var2;
};
void *serv_com(void *pass_arg)
{
struct thread_arg *con = pass_arg;
//required statements irrelevant to the issue
pthread_exit(NULL);
}
void *cli_com(void *pass_arg)
{
struct thread_arg *con = pass_arg;
//required statements irrelevant to the issue
pthread_exit(NULL);
}
int main()
{
pthread_t inter_com;
//necessary code
while(1)
{
th_err_s = pthread_create(&inter_com, NULL, serv_com, (void *)&pass_arg);
th_err_c = pthread_create(&inter_com, NULL, cli_com, (void *)&pass_arg);
if (th_err_s || th_err_c)
{
printf("Alert! Error creating thread! Exiting Now!");
exit(-1);
}
}
pthread_exit(NULL);
return 1;
}
Then I compiled the above code in linux using the following command:
gcc -o sample sample.c
It returned the following error message:
inter.c:(.text+0x374): undefined reference to `pthread_create'
inter.c:(.text+0x398): undefined reference to `pthread_create'
collect2: ld returned 1 exit status
What should I do to correctly compile this file. I am sure it is no syntax error or anything since when I commented off everything inside the while loop, the program was compiling correctly and I verified that the pthread_create syntax is correct. Do I have to issue some other command to compile the file?
EDIT: Is there any problem with the creating of two threads in the above code? The program is just exiting with the error message once it is running. What can be the possible issue and how can I solve it? Thanks in advance.
Try doing this :
gcc -lpthread sample.c
or
gcc -pthread sample.c
above 2 commands will directly create executable a.out
Answer after edit:
1) Wait for the two threads to join main thread using call
int pthread_join(pthread_t thread, void **value_ptr);
2) Create both threads with different ids
3) Also avoid calling pthread_exit from main() if you can, although there is no harm doing that
4) you are calling pthread_create in while(1) this will create infinite threads .. I do not know what are you trying to achieve .
Link to pthread Library when compiling...
gcc -o sample -lpthread sample.c
I am not too sure myself but I would think you could do something like
pthread_t inter_com, inter_com2;
and
th_err_s = pthread_create(&inter_com, NULL, serv_com, (void *)&pass_arg);
th_err_c = pthread_create(&inter_com2, NULL, cli_com, (void *)&pass_arg);
I think it should give you 2 ids of threads. But careful when sharing variables etc between threads. But am glad you resolved it yourself.