Suppose we have expression g=(a+b)*(c+d)-(e/f) with hard-coded arbitrary numbers for variables. I would like to calculate this expression using multiple child processes in order to better understand how fork() works.
My first attempt was to calculate (a + b) on child pid1, (c + d) on child pid2, (e / f) on child pid3, and then do summation & subtraction in the parent process.
Well, to my disappointment, (a + b) calculation done in the child process pid1 did not affect double expression1 variable in the parent process. I think the reason behind that - each fork() creates a separate memory space; as soon as a child process exits, all calculations done in that child process are gone.
What do you usually do in a situation like this? I thought maybe I could nest fork() child process within a child process to calculate (a + b) first; wait; then (c + d); wait; (e / f); wait; the first child calculates the entire expression; child return(0); parent terminates.
But I think there's an easier solution to this problem, am I right?
If you insist on using fork() , so here is my answer now using child process and shared memory
Note that exit() is used here the way it is expected by the system: to signalize if the child has exited normally or not.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
struct shared{
int a_b;
int c_d;
int e_f;
};
const int a=1,b=2,c=3,d=4,e=6,f=2;
const key_t key = 1234;
pid_t pab,pcd,pef;
void* shared_mem;
int main(){
//Parent process create the shared memory
int shmid = shmget(key,sizeof(struct shared), 0666|IPC_CREAT);
if(shmid == -1) exit(EXIT_FAILURE);
//Fork child
pab = fork();
if(pab == 0){
//Inside process ab
//attach to shared memory
shared_mem = shmat(shmid,(void*) 0,0);
if(shared_mem == (void*) -1) exit (EXIT_FAILURE);
struct shared* shared_data = (struct shared*) shared_mem;
shared_data->a_b = a +b;
//detach
if(shmdt(shared_mem) == -1) exit (EXIT_FAILURE);
exit(EXIT_SUCCESS);
}else {
pcd = fork();
if(pcd == 0){
//Inside process cd
//attach to shared memory
shared_mem = shmat(shmid,(void*) 0,0);
if(shared_mem == (void*) -1) exit (EXIT_FAILURE);
struct shared* shared_data = (struct shared*) shared_mem;
shared_data->c_d = c+d;
//detach
if(shmdt(shared_mem) == -1) exit (EXIT_FAILURE);
exit(EXIT_SUCCESS);
}else{
pef = fork();
if(pef == 0){
//Inside process ef
//attach to shared memory
shared_mem = shmat(shmid,(void*) 0,0);
if(shared_mem == (void*) -1) exit (EXIT_FAILURE);
struct shared* shared_data = (struct shared*) shared_mem;
shared_data->e_f = e/f;
//detach
if(shmdt(shared_mem) == -1) exit (EXIT_FAILURE);
exit(EXIT_SUCCESS);
}
}
}
//Wait child process termination
int status_ab,status_cd,status_ef;
waitpid(pab,&status_ab,0);
waitpid(pcd,&status_cd,0);
waitpid(pef,&status_ef,0);
//Check if all child exited normally
if(!WIFEXITED(status_ab) || !WIFEXITED(status_cd)||!WIFEXITED(status_ef)){
exit(EXIT_FAILURE);
}
//Parent attaches to memory
shared_mem = shmat(shmid,(void*) 0,0);
if(shared_mem == (void*) -1) exit (EXIT_FAILURE);
struct shared* shared_data = (struct shared*) shared_mem;
//Calculate result
int result = (shared_data->a_b)*(shared_data->c_d)-(shared_data->e_f);
printf("Result is %d\n", result);
//Parent detaches from shared memory and deletes
if(shmdt(shared_mem) == -1) exit (EXIT_FAILURE);
if(shmctl(shmid,IPC_RMID,0) == -1) exit(EXIT_FAILURE);
return EXIT_SUCCESS;
}
fork()ing the processes, then waitpid()ing on their return values:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
//whatever values you like:
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 15;
int f = 6;
int a_plus_b_pid;
int c_plus_d_pid;
int e_div_f_pid;
int a_plus_b;
int c_plus_d;
int e_div_f;
a_plus_b_pid = fork();
if(a_plus_b_pid)
{
c_plus_d_pid = fork();
if(c_plus_d_pid)
{
e_div_f_pid = fork();
if (e_div_f_pid)
{
//wait for our processes to exit, with our results, and stash the computed values.
waitpid(a_plus_b_pid, &a_plus_b, 0);
waitpid(c_plus_d_pid, &c_plus_d, 0);
waitpid(e_div_f_pid, &e_div_f, 0);
//The 8 least-significant bits carry info that we're not interested in here, so shift them out:
a_plus_b >>= 8;
c_plus_d >>= 8;
e_div_f >>= 8;
printf("%d %d %d %d\n", a_plus_b, c_plus_d, e_div_f, a_plus_b * c_plus_d - e_div_f);
}
else
{
exit (e/f);
}
}
else
{
exit (c+d);
}
}
else
{
exit (a+b);
}
}
This is a version using pthreads:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
volatile int a_b;
volatile int c_d;
volatile int e_f;
const int a=1,b=2,c=3,d=4,e=6,f=2;
void* calc_ab(void*);
void* calc_cd(void*);
void* calc_ef(void*);
int main(){
pthread_t ab_thread, cd_thread, ef_thread;
pthread_create(&ab_thread,NULL,calc_ab,NULL);
pthread_create(&cd_thread,NULL,calc_cd,NULL);
pthread_create(&ef_thread,NULL,calc_ef,NULL);
pthread_join(ab_thread, NULL);
pthread_join(cd_thread, NULL);
pthread_join(ef_thread,NULL);
int result = a_b*c_d-e_f;
printf("Result is %d\n", result);
return EXIT_SUCCESS;
}
void* calc_ab(void* arg){ a_b = a+b;pthread_exit(NULL);}
void* calc_cd(void* arg){ c_d = c+d;pthread_exit(NULL);}
void* calc_ef(void* arg){ e_f = e/f;pthread_exit(NULL);}
To compile you have to link against pthread:
gcc pthread.c -lpthread -o teste
Notes
Note that variables that are shared between the main thread and a child thread are declared volatile. This prevent the compiler of doing some memory optimizations that could prevent a write done in one thread not to be seen by others.
Each child thread writes to a different shared variable. I wanted to keep the code simple, not having to handle synchronization explicitly.
The main thread only reads the shared variable only after it has returned from a pthread_join for the thread that changed it. Again I wanted to keep the code simple, not having to handle synchronization explicitly.
First, you don't need processes at all to do arbitrary computation. Emabedding an interpreter like e.g. lua might be simpler.
Of course, each process has its own address space. Type cat /proc/self/maps to get information about the process running that cat command.
If you insist on using processes to learn how they can communicate thru pipes, you might use something like popen(3) which will use some syscalls to start and pipe a command.
char cmd[80];
int a, b, sum;
/// fill a & b
snprintf (cmd, sizeof(cmd), "echo $[%d + %d]", a, b);
FILE* pcmd = popen(cmd, "r");
if (fscanf (pcmd, "%d", &sum)>0) {
// do something with sum
};
pclose(pcmd);
And you should read a good book like Advanced Unix Programming and Advanced Linux Programming. The real thing is to understand syscalls like fork(2), waitpid(2), execve(2), pipe(2), dup(2), etc.... To understand what syscalls(2) are done by some command or program, use strace
Related
This is my code system call in C.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int n;
int i;
pid_t pid;
int time = 1000;
int sum = 0;
int main(void) {
printf("n: ");
scanf("%d", &n);
pid = fork();
if (pid < 0) {
printf("Fork Failed");
exit(-1);
} else if (pid == 0) {
//child
for (i = 1; i <= n; i++) {
sum += i;
}
printf("Sum of 1 to %d: %d\n", n, sum); // this is ok
} else {
// parent
wait(&time);
printf("Sum of 1 to %d: %d\n", n, sum); // this always return 0;
}
return 0;
}
I don't know why in parent's code block, the sum is always equal to 0.
How to make parent wait for child or am I doing something wrong ?
Waiting for the child works. However, your expectations are wrong.
Apparently you think that computations in the child process after the fork are visible in the parent process. They are not. The child is a new copy of the parent program at the time of fork. At that time, the parent's sum is 0 and stays that way.
There are several mechanisms to pass data from child to parent (the search term is interprocess communication, IPC).
exit() status
files
shared memory
pipes
signals
message queues
anything else I have missed
The issue here is the variable sum is not shared by the parent & child process, after fork() call the child will have its own copy of the variable sum.
Use shmget(),shmat() from POSIX api. Or use pthread which will share the same memory space for the newly created thread.
Update---
Added the shared memory to your code hopes this helps.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/shm.h>
int n;
int i;
pid_t pid;
int time = 1000;
int main(void) {
int shmid;
int *sum;
printf("n: ");
scanf("%d", &n);
/*request the shared memory from the OS using the shmget()*/
shmid = shmget(IPC_PRIVATE, sizeof(int), 0777|IPC_CREAT);
pid = fork();
if (pid < 0) {
printf("Fork Failed");
exit(-1);
} else if (pid == 0) {
//child
/* shmat() returns a char pointer which is typecast here
to int and the address is stored in the int pointer. */
sum = (int *) shmat(shmid, 0, 0);
for (i = 1; i <= n; i++) {
*sum += i;
}
printf("Sum of 1 to %d: %d\n", n, *sum); // this is ok
/* each process should "detach" itself from the
shared memory after it is used */
shmdt(sum);
} else {
// parent
wait(&time);
sum = (int *) shmat(shmid, 0, 0);
printf("Sum of 1 to %d: %d\n", n, *sum); // this always return 0;
shmdt(sum);
/*delete the cretaed shared memory*/
shmctl(shmid, IPC_RMID, 0);
}
return 0;
}
Refer for more info- https://man7.org/linux/man-pages/man2/shmget.2.html
As part of my assignment, I have to implement child-parent processes.
"The parent process shall NOT wait for the child to exit but instead shall prints an element as soon as it arrives into the shared buffer ".
I know the case when the parent has to wait for child to end, but how to implement communication of message from child to parent in async way?
P.S. The exact Q is
Write a program whose main routine obtains two parameters n and d from the user, i.e. passed to your program when it was invoked from the shell. Your program shall then create a shared memory and a child process.
The child process should obtain the values of n and d (you have multiple choices on how to do that) and create an arithmetic sequence of length n, and whose first element is 0 and each subsequent element has the value of kd, where k is the element number (k= 0 to n-1).
The child process shall create the elements, one at a time and wait for a random interval of time (0 to 9.999 seconds) between generating elements of the sequence. As soon as an element is generated, the child places the element in the shared buffer by organizing it as described in slides 33-37 of lecture 4.
(e.g.: if n=5 and d=2, the sequence shall be 0,2,4,6,8)
The parent process shall NOT wait for the child to exit but instead shall prints an element as soon as it arrives into the shared buffer (again, in a manner similar to slides 33-37 of lecture 4)
Hint; use fflush() to ensure printf’s are printed immediately into the screen.
My code so far - gist
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
void printLab4(int n, int a, int *fibo)
{
int i;
for (i = 0; i < n; i++)
printf("%d -> %d\n", i, fibo[i]);
}
void computeLab4(int n, int a, int *fibo)
{
int i;
for (i = 0; i < n; i++)
{
int sleepSec = rand() % 10;
printf("sleeping for %d : ", sleepSec);
sleep(sleepSec);
fibo[i] = i * a;
// randomly sleeping for 0-10 secs
printf("Generated new element %d after %d seconds \n", fibo[i], sleepSec);
}
}
int main(int argc, char *argv[])
{
pid_t childPID;
int status;
int shm_fd;
int *shared_memory;
int msize; // the size (in bytes) of the shared memory segment
const char *name = "Lab_4";
int n, a;
if (argc != 3)
{
fprintf(stderr, "usage: %s <Lab4 Seq to be generated>\n", argv[0]);
return -1;
}
n = atoi(argv[1]);
a = atoi(argv[2]);
printf("%d \n", n);
printf("%d \n", a);
if (n < 0 || a < 0)
{
fprintf(stderr, "Illegal number: %s\n", argv[1]);
return -2;
}
// calculating the array size based on the number of terms being passed from child to parent
msize = (n + 2) * sizeof(int);
// open the memory
shm_fd = shm_open(name, O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG);
if (shm_fd < 0)
{
fprintf(stderr, "Error in shm_open()");
return -3;
}
printf("Created shared memory object %s\n", name);
// attach the shared memory segment
ftruncate(shm_fd, msize);
printf("shmat returned\n");
// allocating the shared memory
shared_memory = (int *)mmap(NULL, msize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (shared_memory == NULL)
{
fprintf(stderr, "Error in mmap()");
return -3;
}
printf("Shared memory segment allocated correctly (%d bytes).\n", msize);
shared_memory[0] = n;
shared_memory[1] = a;
childPID = fork();
if (childPID == -1)
{
fprintf(stderr, "Cannot proceed. fork() error");
return -4;
}
if (childPID == 0)
{
// then we're the child process
computeLab4(shared_memory[0], shared_memory[1], shared_memory + 1);
exit(0);
}
else
{
// parent will wait until the child finished
wait(&status);
// print the final results in the
printLab4(shared_memory[0], shared_memory[1], shared_memory + 1);
// now detach the shared memory segment
shm_unlink(name);
}
return 0;
}
Since you are acquiring the values from the command line at the time the program is invoked by the shell, when the parent calls fork(), the child will have access to the same variables. There is no need to "pass" the values from parent to child.
When the child creates the elements, I assume the parent is reading the elements from the shared memory. The "as soon as it arrives" is a somewhat dubious requirement unless you are on a parallel and real-time system. However, simulating that requirement can be achieved with a busy wait loop of the parent checking to see if the shared memory has acquired new data.
In order to not starve the operating system of CPU cycles while the parent is in the busy wait loop, the parent can call sched_yield() between check iterations, which behaves like a sleep that instantly wakes up.
The shared memory between parent and child can be treated as some kind of queue, and the busy wait of the parent is checking if the queue is non-empty, and if so, processes queue elements until it is empty, at which point it busy waits again.
for (;;) {
if (q_is_empty(q)) {
sched_yield();
continue;
}
int v = q_dequeue(q);
process(v);
}
Instead of sched_yield(), you could use usleep() or nanosleep() with a value of 1.
The child is of course adding elements to the queue, following the stipulations of the assignment.
for (;;) {
if (q_is_full(q)) {
sched_yield();
continue;
}
v = next_value_in_sequence_after_delay();
q_enqueue(v);
}
You may want to add an indication (such as enqueueing -1) that the child is done. The child can then exit, and the parent can know it is safe to reap the child.
The full check and empty check can logically be viewed as part of the enqueue and dequeue operations themselves. So, a possible implementation may be:
void q_enqueue(struct queue_type *q, int v) {
while (q_is_full(q)) sched_yield();
q->q[q->tail % Q_MAX_ELEM] = v;
q->tail += 1;
}
int q_dequeue(struct queue_type *q) {
while (q_is_empty(q)) sched_yield();
int v = q->q[q->head % Q_MAX_ELEM];
q->head += 1;
return v;
}
And then the parent and child functions might look like:
void parent(struct queue_type *q) {
for (;;) {
int v = q_dequeue(q);
if (v == -1) break;
printf("|%d", v);
fflush(stdout);
}
printf("|done!\n");
}
void child(struct queue_type *q, int n, int d) {
int v = 0;
for (;;) {
if (v == -1) break;
useconds_t t = rand() % 10000;
usleep(t * 1000);
v = next_value(n, d, v);
q_enqueue(q, v);
}
}
Below is the rest of the queue implementation I tested with, but you may want to research lock-free queues to see how you might implement single consumer / single producer queues without the need for traditional critical section protection.
#define Q_MAX_ELEM 1
struct queue_type {
volatile uint32_t head;
volatile uint32_t tail;
volatile int q[Q_MAX_ELEM];
};
void q_init(struct queue_type *q) {
static const struct queue_type q_zero;
*q = q_zero;
}
bool q_is_empty(struct queue_type *q) {
uint32_t tail = q->tail;
return q->head == tail;
}
bool q_is_full(struct queue_type *q) {
uint32_t head = q->head;
return (q->tail - head) == Q_MAX_ELEM;
}
Try it online!
The typical way to do this would be to create a pair of pipes shared between the child and parent, with the POSIX pipe() function. The Linux manual page even contains an example: https://www.man7.org/linux/man-pages/man2/pipe.2.html
I have an assignment and I am not quite sure how to go about it. Basically I have to create a coordinator process which creates 5 working processes which are waiting to be awakened. The coordinator passes a marker(integer) to the first process, then that process increments the marker by 1 and passes it to the next process. The coordinator process awakens the next process which does the same and so on. The so called marker should go through all the processes 10 times and in the end its value should be printed by the coordinator. Signals should be used as well as shared memory for the marker.
So I created 5 processes and I am thinking that on every iteration there should be a signal and a handler should be passed which will basically do all the work with the marker.
This is my first time working with processes. This is what I have so far:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <signal.h>
#define numOfProcesses 5
pid_t procIDs[5];
void handler(int signum){
//marker and all work here
}
void createProcesses(){
int i;
for(i = 0; i < numOfProcesses; i++){
procIDs[i] = fork();
if(procIDs[i] < 0 ){
perror("Fork error!");
}else if(procIDs == 0){
pause();
}
}
}
int main(){
createProcesses();
int i;
for(i = 0; i < numOfProcesses; i++){
pkill(SIGUSR1, handler);
}
return 0;
}
I honestly don't know how to go about this. I would really appreciate a piece of advice. Thanks in advance!
This is what I have come up with. Sorry for answering, I couldn't find out how to format code in a comment. Anyway:
It should be 10 times each process. I am using shared memory so I guess I don't need a global variable for the marker? This is what I have come up with:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/shm.h>
#include<signal.h>
#include<sys/ipc.h>
#define numOfProcesses 5
#define numOfLoops 10
pid_t* procIDs[5];
void createProcesses(){
int i;
for(i = 0; i < numOfProcesses; i++){
procIDs[i] = fork();
if(procIDs[i] < 0 ){
perror("Fork error!");
}
else if(procIDs == 0){
pause();
}
}
}
void init(){//init marker = 0
key_t mykey = ftok(".", 0);
int shID = shmget(mykey, sizeof(int), 0666 | IPC_CREAT);
int *data;
data = (int*) shmat(shID, 0, 0);
*data = 0;
}
int* getValue(){//get value of marker
key_t mykey = ftok(".", 0);
int shID = shmget(mykey, sizeof(int), 0666 | IPC_CREAT);
int *data = shmat(shID, 0, 0);
return data;
}
void increment(int sig){//increment + 1
if(sig == SIGUSR1){
int temp;
int* data;
data = getValue();
temp = *data;
temp++;
*data = temp;
}
}
void yourFunc(int count, pid_t* mypid, int mysig){
if(count == 0){
return;
}else{
printf("Signal sent :: to PID : %d\n", mypid);
kill(*mypid, SIGUSR1);
yourFunc(count -1, ++mypid, SIGUSR1);
}
}
int main(){
signal(SIGUSR1, increment);
init();
int i,j;
createProcesses();
for(j = 0; j < numOfLoops; j++){//loop every pid 10 times
pid_t* currProcess = procIDs[0];
yourFunc(numOfProcesses, currProcess, SIGUSR1);
}
int* data = getValue();
printf("Marker: %d\n", *data);
return 0;
}
I tried your problem, but I am really baffled by the structure of your question, its really unclear what your problem statement is.
10 times each(10 times per process or a total of 10 times(2 times per process)
You say the processes are waiting to be awakened, which hints that they are not child processes rather other processes running on the system, and would require a fifo to communicate.
Nevertheless, the following is what I could conclude from the limited information.
You need to create a function which would be invoked 10 times(loop) by the coordinator on the first process(waiting to be awakened)
The function would recursively invoke the second process and so on till the last sleeping process.
You'll have to use SIGUSR1, and define action for it in a custom signal handler,
eg.
signal(SIGUSR1,custom_handler)
You will need to keep marker as a global variable.
Because C is a procedural language and kernel's scheduling is not in your hands once a process terminates you cannot recall it or ensure same PID for a process on forking.
So if you are thinking of creating processes inside functions which will be paused and on getting a signal shall resume, fine.....!, But it would be a one-off.
That's all I can say by the limited information your question presents.
Following is the above idea in C.
Initialise count = 5 (no. of processes)in the caller.
mypid points to the first process's PID.
void party_time(int count, pid_t* mypid, int mysig)
{
if(count == 0)
return;
else
{
printf("Signal sent :: to PID : %d\n",*mypid);
kill(*mypid,SIGUSR1);
party_time(count - 1 ,++mypid,SIGUSR1);
}
}
I have my first Hw assignment from the book. Can anyone help out in designing my code. I don't know where to start. i'm thinking of using an array with all zeros as the first step but i really don't know what to do. I don't understand how creating a parent and when i do that it should initialize a shared memory segment is this where my array should come in? The book is pretty good but really lacking in explaining exactly what i need to do in my program or and doesnt provide any sample output. Thanks for any help
An operating system’s pid manager is responsible for managing process
identifiers. When a process is first created, it is assigned a unique
pid by the pid manager. The pid is returned to the pid manager when
the process completes execution, and the manager may later reassign
this pid. Process identifiers are discussed more fully in Section
3.3.1. What is most important here is to recognize that process identifiers must be unique; no two active processes can have the same
pid. Use the following constants to identify the range of possible pid
values:
#define MIN PID 300 #define MAX PID 5000 You may use any data structure of your choice to represent the availability of process
identifiers. One strategy is to adopt what Linux has done and use a
bitmap in which a value of 0 at position i indicates that a process id
of value i is available and a value of 1 indicates that the process id
is currently in use.
int allocate map(void)—Create sand initializes a data structure for
representing pids; returns—1 if unsuccessful, 1 if successful
int allocate pid(void) — Allocates and returns a pid; returns — 1
if unable to allocate a pid (all pids are in use)
void release pid(int pid)—Releases a pid
Perhaps this is late. But I too came across the same question, worked out the following and wanted to cross-check it. I couldn't find any complete solution but anyway, here is what I did:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define MIN_PID 500
#define MAX_PID 3000
#define cb CHAR_BIT
int sz = MAX_PID - MIN_PID + 1;
unsigned char *b;
int allocate_map();
int allocate_pid();
void release_pid(int pid);
int main()
{
int map = allocate_map();
if (map == 1) {
printf("\nData Structure initialized.\n");
int id = 0, i = 0;
while (i < 15) {
int val = allocate_pid();
printf("\nProcess %d: pid = %d", i+1, val);
i++;
}
release_pid(503); printf("\nProcess 503 released.");
release_pid(505); printf("\nProcess 505 released.");
int val = allocate_pid(); printf("\nProcess %d : pid = %d\n", i+1, val);
}
else printf("\nFailed to initialize data structure.\n");
}
/* Creates and initializes a data structure for representing pids;
returns —1 if unsuccessful, 1 if successful */
int allocate_map() {
b = (unsigned char*)malloc((sz+cb-1)/cb * sizeof(char));
if (b) return 1;
return -1;
}
/* Allocates and returns a pid; returns -1
if unable to allocate a pid (all pids are in use) */
int allocate_pid() {
int i = 0;
int pid = b[i/cb] & (1 << (i & (cb-1)));
while (pid != 0) {
i++;
pid = b[i/cb] & (1 << (i & (cb-1)));
}
if (i+MIN_PID > MAX_PID) return -1;
b[i/cb] |= 1 << (i & (cb-1));
return i+MIN_PID;
}
/* Releases a pid */
void release_pid(int pid) {
if (pid < 500) {
printf("\nInvalid PID: It should lie between 500 and 3000.");
return;
}
int i = pid - MIN_PID;
b[i/cb] &= ~(1 << (i & (cb-1)));
}
I also encounter this question while reading the Operating system concepts, 10th edition book. I didn't find any reference to cross-check my solution.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#define MIN_PID 300
#define MAX_PID 5000
int get_random();
int allocate_map(void);
int allocate_pid();
void release_pid();
bool* pid_map;
int main() {
// initiate pid map
if(allocate_map() == -1){
printf("unable to create the pid map\n");
}
// sample pid for feature release
int pid1, pid2;
// allocate pids
for(int i = 0; i < 1000; i ++){
int pid = allocate_pid();
if(i == 3) pid1 = pid;
if(i == 4) pid2 = pid;
printf("PID: %d\n", pid);
}
// release pids
release_pid(pid1);
release_pid(1000);
release_pid(pid2);
}
int allocate_map(void){
srand(time(0));
pid_map = malloc(sizeof(bool) * MAX_PID); // yah, allocated extra 300 pid
return pid_map == NULL ? -1 : 1;
}
int allocate_pid(){
int pid = get_random();
while(pid_map[pid] == true){
pid = get_random();
}
pid_map[pid] = true;
return pid;
}
void release_pid(int pid){
if(pid_map[pid] == true){
pid_map[pid] = false;
printf("Release pid %d\n", pid);
} else {
printf("PID %d is not associated with any process\n", pid);
}
}
//to get a random number between max and min pid
int get_random(){
return (rand() % (MAX_PID - MIN_PID + 1) + MIN_PID);
}
Create a parent which initializes a shared memory segment, and then creates a number of children. Each child scans the memory segment looking for a free slot (meaning a memory location offset from the base address), and returns the index of that slot as its simulated “pid”, replacing the 0 value with its child ID or some other indication that the slot is taken. This is a start
What I'm trying to do is have a child process run in the background while the parent goes and does something else. When the child returns, I'd like for the parent to get that status and do something with it. However, I don't want to explicitly wait for the child at any point.
I looked into the WNOHANG option of waitpid but this seems to be a little different than what I'm looking for. WNOHANG just only gets the status if it's done, otherwise it moves on. Ideally, I'd like some option that will not wait for the process, but will jump back and grab the return status when it's done.
Is there any way to do this?
Code example:
pid_t p = fork();
if (p == 0){
value = do_child_stuff();
return(value);
}
if (p > 0){
captureStatus(p, &status); //NOT A REAL FUNCTION
// captureStatus will put p's exit status in status
// whenever p returns, without waiting or pausing for p
//do other stuff.....
}
Is there any way to simulate the behavior of captureStatus?
You could establish a signal handler for SIGCHLD and wait for the process once that triggers (it will trigger when the child terminates or is killed).
However, be aware that very few useful things can be done in a signal handler. Everything must be async-signal-safe. The standard specifically mentions wait and waitpid as safe.
Here's the proper way (or at least one proper way) to create an asynchronous wait out of a synchronous one:
struct waitpid_async_args {
pid_t pid;
int *status;
int flags;
sem_t sem, *done;
int *err;
};
static void *waitpid_async_start(void *arg)
{
struct waitpid_async_args *a = arg;
pid_t pid = a->pid;
int *status = a->status, flags = a->flags, *err = a->err;
sem_post(&a->sem);
if (waitpid(pid, status, flags) < 0) *err = errno;
else *err = 0;
sem_post(a->done);
pthread_detach(pthread_self());
return 0;
}
int waitpid_async(pid_t pid, int *status, int flags, sem_t *done, int *err)
{
struct waitpid_async_args a = { .pid = pid, .status = status,
.flags = flags, .done = done, .err = err };
sigset_t set;
pthread_t th;
int ret;
sem_init(&a.sem, 0, 0);
sigfillset(&set);
pthread_sigmask(SIG_BLOCK, &set, &set);
ret = pthread_create(&th, 0, waidpid_async_start, &a);
if (!ret) sem_wait(&a.sem);
pthread_sigmask(SIG_SETMASK, &set, 0);
return ret;
}
Note that the asynchronous function takes as an extra argument a semaphore it will post to flag that it's done. You could just examine status, but without a synchronization object there's no formal guarantee of memory ordering, so it's better to use an approach like this and call sem_trywait or sem_timedwait with a timeout to check whether the status is available yet before accessing it.
You can used shared memory IPC functions like shmget and shmat to communicate between the two processes. This can be non-blocking and may work well for producer/consumer models like the one you are describing. You still will have to poll, though.
shmget should be called before the fork to create the shared memory block.
Then you can use shmat after the fork to get a pointer to the shared memory and just use it as a buffer from there on.
When finished call shmdt on both child and parent to detach, and shmctl to remove the shared memory.
There is an example on the web here.
I like Vyktor's solution - in the parent process, start a thread to block on the child process and set the status variable when it's done. Here is an implementation:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
struct return_monitor {
pid_t pid;
int *return_status;
};
static void
captureStatus(struct return_monitor *monitor)
{
printf("Monitor thread started...\n");
int return_status;
waitpid(monitor->pid, &return_status, 0);
*(monitor->return_status) = WEXITSTATUS(return_status);
}
int
main()
{
printf("Parent process started...\n");
pid_t p = fork();
if (p == 0){
/* child */
printf("Child process started...\n");
int i;
for (i = 0; i < 10; ++i) {
sleep(1);
printf("Child iteration %d...\n", i);
}
/* arbitrary return value that will be recognizable in parent */
return(3);
}
if (p > 0){
int child_return_status = -1;
struct return_monitor monitor = {p, &child_return_status};
pthread_t monitor_thread;
pthread_create(&monitor_thread, NULL, captureStatus, &monitor);
int i;
for (i = 0; i < 10; ++i) {
sleep(2);
printf("Parent process iteration %d (Child return status %d)...\n",
i, child_return_status);
}
/* captureStatus(p, &status); */
}
}