Can one changing value by pointer from the parent process? - c

I try to change a _Bool flag used as while loop parameter inside of a child process. The child needs to exit the while loop to aboard properly.
I want a pointer in a other structure to control the execution of the child. Simple reproducible example of this idea may boil to (beware, this code will spawn a zombie process):
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
typedef struct {
void (*loop)();
_Bool done;
} __state;
__state state;
void state_loop() {
pid_t pid;
switch(pid = fork()) {
case -1:
perror("Failed to fork!\n");
exit(1);
case 0:
printf("Connecting..\n");
printf("Child's adress: %p\n", &state.done);
while(!state.done) {
;;
}
printf("Conected.\n");
default:
printf("Spawned a child..\n");
signal(SIGCHLD, SIG_IGN);
}
}
__state state = {state_loop, 0};
typedef struct {
void (*set_state)(_Bool *flag_p);
void (*flip)();
// Data
_Bool *flag_p;
} __controller;
__controller controller;
void controller_set_state(_Bool *flag_p) {
controller.flag_p = flag_p;
}
void controller_flip() {
*controller.flag_p = (_Bool)1 - *controller.flag_p;
}
__controller controller = {
controller_set_state, controller_flip
};
int main(void) {
printf("%i\n", state.done);
controller.set_state(&state.done);
state.loop();
controller.flip();
printf("%i\n", state.done);
printf("%p:%p\n", &state.done, controller.flag_p);
return 0;
}
Example above does print the addresses of server.done and controller.flag_p from the parent process and server.done address from the child process.
How is that addresses are the same in all of the places, but the value in the child process reminds 0 after being changed by controller.flip?

fork creates a separate process which gets its own virtual address space. With a few exceptions (see the documentation for fork), the child process is an exact duplicate of its parent. That means it gets its own separate copy of state at the same virtual address.
If you don't specifically need a separate process, you could spin off a thread instead. Threads share the same memory space and so would have access to the same state. However, you would need to protect the read/write of state's fields via something like a pthread_mutex_t.

Related

What is the idea behind the way the signals are used here?

While reading up and learning about signals, I found a program, that uses signals in a specific way. I tried understand it, but I am not sure, how all the parts of the code interact with another.
Below is the above mentioned code and I added comments, where I have difficulties:
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define CP 5
static volatile int curprocs =0; ;
static void die() {
exit(EXIT_FAILURE);
}
static void chldhandler() {
int e = errno;
// Why do we use waitpid here? What does it do?
while(waitpid(-1, NULL, WNOHANG) > 0) {
curprocs--;
}
errno = e;
}
void do_work() {
time_t t;
srand((unsigned) time(&t));
sleep(5+ rand() % 4);
}
int main() {
struct sigaction sa = {
.sa_handler = chldhandler,
.sa_flags = SA_RESTART,
};
sigemptyset(&sa.sa_mask);
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
exit(-1);
}
while(1) {
sigset_t chld, empty;
sigemptyset(&empty);
sigemptyset(&chld);
sigaddset(&chld, SIGCHLD);
// What do the following lines of code do??
sigprocmask(SIG_BLOCK, &chld, NULL);
while (curprocs >= CP) { // cap for the number of child processes
sigsuspend(&empty);
}
curprocs++;
sigprocmask(SIG_UNBLOCK, &chld, NULL);
pid_t p = fork();
if (p == -1) {
return -1;
}
if (p == 0) {
// code for the child processes to execute
do_work();
die();
} else {
// Parent process does nothing
}
}
return 0;
}
Obviously above program is intended to have a max amount of 42 child processes doing work. Whenever we want to have a new child process, we use fork, and adjust curprocs.
Whenever a child process finishes, chldhandler() is called and curprocs is adjusted as well.
However I don't understand the interplay of the two sigproc_mask, sigsuspend, waitpid and our two signalsets chld, empty.
Can someone explain what these lines do or why they are used the way they are?
sigprocmask(SIG_BLOCK, &chld, NULL); blocks SIGCHLD so that you can be sure that while you do while (curprocs >= 42) the SIGCHLD handler won't interrupt the code, changing curprocs in the middle of the check.
sigsuspends atomically unblocks it and waits for a SIGCHLD (any signal really, since your passing an empty mask), atomically reblocking it when it returns.
The waitpid(-1,/*...*/) in the handler reaps the status of any (that's what the -1 means) child that has a status change (typically termination notification) pending so that the data the kernel associates with the status change can be freed. The second argument would be where the status change info would go but since you passed NULL, the info will simply be dropped. WNOHANG means don't wait if there aren't any more status change notifications.
Since the handler is run in response to SIGCHLD, there should be at least one status change notification, but there could be more because SIGCHLDs can coalesce (it's also possible there isn't any — if you called waitpid from normal context while SIGCHLD was blocked). That's why the handler loops. The WNOHANG is important because without it, after all the status change notifications have been reaped, the waitpid call would block your process until a new notification arrived.

Synchronization between childs and parent processes c

im trying to implement this:
Make a C multi-process program that does the following:
A process P generates two child processes P1 and P2. The two sons P1 and P2 perform an indeterminate cycle in which generate, each second, a random integer between 0 and 100. With each draw, the children communicate the numbers generated by the parent P process which provides for adding them, printing them on the screen and storing them in one file. Process P1 must handle the SIGINT interrupt signal. In particular, at the arrival of this signal P1 must display the warning message "P1 process busy!". The program is terminated by the parent P process when it verifies that the sum of the numbers, which it has received from the child processes, assumes the value 100.
Now, I need some help with the synchronization between childs and parent. Im trying to use semaphores but it looks like impossible. what can i use to synchronize them? signals? how?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <semaphore.h>
#include <fcntl.h>
#define READ 0
#define WRITE 1
void handler(int sig){
printf("process 1 is busy\n");
}
void codeprocess1(int pd[], sem_t *sem1){
int i = 0;
int numgenerated;
while( i = 0){
signal(SIGUSR1, handler);
numgenerated = rand()%101;
close(pd[READ]);
write(pd[WRITE], &numgenerated, sizeof(int));
sleep(1);
sem_wait(sem1);
}
}
void codeprocess2(int pd[], sem_t *sem2){
int i = 0;
int numgenerated;
while( i = 0){
numgenerated = rand()%101;
close(pd[READ]);
write(pd[WRITE], &numgenerated, sizeof(int));
sleep(1);
sem_wait(sem2);
}
}
int main(){
pid_t pid1, pid2;
int sum, numread1, numread2, pipe1[2], pipe2[2];
sem_t *sem2 = sem_open("semaph2", O_CREAT | O_EXCL, 1, 0);
sem_t *sem1 = sem_open("semaph1", O_CREAT | O_EXCL, 1, 0);
if(pipe(pipe1)<0){
exit(1);
}
if(pipe(pipe2)<0){
exit(1);
}
pid1 = fork();
switch(pid1){
case -1:
exit(1);
case 0:
codeprocess1(pipe1, sem1);
break;
default:
pid2= fork();
switch( pid2){
case -1:
exit(1);
case 0:
codeprocess2(pipe2, sem2);
break;
default:
while(sum!=1000){
close(pipe1[WRITE]);
read(pipe1[READ], &numread1, sizeof(int));
close(pipe2[WRITE]);
read(pipe2[READ], &numread2, sizeof(int));
sum = sum + numread1 + numread2;
printf("%d\n", sum);
sem_post(sem1);
sem_post(sem2);
}
kill(0, SIGKILL);
}
}
}
I'm reporting here the relevant part of the man page of sem_overview(7):
POSIX semaphores come in two forms: named semaphores and unnamed sema‐
phores.
Named semaphores
A named semaphore is identified by a name of the form /somename;
that is, a null-terminated string of up to NAME_MAX-4 (i.e.,
251) characters consisting of an initial slash, followed by one
or more characters, none of which are slashes. Two processes
can operate on the same named semaphore by passing the same name
to sem_open(3).
The sem_open(3) function creates a new named semaphore or opens
an existing named semaphore. After the semaphore has been
opened, it can be operated on using sem_post(3) and sem_wait(3).
When a process has finished using the semaphore, it can use
sem_close(3) to close the semaphore. When all processes have
finished using the semaphore, it can be removed from the system
using sem_unlink(3).
Unnamed semaphores (memory-based semaphores)
An unnamed semaphore does not have a name. Instead the sema‐
phore is placed in a region of memory that is shared between
multiple threads (a thread-shared semaphore) or processes (a
process-shared semaphore). A thread-shared semaphore is placed
in an area of memory shared between the threads of a process,
for example, a global variable. A process-shared semaphore must
be placed in a shared memory region (e.g., a System V shared
memory segment created using shmget(2), or a POSIX shared memory
object built created using shm_open(3)).
Before being used, an unnamed semaphore must be initialized
using sem_init(3). It can then be operated on using sem_post(3)
and sem_wait(3). When the semaphore is no longer required, and
before the memory in which it is located is deallocated, the
semaphore should be destroyed using sem_destroy(3).
You are trying to use unnamed semaphores in standard memory. But they are meant to synchronize threads only, not processes.
I suggest to use either named semaphores (that should be easier) or unnamed semaphores backed by shared memory (get it with shmget() or shm_open(), then use it with sem_init() - the parent and the forked processes must use the same shared memory segment to have access to the inter-process semaphore).
In fact, in your code sem1 and sem2, initialized in the main process, won't be propagated to the forked processes: they have independent memory regions and addresses, and cannot be shared.
After the edit, regarding the semaphores there are many problems:
the most logically wrong: you cannot pass the pointer of one process to another process: the addresses are not shared. Every process must independently open the semaphore and use it with his own handler.
while (i=0)... ouch, try compiling with -Wall.
You wasn't checking the return code of sem_open() it was failing with errno=13 (EACCESS)
You wasn't properly setting the permission of the semaphore... it's a (sort of) file. Note that once you crete it with the wrong permissions, it stays there and it won't be possible to create it again with the same name (until you reboot the system). You can see them with: ls -l /dev/shm, and eventually just remove them with rm.
You was requesting O_EXCL, that is, exclusive access to one process, that's not what you want. See man 2 open.
the name of the semaphore must begin with /, see man sem_overview
Here is the revised code, some comments in-line:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <semaphore.h>
#include <fcntl.h>
#include <errno.h>
#define READ 0
#define WRITE 1
#define SEM1_NAME "/semaph_1a"
#define SEM2_NAME "/semaph_2a"
void handler(int sig) {
printf("process 1 is busy\n");
}
void codeprocess1(int pd[]) {
int i = 0;
int numgenerated;
// each process must open the handle to the same named semaphore.
// they cannot share a local memory address.
sem_t *my_sem = sem_open(SEM1_NAME, O_CREAT , 0777, 0);
if (my_sem==SEM_FAILED) {
printf("semaphore creation failed, errno=%d\n", errno);
exit(1);
}
// the seed for the two children must be different or they will be generating the same
// sequence of random numbers.
srand(3333);
while(i == 0) {
signal(SIGUSR1, handler);
numgenerated = rand()%101;
// close(pd[READ]);
write(pd[WRITE], &numgenerated, sizeof(int));
sleep(1);
sem_wait(my_sem);
}
}
void codeprocess2(int pd[]){
int i = 0;
int numgenerated;
sem_t *my_sem = sem_open(SEM2_NAME, O_CREAT, 0777, 0);
if (my_sem==SEM_FAILED) {
printf("semaphore creation failed, errno=%d\n", errno);
exit(1);
}
srand(1111);
while(i == 0) {
numgenerated = rand()%101;
// close(pd[READ]);
write(pd[WRITE], &numgenerated, sizeof(int));
sleep(1);
sem_wait(my_sem);
}
}
int main(){
pid_t pid1, pid2;
int sum, numread1, numread2, pipe1[2], pipe2[2];
// O_EXCL removed
// the mode flag must be set to 0777 for example, not "1".
// return value check added
sem_t *sem1 = sem_open(SEM1_NAME, O_CREAT , 0777, 0);
if (sem1==SEM_FAILED) {
printf("semaphore sem1 creation failed, errno=%d\n", errno);
exit(1);
}
sem_t *sem2 = sem_open(SEM2_NAME, O_CREAT, 0777, 0);
if (sem2==SEM_FAILED) {
printf("semaphore sem2 creation failed, errno=%d\n", errno);
exit(1);
}
if (pipe(pipe1) < 0 ) {
exit(1);
}
if (pipe(pipe2) < 0) {
exit(1);
}
pid1 = fork();
switch(pid1){
case -1:
exit(1);
case 0:
codeprocess1(pipe1);
break;
default:
pid2= fork();
switch( pid2) {
case -1:
exit(1);
case 0:
codeprocess2(pipe2);
break;
default:
// 100, not 1000
while (sum != 100) {
// all the "close()" calls are commented out
// close(pipe1[WRITE]);
read(pipe1[READ], &numread1, sizeof(int));
// close(pipe2[WRITE]);
read(pipe2[READ], &numread2, sizeof(int));
// sum must not be incremented
sum = numread1 + numread2;
printf("%d\n", sum);
sem_post(sem1);
sem_post(sem2);
}
kill(0, SIGKILL);
}
}
}
There is really a lot going on in your question.
As posted in the answer #Sigismondo, you are confusing multithreading with multiprocess programming. They have different method of communications.
To oversimplify threads share the same memory, so a thread can see for example values of global variables such as semaphores mutex and so on: if a thread modifies it, the other thread will be affected.
In multiprocessing when you fork(), a new process is generated with its own memory space. Right after the fork() variable values are almost the same (apart pid, ppid and so on) but they are in a different memory space: if you have a code block executed by only one process, modifying it will not affect the variables (the semaphores in your program) of the other process.
In your case: first of all if the children process do the same stuff (i.e. generate a random number) why do you have to different functions? Can't you do something like:
#include<stdlib.h>
int generateRand()
{
n = rand() % 100 + 1; //should be random in [1, 100]
}
HANDLING SIGNALS
Process P1 must handle the SIGINT interrupt signal. In particular, at
the arrival of this signal P1 must display the warning message "P1
process busy!". The program is terminated by the parent P process when
it verifies that the sum of the numbers, which it has received from
the child processes, assumes the value 100.
This is really unclear, in my opinion. The parent should catch the SIGINT signal. What should the children do? From what you say it seems they shouldn't catch that signal. In this case you must take a look at signal masks: basically you have to block the signal in the parent, the call the fork()s and then put back the original mask. Now you should go deeper but somehting like this (here)
sigset_t *parent_mask, *child_mask
//get the current mask
if (int res = sigprocmask (0, NULL, child_mask)<0)
printf("some error\n");
//make the mask block the signal
if (int res = sigaddset(child_mask, SIGINT)<0)
printf("some error in sigaddset \n");
// block the signal with the new mask
if (int res = sigprocmask (SIG_SETMASK, child_mask, parent_mask)<0)
printf("some error\n");
//do your forks: children will inherit the current mask and will not catch SIGINT
...
fork()
...
fork()
....
//set back the original mask so the parent catches SIGINT
if (int res = sigprocmask (SIG_SETMASK, parent_mask, NULL)<0)
printf("some error\n");
This answer of mine, although for multithreading should be a little clearer.
SIGNAL HANDLER
Why are you registering the signal handler in codeprocess1(int pd[])? I don't get it at all. And why SIGUSR1?
You should do it in the parent (before or after the fork()s shouldn't change since the signal is blocked for children: it depends if you want to have the user exit the program before starting the forks() or not: in the first case register the signal handler after the fork() otherwise put it at the beginning of main(). In both case you should do:
signal(SIGINT, handler);
Now the core to your program: to communicate your program you can use pipe() in a blocking way together with file descriptors: check here.
You need two file descriptors (one per child process and close the end (read/write) not used by the process).
Consider a single child process:
int p = fork();
int fd1[2]; //file descriptor for child1
int fd2[2]; //file descriptor for child2
if (p>0)//parent
{
close(fd1[1]);//close writing end
int n;
read(fd1[0], &n, sizeof(n));
//you might to call the other fork here and redo the same stuff
int p2 = fork();
if (p2>0)
{
close(fd2[1]);//close writing end
int n2;
read(fd2[0], &n2, sizeof(n2));
sum = n2+n1
if (sum==100 && exit = 1)
{
kill(p, SIGKILL);
kill(p2, SIGKILL);
}
}
}
else if(p==0)//child
{
close(fd1[0]);//close read end
int rand_n = generateRand();//or whaterver the name
wrote(fd1[1], &rand_n, sizeof(rand_n));
}
The exit condition is both based on the value of the sum (100) and the fact that CTRL+C has been pressed. The former is obvious in the code above. For the latter you can declare a global variable (I used exit) that if 0 CTRL+C has not been pressed, if 1 it has. This value is checked in the exit condition of the code above. Your handler will be responsible to write this variable:
//global variable here
int exit = 0;
void handler(int signo)
{
print("Parent busy doing stuff\n");
exit =1;
}
Note one thing exit is written by the parent since it is written ONLY in the handler which is called only by the parent and it is read in the part of the code executed only by the parent: the the children read its value it will be always 0 for them.
Being your question too general I tried to give some hints: there might be errors in my code since I haven't tried. You should study your own. If you will provide a minimal working example I will try to help.

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

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);
}

Static variable in child process of fork

I understand that both parent and child in fork() are two separated processes, but I was trying to understand the behavior of a static variable when it's declared and initialized in the child. Consider this code please:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
static int z = 99;
void main(){
int x=99;
int pid;
pid = fork();
switch(pid){
case -1: printf("fork failed.");break;
case 0: printf("I am the Child[PID=%d].\n",getpid());
static int y=99;
x++;y++;z++;
printf("x=%d, y=%d, z=%d\n",x,y,z);break;
default: wait(NULL);
//int y = 99;
printf("Child has finished. I am the parent[PID=%d].\n",getpid());
printf("x=%d, y=%d, z=%d\n",x,y,z);
}
}
Output:
Why in parent printf statement the value of y is 99? although that the parent waited for the child to finish, and in the child the value of y is changed to 100 "y++" after it was set to 99.
This is because y exists in two separate processes, i.e. two separate virtual address spaces. Changing one process won't affect the other process.
Compare this with threads, where threads share the same process, i.e. the same virtual address space, change will be seen by all threads.
Static variables are initialized at load time (compile time), not at run time. In the fork(), the memory image is copied, including these initialized static vaiables. The child performs the increment, the parent not.

posix semaphore trouble

I supposed that my program should work like this:
1) initializing unnamed semaphore with value = 0
the second value for sem_init(..) is 1 so as it said in MAN the semaphore is shared between processes
2) creating child, child waits until semaphore value becomes 1
parent process increases the value of semaphore so the child should exit now
but it doesn't exit really, so that is the problem
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
pid_t child;
void child_proc(sem_t* sem) {
sem_wait(sem);
printf("OK\n");
}
void parent_proc(sem_t* sem) {
sem_post(sem);
sleep(2);
int status;
waitpid(child, &status, 0);
}
int main(int argc, char* argv[]) {
sem_t sem;
sem_init(&sem, 1, 0);
child = fork();
if (0 == child) {
child_proc(&sem);
return 0;
}
parent_proc(&sem);
return 0;
}
The problem is that both processes have a local (not shared) copy of the semaphore structure and changes in one process won't reflect to the other process.
As the man page also says, if you want to share semaphores across processes, not only do you need to pass a non-zero value to the second argument of sem_init, but the sem_t structure also needs to exist in an area of shared memory. In your example program, it exists on the stack, which is not shared.
You can have shared memory by using a common file mapping (with mmap) or with shm_open, notably.

Resources