Related
I have the following program:
#include <stdio.h>
#include <sys/types.h>
#define MAX_COUNT 100
void ChildProcess(void);
void ParentProcess(void);
void main(void)
{
pid_t pid;
pid = fork();
if (pid == 0)
ChildProcess();
else
ParentProcess();
}
void ChildProcess(void)
{
int i;
for (i = 1; i <= MAX_COUNT; i++)
printf(" This line is from child, value = %d\n", i);
printf(" *** Child process is done ***\n");
}
void ParentProcess(void)
{
int i;
for (i = 1; i <= MAX_COUNT; i++)
printf("This line is from parent, value = %d\n", i);
printf("*** Parent is done ***\n");
}
I have to modify it in a way that both the parent and the child print stored data from the shared memory in the following way:
Create and initialize the shared memory in the parent.
Fill the shared memory with 5 integer numbers. (I should allocate enough shared memory to store the 5 ints.)
Fork from the parent to the child.
If fork is successful, then the child process must print the values stored in the shared memory as shown in the expected output where N1, N2, N3, N4, N5 are the numbers found in the shared memory.
Expected output
What I did in the ParentProcess function is the following:
void ParentProcess(void)
{
int i;
for (i = 1; i <= MAX_COUNT; i++)
printf("This line is from parent, value = %d\n", i);
printf("*** Parent is done ***\n");
int localVar = 0;
int* p = (int*) malloc(2);
pid_t childPID = fork();
*p = 0;
if (childPID >= 0)
{
printf("\nChild process has started\n");
if (childPID == 0)
{
localVar++;
globalVar++;
printf("Child process has found the following data %d,", *p);
*p = 70;
printf( " %d,", *p);
*p = 66;
printf(" %d,", *p);
*p = 51;
printf(" %d,", *p);
*p = 90;
printf(" %d in shared memory\n",*p);
printf("Child is existing\n\n");
}
}
}
And now I realize that I did it completely wrong but I have no idea how to fix that. I suppose I have to use shmget to create the shared memory, but then what? How do I store values in it?
If you find that you cannot help me with this or it is too long, please share sources where I can learn more about C programming in Linux, particularly regarding the usage of shared memory. Thank you in advance
It may be better to make it clear what you want to do first because as far as I read your code you call fork() twice in your code (once in main() function and once in ParentProcess() function)
So I write general solution for parent/child shared memory. There are several ways to achieve shared memory but this is one example which is modified version of the code here
How to use shared memory with Linux in C
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
void *create_shared_memory(size_t size)
{
int protection = PROT_READ | PROT_WRITE;
int visibility = MAP_SHARED | MAP_ANONYMOUS;
return mmap(NULL, size, protection, visibility, -1, 0);
}
int main()
{
// Allocate 4 ints
void *shmem = create_shared_memory(sizeof(int)*4);
if( shmem == NULL ){
fprintf(stderr, "Failed to create shared memory\n");
return -1;
}
// Initialize 4 ints
((int*)shmem)[0] = 10;
((int*)shmem)[1] = 100;
((int*)shmem)[2] = 1000;
((int*)shmem)[3] = 10000;
int pid = fork();
if (pid == 0)
{
// Print 4 ints in child
printf("Child reading int 0: %d\n", ((int*)shmem)[0]);
printf("Child reading int 1: %d\n", ((int*)shmem)[1]);
printf("Child reading int 2: %d\n", ((int*)shmem)[2]);
printf("Child reading int 3: %d\n", ((int*)shmem)[3]);
printf("Child end\n");
}
else
{
printf("Parent waiting for child ends...\n");
waitpid(pid, NULL, 0);
printf("Parent ends\n");
}
int ret = munmap(shmem, sizeof(int)*4);
if( ret != 0 ){
fprintf(stderr, "Failed to unmap shared memory\n");
return -1;
}
return 0;
}
I've written a small piece of c code which you might find helpful:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define NUM_INTS 5
int main(int argc, char *argv[])
{
key_t key = (key_t) 123456;
int shmgetrc, semgetrc;
struct shmid_ds ds;
int *shared_values;
int i;
struct sembuf sops[2];
int semid;
sops[0].sem_num = 0; /* Operate on semaphore 0 */
sops[0].sem_op = 0; /* Wait for value to equal 0 */
sops[0].sem_flg = 0;
sops[1].sem_num = 0; /* Operate on semaphore 0 */
sops[1].sem_op = 1; /* Increment value by one */
sops[1].sem_flg = 0;
/* create SHM segment */
shmgetrc = shmget(key, NUM_INTS * sizeof(int), IPC_CREAT | IPC_EXCL | 0x180);
if (shmgetrc < 0) {
perror("shmget failed...");
exit(1);
}
/* retrieve the address of the segment */
shared_values = (int *) shmat(shmgetrc, NULL, 0);
/* create a semaphore */
semgetrc = semget(key, 1, IPC_CREAT | IPC_EXCL | 0x180);
if (semgetrc < 0) {
perror("semget failed...");
exit(1);
}
/* lock the semaphore */
if (semop(semgetrc, sops, 2) == -1) {
perror("semop lock failed ...");
exit(1);
}
/* fill it with values */
for (i = 0; i < NUM_INTS; ++i) {
shared_values[i] = i;
}
/* unlock the semaphore */
sops[0].sem_op = -1;
if (semop(semgetrc, sops, 1) == -1) {
perror("semop release failed ...");
exit(1);
}
/* here something else could happen */
sleep(60);
/* lock the semaphore */
sops[0].sem_op = 0;
if (semop(semgetrc, sops, 2) == -1) {
perror("semop lock failed ...");
exit(1);
}
/* print values */
for (i = 0; i < NUM_INTS; ++i) {
printf("%d ", shared_values[i]);
}
printf("\n");
/* unlock the semaphore */
sops[0].sem_op = -1;
if (semop(semgetrc, sops, 1) == -1) {
perror("semop release failed ...");
exit(1);
}
/* remove the semaphore */
if (semctl(semgetrc, semgetrc, IPC_RMID) < 0) {
perror("semctl failed ...");
exit(1);
}
/* remove shm segment again */
if (shmctl(shmgetrc, IPC_RMID, &ds) < 0) {
perror("shmctl failed ...");
exit(1);
}
exit(0);
}
It was not my intention to write the most beautiful code ever written, just an example that shows:
how to create a shm segment
how to retrieve the address and to use it
how to remove it
Additionally, I've used a semaphore to protect the access.
Contrary to the other answer, I've used the ipc interface, not mmap().
Im working on a project that is mainly producer-consumer with pthread conditions and mutexes on a circular buffer. The user gives how many consumers and producer threads it wants, random number generator seed, how many numbers each producer produces and the size of the circular buffer. All of them along with the condition and the mutex are global variables
Here is the producer thread code
void * newProducer(void * t){
// Get the thread id
int * a = (int *) t;
int id = *a;
printf("Producer thread %d started!\n", id);
int i;
//Get the seed
//seed is global vairable(Random number generator seed
unsigned int thread_seed = seed * id;
//Allocate memory for the array to save the numbers
int * numbers = (int *) malloc(sizeof(int) * n);
//Open the files
FILE * producer_file = fopen("prods_out.txt", "w");
if(producer_file == NULL){
printf("ERROR: fopen failed.\n");
exit(1);
}
fprintf(producer_file, "-- PRODUCER DATA --\n");
//Produce the numbers
for( i = 0; i < n; i++){
numbers[i] = rand_r(&thread_seed) % 100;
//Lock mutex
if(pthread_mutex_lock(&mutex) < 0){
printf("ERROR: pthread_mutex_lock failed.\n");
exit(1);
}
//printf("Producer thread <%d> locked mutex\n", *id);
//Wait until the cb has space to store numbers
while(cb->count == cb->capacity) {
if(pthread_cond_wait(&cond, &mutex) < 0){
printf("ERROR: pthread_cond_wait failed\n");
exit(1);
}
printf("Producer <%d> waiting\n", id);
}
//Put the number in the cb
cb_push_back(cb, &numbers[i]);
// printf("Producer <%d>: %d\n", id, numbers[i]);
fprintf(producer_file, "Producer <%d>: %d\n", id, numbers[i]);
count++;
//Count is a global variable to inform the consumers that there're
//no more numbers to be produced
//Tell the consumer threads that you have put a number to the cb
if(pthread_cond_broadcast(&cond) < 0){
printf("ERROR: pthread_cond_broadcast failed.\n");
exit(1);
}
//Unlock the mutex
if(pthread_mutex_unlock(&mutex) < 0){
printf("ERROR: pthread_mutex_unlock failed\n");
exit(1);
}
//printf("Producer thread <%d> unlocked mutex\n", *id);
}
// Free array
free(numbers);
//Close File
fprintf(producer_file, "-- END DATA --");
if( fclose(producer_file) != 0){
printf("ERROR: fclose failed.\n");
exit(1);
}
//THREAD EXIT
printf("PRODUCER %d EXITS\n", id);
pthread_exit(t);
}
And here is the consumer thread
void * newConsumer(void * t){
// Get the thread id
int * a = (int*) t;
int id = *a;
printf("Consumer thread %d started!\n", id);
int number;
//Open the file
FILE * consumer_file = fopen("cons_out.txt", "w");
if(consumer_file == NULL){
printf("ERROR: fopen failed.\n");
exit(1);
}
fprintf(consumer_file, "-- CONSUMER DATA --\n");
/*Consume integers until there are no integers left
either to be produced or in the buffer waiting */
while(count != p*n || cb->count != 0){
//Lock the mutex
//printf("Consumer <%d> has locked the mutex\n", *id);
if (pthread_mutex_lock(&mutex) < 0){
printf("ERROR: Could not lock mutex\n");
exit(1);
}
//Wait until a producer puts an integer in the cb
while(cb->count == 0 && end == 0){
printf("Consumer <%d> waiting\n", id);
if(pthread_cond_wait(&cond, &mutex) < 0){
printf("ERROR: pthread_cond_wait failed.\n");
exit(1);
}
}
if (cb->count != 0){
//Consume a number
cb_pop_front(cb, &number);
// printf("Consumer <%d>: %d\n", id, number);
fprintf(consumer_file, "Consumer <%d>: %d\n", id, number);
}
//Tell the producers that you have popped a number from the cb
if(pthread_cond_broadcast(&cond) < 0){
printf("ERROR: pthread_cond_broadcast() failed.\n");
exit(1);
}
//printf("Consumer <%d> Broadcast.\n", id);
//Unlock the mutex
//printf("Consumer <%d> has unlocked the mutex\n", *id);
if(pthread_mutex_unlock(&mutex) < 0){
printf("ERROR: pthread_mutex_unlock failed\n");
exit(1);
}
}
//Close the file stream
fprintf(consumer_file, "-- END DATA --");
if(fclose(consumer_file) != 0){
printf("ERROR: fclose failed.\n");
exit(1);
}
// THREAD EXIT
printf("CONSUMER %d EXITS\n", id);
pthread_exit(t);
}
In the main function i run
pthread_t * CONSUMERS[i] = (pthread_t*) malloc(sizeof(pthread_t) * i);
if (CONSUMERS == NULL){
printf("ERROR: Malloc failed.\n");
exit(1);
}
for (i = 0; i<c; i++){
id[i] = i+1;
pthread_create(&CONSUMERS[i], NULL, newConsumer, &id[i]);
}
and the I wait for them to finish.
i do exactly the same for the producer threads.( With a diffrent pthread_t array and id array.)
So the thing is : the id I put at the args at create is getting changed. If I run 5 consumer threads even though I put their id as 1 , 2, 3, 4, 5 it comes out as 65, 2, 3, 4, 68. This happens to the producer threads as well.
Thanks.
The following code is supposed to create one Producer thread and one Consumer thread. The Producer creates random numbers, adds them to a circular buffer(push_back()) and then writes them in an output txt called prod_out.txt. The Consumer "consumes" these numbers by poping them (pop_front()) and then writing them in an output txt called cons_out.txt. For a call like this ./a.out 1 1 10 20 1 the arguements are as follows: there will be 1 producer, 1 consumer, 10 is circular buffer's capacity, 20 random numbers will be generated and the seed for rand_r is 1.
If the buffer's capacity is smaller than the ammount of random nums to be generated the program only writes 9 numbers in the consumers' txt, while the producers' txt is just fine. If they are the same then the consumers' txt does not get created and producers' txt misses one number (namely the last one, just like the consumer txt does when the capacity is smaller than the numbers generated).
This is the main function
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include "prodcons.h"
FILE * fp;
int rc;
circular_buffer* cb;
int fileW, my_random;
pthread_mutex_t Mutex;
pthread_cond_t Condition;
args* t_args;
void* Producers(void* fargs){
printf("I am Producer and I am not locked\n");
args* f_args= fargs;
rc = pthread_mutex_lock(&Mutex);
printf("I am Producer and I am locked\n");
if (rc != 0) {
printf("ERROR: return code from pthread_mutex_lock() is %d\n", rc);
pthread_exit(&rc);
}
printf(" The ruler of all mutexes has passed from here and shall start
creating random numbers\n");
for(int i=0; i < *(f_args->rand_count); i++){
if(i==cb->capacity){
printf(" Your producer king will now sleep\n");
rc = pthread_cond_signal(&Condition);
if (rc != 0) {
printf("ERROR: return code from pthread_cond_signal() is %d\n",
rc);
pthread_exit(&rc);
}
rc = pthread_cond_wait(&Condition, &Mutex);
if (rc != 0) {
printf("ERROR: return code from pthread_cond_wait() is %d\n",
rc);
pthread_exit(&rc);
}
}
my_random =rand_r(f_args->seed)%255;
printf ( "Creating random number %d.\n", my_random);
cb_push_back(cb, &my_random);
if(i==0){
fp=fopen("prod_in.txt", "w");
}else{
fp=fopen("prod_in.txt", "a");
}
fprintf(fp, "Producer %d: %d\n", f_args->ID, my_random);
fclose(fp);
}
printf("I am going to go to signal the consumer.\n");
rc = pthread_cond_signal(&Condition);
if (rc != 0) {
printf("ERROR: return code from pthread_cond_signal() is %d\n", rc);
pthread_exit(&rc);
}
rc = pthread_mutex_unlock(&Mutex);
if (rc != 0) {
printf("ERROR: return code from pthread_mutex_unlock() is %d\n", rc);
pthread_exit(&rc);
}
}
void* Consumer(void* t1){
int *threadId = (int *) t1;
printf("I am Consumer and I am NOT locked\n");
rc = pthread_mutex_lock(&Mutex);
printf("I am Consumer and I am locked\n");
if (rc != 0) {
printf("ERROR: return code from pthread_mutex_lock() is %d\n", rc);
pthread_exit(&rc);
}
if(cb->count == 0){
rc = pthread_cond_wait(&Condition, &Mutex);
if (rc != 0) {
printf("ERROR: return code from pthread_cond_wait() is %d\n", rc);
pthread_exit(&rc);
}
}
int counter = cb-> count;
for (int j=0; j<= counter; j++){
printf("Consumer will now consume with items remaining %d.\n", (int)cb-
>count);
if(j==0){
fp=fopen("cons_out.txt", "w");
}else{
fp=fopen("cons_out.txt", "a");
}
printf("I will now write the following %d, %d inside the
file.\n",*threadId,my_random);
fprintf(fp, "Consumer %d: %d\n",*threadId, my_random);
cb_pop_front(cb,&my_random);
if(cb->count == 0){
rc = pthread_cond_signal(&Condition);
if (rc != 0) {
printf("ERROR: return code from pthread_cond_signal() is %d\n",
rc);
pthread_exit(&rc);
}
rc = pthread_cond_wait(&Condition, &Mutex);
if (rc != 0) {
printf("ERROR: return code from pthread_cond_wait() is %d\n",
rc);
pthread_exit(&rc);
}
}
fclose(fp);
}
rc = pthread_mutex_unlock(&Mutex);
if (rc != 0) {
printf("ERROR: return code from pthread_mutex_unlock() is %d\n", rc);
pthread_exit(&rc);
}
}
int main(int argc, char *argv[]){
if (argc !=6){
printf("ERROR: the program should take five arguments!\n");
exit(-1);
}
if(atoi(argv[1])!= 1 || atoi(argv[2])!= 1){
printf("ERROR:This program only functions for one producer and
consumer.\n");
exit(-1);
}
int main_seed = atoi(argv[5]);
int main_rand = atoi(argv[4]);
int buffer_size = atoi(argv[3]);
cb=(circular_buffer*) malloc(sizeof(struct circular_buffer));
t_args=(args*)malloc(sizeof(struct args));
cb_init(cb, buffer_size, sizeof(int));
t_args->seed= &main_seed;
t_args->rand_count= &main_rand;
t_args->ID=1;
int t1=1;
pthread_t p1, c1;
printf ( "I am the ruler of all mutexes. I now shall initialise the
mutex\n");
rc = pthread_mutex_init(&Mutex, NULL);
if (rc != 0) {
printf("ERROR: return code from pthread_mutex_init() is %d\n", rc);
exit(-1);
}
rc = pthread_cond_init(&Condition, NULL);
if (rc != 0) {
printf("ERROR: return code from pthread_cond_init() is %d\n", rc);
exit(-1);
}
rc = pthread_create(&p1, NULL, Producers, (void*)t_args); //cast σε void????
if (rc != 0) {
printf("ERROR: return code from pthread_create() is %d\n", rc);
exit(-1);
}
rc = pthread_create(&c1, NULL,Consumer, &t1);
if (rc != 0) {
printf("ERROR: return code from pthread_create() is %d\n", rc);
exit(-1);
}
void *status;
rc = pthread_join(p1, &status);
if (rc != 0) {
printf("ERROR: return code from pthread_join() is %d\n", rc);
exit(-1);
}
printf("Main(): Thread %d terminated successfully.\n", *(int *) status);
rc = pthread_join(c1, &status);
if (rc != 0) {
printf("ERROR: return code from pthread_join() is %d\n", rc);
exit(-1);
}
printf("Main(): Thread %d terminated successfully.\n", *(int *) status);
rc = pthread_mutex_destroy(&Mutex);
if (rc != 0) {
printf("ERROR: return code from pthread_mutex_destroy() is %d\n", rc);
exit(-1);
}
rc = pthread_cond_destroy(&Condition);
if (rc != 0) {
printf("ERROR: return code from pthread_cond_destroy() is %d\n", rc);
exit(-1);
}
free(cb);
free(t_args);
return 1;
}
This is a header file with the 2 structs and the circular buffer functions
#include "stdio.h"
#include "sys/types.h"
#include "stdlib.h"
#include "string.h"
typedef struct circular_buffer
{
void *buffer; // data buffer
void *buffer_end; // end of data buffer
size_t capacity; // maximum number of items in the buffer
size_t count; // number of items in the buffer
size_t sz; // size of each item in the buffer
void *head; // pointer to head
void *tail; // pointer to tail
}circular_buffer;
typedef struct args
{
int ID; //Unique ID from 1 to n
int* seed; //The seed used for rand_r
int* rand_count; //The number of random generated numbers
}args;
void cb_push_back(circular_buffer *cb, const void *item);
void cb_pop_front(circular_buffer *cb, void *item);
void cb_init(circular_buffer *cb, size_t capacity, size_t sz);
void cb_free(circular_buffer *cb);
In this file are the implementetions of the circular buffer functions
#include "prodcons.h"
//initialize circular buffer
//capacity: maximum number of elements in the buffer
//sz: size of each element
void cb_init(circular_buffer *cb, size_t capacity, size_t sz)
{
cb->buffer = malloc(capacity * sz);
if(cb->buffer == NULL){
printf("Could not allocate memory..Exiting! \n");
exit(1);
}
// handle error
cb->buffer_end = (char *)cb->buffer + capacity * sz;
cb->capacity = capacity;
cb->count = 0;
cb->sz = sz;
cb->head = cb->buffer;
cb->tail = cb->buffer;
}
//destroy circular buffer
void cb_free(circular_buffer *cb)
{
free(cb->buffer);
// clear out other fields too, just to be safe
}
//add item to circular buffer
void cb_push_back(circular_buffer *cb, const void *item)
{
if(cb->count == cb->capacity)
{
printf("Access violation. Buffer is full\n");
exit(1);
}
memcpy(cb->head, item, cb->sz);
cb->head = (char*)cb->head + cb->sz;
if(cb->head == cb->buffer_end)
cb->head = cb->buffer;
cb->count++;
}
//remove first item from circular item
void cb_pop_front(circular_buffer *cb, void *item)
{
if(cb->count == 0)
{
printf("Access violation. Buffer is empy\n");
exit(1);
}
memcpy(item, cb->tail, cb->sz);
cb->tail = (char*)cb->tail + cb->sz;
if(cb->tail == cb->buffer_end)
cb->tail = cb->buffer;
cb->count--;
}
My code should be executing the start_hydrogen and start_carbon functions several times but only outputs to the console from execution of one thread and then the program hangs. I think I may be incorrectly starting my threads. I am new to C so let me know if additional information is needed. Note that the this line is reached output is printed.
#include "main.h"
void *start_hydrogen(void *);//executes hydrogen.c
void *start_carbon(void *);//executes carbon.c
struct threadInfo {
int threadId;
};
struct threadInfo hydrogenIDs[NUM_H];
struct threadInfo carbonIDs[NUM_C];
int main() {
int semid, shmid;//semaphore memory id, shared memory id
unsigned short seminit[NUM_SEMS];//used to initialize semaphores
struct common *shared;//pointer to shared data structure
union semun semctlarg;//used to initialize semaphores
pthread_t hydrogen[NUM_H];
pthread_t carbon[NUM_C];
pthread_attr_t attr;
void *exit_status;
//Creating a set of attributes to send to the threads
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
//get semaphore memory id
if ((semid = semget(SEMKEY, NUM_SEMS, IPC_CREAT|0777)) < 0) {
perror("semget");
exit(EXIT_FAILURE);
}
printf("THE PROGRAM IS STARTING\n\n");
seminit[MUTEX] = 1;//initialize mutex semaphore count to 1
seminit[SH] = 0;//initialize hyrdrogen semaphore count to 0
seminit[SC] = 0;//initialize carbon semaphore count to 0
semctlarg.array = seminit;//set control array
//apply initialization
if ((semctl(semid, NUM_SEMS, SETALL, semctlarg)) < 0) {
perror("semctl");
exit(EXIT_FAILURE);
}
//get shared memory id
if ((shmid = shmget(SHMKEY, 1*K, IPC_CREAT|0777)) < 0) {
perror("shmget");
exit(EXIT_FAILURE);
}
//retrieve pointer to shared data structure
if ((shared = (struct common *)shmat(shmid, 0, 0)) < 0) {
perror("shmat");
exit(EXIT_FAILURE);
}
//initialize shared data structure variables
shared->waiting_H = 0;
shared->waiting_C = 0;
int retVal;//used to check return value of fork()
// spawn 20 Hydrogens
for (int i=0; i<NUM_H; i++) {
// if ((retVal = fork()) == 0) {
// hydrogen();
// fflush(stdout);
// printf("New Hydrogen process created\n");
// fflush(stdout);
// } else if (retVal < 0) {
// perror("fork");
// exit(EXIT_FAILURE);
// }
hydrogenIDs[i].threadId = i;
retVal = pthread_create(&hydrogen[i], &attr, start_hydrogen, (void*) &hydrogenIDs[i]);
if (retVal != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
}
printf("this line reached\n");
// spawn 5 Carbons
for (int i=0; i<NUM_C; i++) {
// if ((retVal = fork()) == 0) {
// carbon();
// fflush(stdout);
// printf("New Hydrogen process created\n");
// fflush(stdout);
// } else if (retVal < 0) {
// perror("fork");
// exit(EXIT_FAILURE);
// }
carbonIDs[i].threadId = i;
retVal = pthread_create(&carbon[i], &attr, start_carbon, (void*) &carbonIDs[i]);
if (retVal != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
}
//wait for all car processes to finish
// for (int i = 0; i < 25; ++i) {
// if (wait(0) < 0) {
// perror("wait");
// exit(EXIT_FAILURE);
// }
// }
//Wait for all the threads to finish
for(int i = 0; i < NUM_C; i++)
{
pthread_join(carbon[i], &exit_status);
}
for(int i = 0; i < NUM_H; i++)
{
pthread_join(hydrogen[i], &exit_status);
}
printf("All atoms have crossed!\n");
//delete semaphores
if (semctl(semid, NUM_SEMS, IPC_RMID, 0) < 0) {
perror("semctl");
exit(EXIT_FAILURE);
}
//delete shared memory
if (shmctl(shmid, IPC_RMID, 0) < 0) {
perror("shmctl");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
Original start_carbon() and start_hydrogen() functions
void *start_carbon(void* arg) {
execl("carbon", "carbon", 0);
perror("execl");
exit(EXIT_FAILURE);//if exec returns there was an error
}
void *start_hydrogen(void* arg) {
execl("hydrogen", "hydrogen", 0);
perror("execl");
exit(EXIT_FAILURE);//if exec returns there was an error
}
Modified start_hydrogen() and start_carbon() functions
After receiving feedback about the inappropriateness of using execl(), I changed the start_hydrogen() and start_carbon() functions to:
void *start_hydrogen(void* arg) {
struct common *shared;//pointer to shared data structure
int semid, shmid;//semaphore memory id, shared memory id
int pid = getpid();
//get semaphore memory id
if ((semid = semget(SEMKEY, NUM_SEMS, 0777)) < 0) {
perror("semget");
exit(EXIT_FAILURE);
}
//get shared memory id
if ((shmid = shmget(SHMKEY, 1*K, 0777)) < 0) {
perror("shmget");
exit(EXIT_FAILURE);
}
//get pointer to shared data structure
if ((shared = (struct common *)shmat(shmid, 0, 0)) < 0) {
perror("shmat");
exit(EXIT_FAILURE);
}
// acquire lock on mutex before accessing shared memory
semWait(semid, MUTEX);
fflush(stdout);
printf("Hydrogen atom, pid %d, arrives at barrier\n", pid);
printf("Currently %d Hydrogens and %d Carbons waiting\n", shared->waiting_H + 1, shared->waiting_C);
fflush(stdout);
// if enough C and H is waiting, continue past barrier
if (shared->waiting_H >= 3
&& shared->waiting_C >= 1) {
// release 3 H
for (int i=0; i < 3; i++) {
semSignal(semid, SH);
}
shared->waiting_H -= 3;
semSignal(semid, SC); // release 1 C
shared->waiting_C -= 1;
fflush(stdout);
printf("\nHello from %d, 1 CH4 molecule has xed the barrier\n\n", pid);
fflush(stdout);
// release lock on mutex
semSignal(semid, MUTEX);
} else {
// not enough C or H is waiting, so wait at barrier
shared->waiting_H += 1;
// relaese lock on mutex
semSignal(semid, MUTEX);
semWait(semid, SH);
}
pthread_exit(NULL);
}
void *start_carbon(void* arg) {
struct common *shared;//pointer to shared data structure
int semid, shmid;//semaphore memory id, shared memory id
int pid = getpid();
//get semaphore memory id
if ((semid = semget(SEMKEY, NUM_SEMS, 0777)) < 0) {
perror("semget");
exit(EXIT_FAILURE);
}
//get shared memory id
if ((shmid = shmget(SHMKEY, 1*K, 0777)) < 0) {
perror("shmget");
exit(EXIT_FAILURE);
}
//get pointer to shared data structure
if ((shared = (struct common *)shmat(shmid, 0, 0)) < 0) {
perror("shmat");
exit(EXIT_FAILURE);
}
// acquire lock on mutex before accessing shared memory
semWait(semid, MUTEX);
fflush(stdout);
printf("Hydrogen atom, pid %d, arrives at barrier\n", pid);
printf("Currently %d Hydrogens and %d Carbons waiting\n", shared->waiting_H + 1, shared->waiting_C);
fflush(stdout);
// if enough C and H is waiting, continue past barrier
if (shared->waiting_H >= 3
&& shared->waiting_C >= 1) {
// release 3 H
for (int i=0; i < 3; i++) {
semSignal(semid, SH);
}
shared->waiting_H -= 3;
semSignal(semid, SC); // release 1 C
shared->waiting_C -= 1;
fflush(stdout);
printf("\nHello from %d, 1 CH4 molecule has xed the barrier\n\n", pid);
fflush(stdout);
// release lock on mutex
semSignal(semid, MUTEX);
} else {
// not enough C or H is waiting, so wait at barrier
shared->waiting_H += 1;
// relaese lock on mutex
semSignal(semid, MUTEX);
semWait(semid, SH);
}
pthread_exit(NULL);
}
From the man page for execl(),
The exec() family of functions replaces the current process image with a new process image.
and
The exec() functions only return if an error has occurred.
If you want to use execl() to call external programs, you should fork() the parent process first to allow the parent process to continue running. Note in that case, the threads really won't do what you want.
I can't exit or terminate children processes sending a signal.
Could you please tell me what I'm doing wrong in this code:
//###################################### INVERTER.C (main)
#include <semaphore.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <errno.h>
#include <unistd.h>
#include "timeprofiler.h"
#include "ppmtools.h"
//Global vars:
int shmids[4], shmPixelId, *total_lines, *processed_lines, *next_line, *buf_vars;
//To share unnamed semaphores between processes, they must be allocated in a shared memory.
mem_struct *sh_mm;
//unnamed semaphores
sem_t *mutex1, *mutex2, *mutex3, *sem_remaining_lines;
//struct that will hold the image in shared memory
image_struct *image;
pid_t *workersPID;
header *h;
int main(int argc, char *argv[]) {
int i, j, k, cur = 0, id;
pixel *row;
double start, stop, startms, stopms;
if (argc < 3) {
printf("Incorrect usage.\nPlease use \"./invert input_filename.ppm output_filename.ppm\"\n");
return -1;
}
//BLOCK ALL SIGNAL
sigset_t block_ctrlc;
sigfillset(&block_ctrlc);
sigdelset(&block_ctrlc, SIGINT);
sigprocmask(SIG_BLOCK, &block_ctrlc, NULL);
//start timer
start = getCurrentTimeMicro();
startms = getCurrentTimeMili();
printf("Opening input file [%s]\n", argv[1]);
FILE *fpin = fopen(argv[1], "r");
if (fpin == NULL) {
printf("Could not open input file\n");
return -1;
}
printf("Opening output file [%s]\n", argv[2]);
FILE *fpout = fopen(argv[2], "w");
if (fpout == NULL) {
printf("Could not open output file\n");
return -1;
}
printf("Getting header\n");
h = getImageHeader(fpin);
if (h == NULL) {
printf("Error getting header from file\n");
return -1;
}
printf("Got file Header: %s - %u x %u - %u\n", h->type, h->width, h->height, h->depth);
printf("Saving header to output file\n");
if (writeImageHeader(h, fpout) == -1) {
printf("Could not write to output file\n");
return -1;
}
init();
printf("After init...\n");
//alloc mem space for one row (width * size of one pixel struct)
row = (pixel *) malloc(h->width * sizeof (pixel));
printf("Starting work\n");
for (i = 0; i < h->height; i++) {
printf("Reading row... \n");
if (getImageRow(h->width, row, fpin) == -1) {
printf("Error while reading row\n");
}
printf("Got row %d || \n", (i + 1));
for (j = cur, k = 0; j < cur + h->width; j++, k++) {
image->pixel_data[j].red = row[k].red;
image->pixel_data[j].blue = row[k].blue;
image->pixel_data[j].green = row[k].green;
}
cur += h->width;
}
/*Creates workers*/
workersPID = (pid_t*) malloc(sizeof (pid_t) *((NUM_WORKERS)));
for (i = 0; i < NUM_WORKERS; i++) {
id = fork();
if (id == -1) {
printf("Error creating worker no %d\n", i);
return (EXIT_FAILURE);
} else if (id == 0) {
workersPID[i] = getpid();
printf("Launching son with pid %d\n", getpid());
worker(i);
}
}
cur = 0;
sem_wait(mutex2);
/*Writes the invert image on the output file*/
for (i = 0; i < h->height; i++) {
for (j = cur, k = 0; j < cur + h->width; j++, k++) {
row[k].red = image->pixel_data[j].red;
row[k].blue = image->pixel_data[j].blue;
row[k].green = image->pixel_data[j].green;
}
cur += h->width;
printf("Saving row... \n");
if (writeRow(h->width, row, fpout) == -1) {
printf("Error while writing row\n");
}
printf("Done\n");
}
printf("Cleaning up...\n");
//clean up row
free(row);
//clean up header
free(h);
printf("Closing file pointers.\n");
fclose(fpin);
fclose(fpout);
//stop timer
stop = getCurrentTimeMicro();
stopms = getCurrentTimeMili();
for (i = 0; i < NUM_WORKERS; i++) {
if (workersPID[i]) {
kill(workersPID[i], SIGTERM);
waitpid(workersPID[i], NULL, 0);
}
}
terminate();
printTimeElapsed(start, stop, "microseconds");
printTimeElapsed(startms, stopms, "miliseconds");
printf("Done!\n");
return 0;
}
void init() {
//create shared memory to hold the source image:
if ((shmids[0] = shmget(IPC_PRIVATE, sizeof (image_struct), IPC_CREAT | 0700)) == -1) {
printf("shmget to allocate image struct failed. Errno returned: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
image = (image_struct*) shmat(shmids[0], NULL, 0);
//shared memory to allocate the pointer to pointer pixel_data
if ((shmids[1] = shmget(IPC_PRIVATE, h->width * h->height * sizeof (pixel), IPC_CREAT | 0700)) == -1) {
printf("shmget to allocate pixel_data array failed. Errno returned: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
image->pixel_data = (pixel*) shmat(shmids[1], NULL, 0);
/*Shared Memory segment for 3 integers*/
if ((shmids[2] = shmget(IPC_PRIVATE, 3 * sizeof (int), IPC_CREAT | 0700)) == -1) {
printf("shmget to allocate the 3 integers failed. Errno returned; %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
buf_vars = (int*) shmat(shmids[2], NULL, 0);
total_lines = &buf_vars[0];
processed_lines = &buf_vars[1];
next_line = &buf_vars[2];
*processed_lines = *next_line = 0;
*total_lines = h->height;
if ((shmids[3] = shmget(IPC_PRIVATE, sizeof (mem_struct), IPC_CREAT | 0700)) == -1) {
printf("shmget to allocate mem_Struct for semaphores failed. Errno returned %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
sh_mm = (mem_struct*) shmat(shmids[3], NULL, 0);
if (sem_init(&sh_mm->mutex1, 1, 1) == -1) {
printf("Error initializing semaphore mutex1.Errno returned: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
mutex1 = &sh_mm->mutex1;
if (sem_init(&sh_mm->mutex2, 1, 0) == -1) {
printf("Error initializing semaphore mutex2.Errno returned: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
mutex2 = &sh_mm->mutex2;
if (sem_init(&sh_mm->mutex3, 1, 1) == -1) {
printf("Error initializing semaphore mutex3.Errno returned: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
mutex3 = &sh_mm->mutex3;
if (sem_init(&sh_mm->sem_remaining_lines, 1, h->height) == -1) {
printf("Error initializing semaphore sem_remaining_lines.Errno returned: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
sem_remaining_lines = &sh_mm->sem_remaining_lines;
}
/*Worker process*/
void worker(int id) {
int i, k, cur = 0;
pixel *row;
//Block all signals, except SIGINT and SIGKILL which are handled
sigset_t block_ctrlc;
sigfillset(&block_ctrlc);
sigdelset(&block_ctrlc, SIGINT);
sigdelset(&block_ctrlc, SIGTERM);
sigprocmask(SIG_BLOCK, &block_ctrlc, NULL);
signal(SIGINT, handle_signal);
signal(SIGTERM, handle_signal);
while (sem_wait(sem_remaining_lines)!= -1) { //if there are still lines to read, go on
sem_wait(mutex3);
cur = *next_line; //current image's line
*next_line += h->width; //refreshs line for the next worker
sem_post(mutex3);
row = (pixel *) malloc(h->width * sizeof (pixel));
for (i = cur, k = 0; i < cur + h->width; i++, k++) {
row[k].red = image->pixel_data[i].red;
row[k].blue = image->pixel_data[i].blue;
row[k].green = image->pixel_data[i].green;
}
//printf("% - Inverting row... \n",id);
invertRow(h->width, row); //invert
//printf("Done || \n");
for (i = cur, k = 0; i < cur + h->width; i++, k++) {
image->pixel_data[i].red = row[k].red;
image->pixel_data[i].blue = row[k].blue;
image->pixel_data[i].green = row[k].green;
}
sem_wait(mutex1);
*processed_lines += 1; //increases the number of inverted lines
if (*processed_lines == *total_lines) { //check if it reaches last line
sem_post(mutex2); //if so, wakes the master telling that is ready
}
sem_post(mutex1);
}
//printf("Son %d is exiting\n",id);
exit(0);
}
void handle_signal(int signum) {
if(signum == SIGINT)
signal(SIGINT, handle_signal);
else
signal(SIGTERM, handle_signal);
exit(0);
}
void terminate() {
int i;
//close semaphores
sem_destroy(mutex1);
sem_destroy(mutex2);
sem_destroy(mutex3);
sem_destroy(sem_remaining_lines);
//cleans up shared memory = removes shared memory segments
for (i = 0; i < 4; i++) {
shmctl(shmids[i], IPC_RMID, NULL);
}
}
I'm gonna leave the explanation of the assignment (that has already finished btw)here:
1 page pdf
Your worker threads have SIGTERM blocked (because it was blocked in main, and sigprocmask doesn't remove signals from the blocked set unless explicitly told to do so)
You may want to do something like this in the worker instead:
sigemptyset(&block_ctrlc);
sigaddset(&block_ctrlc, SIGINT);
sigaddset(&block_ctrlc, SIGTERM);
sigprocmask(SIG_UNBLOCK, &block_ctrlc, NULL);
Alternately, call sigprocmask with SIG_SETMASK.