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
Related
I need help with my c program, I have most of it done but it has some issues.
The program is about ** Exploring Synchronization Among Processes and Threads.**
You are given three (3) processes in one program that work together to solve a producer consumer problem:
2 processes are “producers” and each process produces its own type of product in a continuous loop. That is, one product type produced by one producer and a different product type by the other producer.
One Process is a “consumer” of products and consist of five (5) threads:
1 thread is a ‘distributor’ thread
two (2) threads consume one type of product (ex. consumes only product type 1)
two (2) threads consume a second type of product (ex. consumes only product type 2).
The consumer process contains two (2) product storage buffers, each comprised of a fixed number of slots. The number of slots in the buffers are to be different (one has more slots than
the other). You choose and specify the number of slots in each buffer as a definition in your
program solution
Communication between the producer processes and the consumer process is to be through a
single “pipe”. This single, shared pipe is used to communicate between each producer process
and the consumer process. Each producer process writes into this pipe the product to be
consumed by the consumer process.
Final program delivery: completion of the product consumer threads, output file design and
write functions; sample data runs with output files
I have most of it done but it has some issues.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <stdbool.h>
#include <fcntl.h>
#include <pthread.h>
#define BUFFER_SIZE1 20
#define BUFFER_SIZE2 30
typedef struct
{
int count;
int productType;
} product;
int count = 0;
int fd[2];
pthread_mutex_t lock;
pthread_cond_t cond;
typedef struct
{
product *values;
int head;
int tail;
int numEntries;
int size;
} queue;
queue q1;
queue q2;
void producer(int args);
void *consumer(void *args);
void *distributor(void *args);
void initQ(queue *q, int size);
bool QEmpty(queue *q);
bool QFull(queue *q);
bool put(queue *q, product prod);
product get(queue *q);
// https://www.youtube.com/watch?v=l6zkaJFjUbM
// https://www.geeksforgeeks.org/multithreading-c-2/
int main(int argc, char const *argv[])
{
// Creating 5 threads 4 consumer and 1 distributor
pthread_t th[5];
// Creating our pipe, fd[0] is read end, fd[1] is write end
if (pipe(fd) == -1)
{
perror("error creating pipe");
exit(1);
}
// Initializing both buffers
initQ(&q1, BUFFER_SIZE1);
initQ(&q2, BUFFER_SIZE2);
int pid1;
int pid2;
int consId1 = 1;
int consId2 = 2;
// Initializing lock
pthread_mutex_init(&lock, NULL);
// Initialziing condition variables
pthread_cond_init(&cond, NULL);
// Create first producer process using fork(), child process 1
if (pid1 = fork() == 0)
{
producer(1);
}
// Create second prodcuer process using fork(), child process 2
else if (pid2 = fork() == 0)
{
producer(2);
}
// Create distrubtor and consumer threads, parent process
else
{
// Creating 4 threads using for loop and pthread_create
for (int i = 0; i < 4; i++)
{
// 2 consumer threads for product type 1
if (i == 1 || i == 2)
{
if (pthread_create(&th[i], NULL, &consumer, &consId1) != 0)
{
perror("Error creating thread");
}
}
// 2 consumer threads for product type 2
else
{
if (pthread_create(&th[i], NULL, &consumer, &consId2) != 0)
{
perror("Error creating thread");
}
}
}
// use pthread_join to wait for preivous thread to terminate
for (int i = 0; i < 4; i++)
{
if (pthread_join(th[i], NULL) != 0)
{
perror("Error joining thread");
}
}
// Distributor thread
close(fd[1]);
while (1)
{
product prod;
// Using lock and condition variable around crit section to avoid race condition
// pthread_mutex_lock(&lock);
// pthread_cond_wait(&cond, &lock);
// Read from the pipe
read(fd[0], &prod, sizeof(prod));
if (prod.productType == 1)
{
put(&q1, prod);
}
else
{
put(&q2, prod);
}
}
// pthread_cond_signal(&cond);
// pthread_mutex_unlock(&lock);
// Close read end of the pipe
close(fd[0]);
}
return 0;
}
// Creating the producers
void producer(int args)
{
int prodCount = 0;
product prod;
prod.productType = args;
// Close read end of the pipe
close(fd[0]);
while (1)
{
prodCount++;
prod.count = prodCount;
// Send product to the pipe so the consumer can use
write(fd[1], &prod, sizeof(prod));
// Sleep for 0.01 - 0.2 seconds after each loop
int time = (rand() % (200000 - 10000 + 1)) + 10000;
usleep(time);
}
// Close write end of the pipe
close(fd[1]);
}
void *consumer(void *args)
{
int consCount1;
int consCount2;
FILE *fp;
fp = fopen("output.txt", "w");
product prod;
int prodType = *(int *)args;
while (1)
{
if (prodType == 1)
{
get(&q1);
consCount1++;
fprintf("Thread ID: %d\n", prodType);
fprintf(fp, "Product Type: %d\n", prod.productType);
fprintf(fp, "Production Sequence #: %d\n", prod.count);
fprintf(fp, "Consumption Sequence #: %d\n", consCount1);
}
else
{
get(&q2);
consCount2++;
fputs("Thread ID: 2\n", fp);
fprintf(fp, "Product Type: %d\n", prod.productType);
fprintf(fp, "Production Sequence #: %d\n", prod.count);
fprintf(fp, "Consumption Sequence #: %d\n", consCount2);
}
}
fclose(fp);
}
// https://www.youtube.com/watch?v=oyX30WVuEos&t=196s
// Circular buffer
void initQ(queue *q, int size)
{
q->size = size;
q->values = malloc(sizeof(product) * q->size);
q->numEntries = 0;
q->head = NULL;
q->tail = NULL;
}
// Checks if the queue is empty
bool QEmpty(queue *q)
{
return (q->numEntries == 0);
}
// Checks if the queue is full
bool QFull(queue *q)
{
return (q->numEntries == q->size);
}
// Used for adding products to the queue
bool put(queue *q, product prod)
{
// If the queue is full we can not add to it
if (QFull(q))
{
return false;
}
// Add product to the end of the queue
q->values[q->tail] = prod;
q->numEntries++;
// Move index of the tail
q->tail = (q->tail + 1);
// If index goes out of bounds set back to 0
if (q->tail >= q->size)
{
q->tail = 0;
}
return true;
}
// Used for removing products for the queue
product get(queue *q)
{
product result;
// If the queue is empty we can not dequeue anymore
if (QEmpty(q))
{
perror("Error on dequeue");
}
// Remove from the head of the queue
result = q->values[q->head];
q->head = (q->head + 1) & q->size;
q->numEntries--;
return result;
}
I suggest to revise the code to make it more readable since now it's quite hard to understand.
Here I point out some issues of your code that lead to errors:
if condition if (pidN = fork() == 0) {...}
you should fork() first and then check the pid:
int pidN = fork();
if (pidN == 0) {...}
fprintf() function fprintf("Thread ID: %d\n", prodType);
either write the function fputs or fprintf:
fputs("Thread ID: \n", fp);
or
fprintf(fp, "Thread ID: %d\n", prod.productType);
Parameter of the fprintf() function: pay attention to the variable you're passing since prodType does not exist.
I am doing an assignment that implements the producer / consumer problem using a UNIX message queue as the data structure shared by a single producer and three consumers. The program I am creating is supposed to create a child process and the child process will in turn create three threads. The parent process is to be the producer and the three threads will be the consumers. The number of items, N, that are to be produced will be provided to the program via the command line. After spawning the child process the parent will enter an N-iteration loop. In each iteration of the loop the parent will do the following:
1) Generate a random number, R, in the range of 0-999.
2) Send a message containing R.
3) Add R to a running total of all values produced.
4) Display the string “Producer produced a R”.
5) Put the producer to sleep for 0-1 seconds using sleep(rand()%2).
After the N iterations have completed display the string "Total produced = XXXX” (where XXXX is the sum of all R values produced) and wait for the child to terminate. It is the parent’s responsibility to create and destroy the queue.
The child process will create three consumer threads, 0, 1 and 2. Each thread will enter an N/3 iteration loop. In each iteration of the loop each consumer thread will do the following:
1) Read a message containing a value, C, consumed from the queue.
2) Add C to a running total maintained by each consumer thread.
3) Display the string “Consumer thread Z consumed a C” where Z is the thread number – 0,1 or 2.
4) Put the consumer thread to sleep for 1-3 seconds using sleep((rand()%3)+1)
After N/3 iterations display the string "Total consumed by consumer thread Z = YYYY” where YYYY is the sum of all N/3 values consumed.I am receiving a segmentation fault in the last iteration of the loop and I am not sure why. Can anyone help me with this issue?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <pthread.h>
#include <sys/sem.h>
#include <semaphore.h>
struct msgbuf {
long mtype;
int mnum;
};
int msqid;
unsigned long total;
pthread_mutex_t sem_id;
int init() {
srand (time(NULL));
msqid = msgget(IPC_PRIVATE, IPC_CREAT | 0600);
if (msqid == -1) { perror("msgget"); return EXIT_FAILURE; }
pthread_mutex_init(&sem_id, NULL);
total = 0;
return 0;
}
int producer() {
int R = rand() % 999;
struct msgbuf msg = {999, R};
if(msgsnd(msqid, &msg, sizeof(msg.mnum) + 1, 0) == -1) {
perror("msgsnd"); return -1; }
total += R;
return R;
}
void *consumer(int thread_num, int iteration) {
struct msgbuf msg;
int thread_total = 0;
while(iteration--) {
pthread_mutex_lock(&sem_id);
if (msgrcv(msqid, &msg, sizeof(msg.mnum) + 1, 0, 0) == -1)
perror("msgrcv");
thread_total += msg.mnum;
printf("Consumer thread %d consumed a %d\n", thread_num, msg.mnum);
pthread_mutex_unlock(&sem_id);
}
printf("Total consumed by consumer thread %d = %d\n", thread_num,
thread_total);
sleep((rand()%3)+1);
}
int main(int argc, char *argv[]) {
int N = argc > 1 ? atoi(argv[1]) : 10;
if (init() != 0) return EXIT_FAILURE;
for (int i=0;i<N;i++) {
int R = producer();
if(R == -1) return EXIT_FAILURE;
printf("Producer produced a %d\n", R);
sleep(rand()%2);
}
printf("Total produced = %lu\n", total);
pthread_t thread_nums[3];
for (int i=0; i<=4; i++) {
int iteration = i == 0 ? N/3 + (N%3) : N/3;
if(pthread_create(&thread_nums[i], NULL,
consumer(i, iteration), NULL) != 0) {
perror("pthread_create");
return EXIT_FAILURE;
}
}
for (int i=0;i<4;i++) pthread_join(thread_nums[i], NULL);
return 0;
}
Ok so in the main function, thread_nums has 3 "slots", where as the loop proceeding wants to go over 5 different "slots".
Also, the last loop accesses a "slot" which does not exist
Remember that an array of size [3] has only three positions. The last item is at index 2 (0,1,2), for a total of three elements.
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
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