Segmentation fault without even running the first line of code [closed] - c

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 9 months ago.
Improve this question
As you can see in the code I've added printfs everywhere for debugging and have numbered them. I have tried both repl.it and GCC(on gcc it even compiles and everything but when run, it prints the statement that asks for a user input and scans then fails) on ubuntu but both Show segmentation fault without even printing the first printf which is a "1". the code initializes an array in a parent function, sends the array to a child function, then the child prints it. I have to calculate the sum of all the elements in the mentioned array, send it back to the parent, and let the p print it too, but can't even do that if I can't check the functionality of this code
any thoughts?
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sysexits.h>
#include <assert.h>
#include <time.h>
#include <sys/wait.h>
int main() {
printf("\n1\n");
int size;
sem_t *sem1, *sem2, *sem3;
mode_t perms = S_IRUSR | S_IWUSR;
int shmflags = O_CREAT | O_RDWR | O_NONBLOCK;
int semflags = O_CREAT | O_EXCL;
srand(time(NULL));
sem_t *sem = sem_open("/sem1", semflags, perms, 1);
if (sem == SEM_FAILED) {
printf("semaphore_open error \n");
exit(EXIT_SUCCESS);
}
printf("\n2\n");
pid_t pid = fork();
if (pid > 0) {//parent
printf("enter the size of array\n");
scanf("%d", &size);
int array[size];
for (int i = 0; i < size; i++) {
array[i] = rand() % 1001;
}
char str[512];
int i = 0;
int index = 0;
for (i = 0; i < size; i++) {
index += sprintf(&str[index], "%d,", array[i]);
}
printf("\n3\n");
size_t len = strlen(str);
int fd = shm_open("/shmem", shmflags, perms);
if (fd == -1) {
printf("shm_open error \n");
exit(EXIT_SUCCESS);
}
if (ftruncate(fd, len) == -1) {
printf("ftruncate error \n");
exit(EXIT_SUCCESS);
}
char *addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
printf("mmap error \n");
exit(EXIT_SUCCESS);
}
if (close(fd) == -1) {
printf("fd close error \n");
exit(EXIT_SUCCESS);
}
sem_wait(sem);
memcpy(addr, str, strlen(str));
sem_post(sem);
wait(NULL);
} else { //child
struct stat sb;
printf("\n4\n");
int fd = shm_open("/shmem", O_RDONLY, 0);
if (fstat(fd, &sb) == -1) {
printf("fstat error \n");
exit(EXIT_SUCCESS);
}
char *addr = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
printf("mmap error \n");
exit(EXIT_SUCCESS);
}
if (close(fd) == -1) {
printf("fd close error \n");
exit(EXIT_SUCCESS);
}
printf("\n5\n");
sem_wait(sem);
puts(addr);
sem_post(sem);
if (shm_unlink("/shmem") == -1) {
printf("unlink error \n");
exit(EXIT_SUCCESS);
}
if (sem_unlink("/sem1") == -1) {
printf("unlink error \n");
exit(EXIT_SUCCESS);
}
}
return 0;
}

Here are a few good candidates for undefined behavior:
scanf("%d", &size);
int array[size];
for (int i = 0; i < size; i++) {
array[i] = rand() % 1001;
}
If size is too large, say greater than a few millions, the array allocated with automatic storage will likely cause a stack overflow.
char str[512];
int i = 0;
int index = 0;
for (i = 0; i < size; i++) {
index += sprintf(&str[index], "%d,", array[i]);
}
If size entered by the user is larger than 128, the code converting the array to a string will likely cause a buffer overflow causing undefined behavior.
In the child process:
puts(addr);
addr points to the memory mapped file contents. This file does not contain a null terminator, hence if by chance its size is a multiple of the page size, the memory mapped block will not contain a null terminator and puts(addr) will read beyond the end of the block, causing undefined behavior.
Note that none of the above may cause the process to fail before the first printf call.

Related

Taking an exact number of bytes from input in C and synchronization between processes

Hi i need to take only 5 bytes from stdin, i've tried this but i have problem while executing it since it keeps asking me for input and at the end the string contained in buffer is wrong.
Also i'd like to know how to synchronize N processes while the parent is sleeping.
buffers[] is an array of buffers.
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define fflush(stdin) while (getchar() != '\n')
char **filenames;
int *files;
char **buffers;
int n_proc;
int main(int argc, char **argv) {
long i;
pid_t pid;
int status;
if(argc < 2) {
puts("Usage error: prog file1 ... fileN.\n");
exit(1);
}
filenames = argv + 1;
n_proc = argc - 1;
puts("Bef malloc buff.\n");
if((buffers = malloc(sizeof(char *) * n_proc)) == NULL) {
puts("Buffers' malloc error.\n");
exit(1);
}
if((files = malloc(sizeof(int) * n_proc)) == NULL) {
puts("Files' malloc error.\n");
exit(1);
}
puts("After malloc buff.\n");
for(i = 0; i < n_proc; i++) {
if((files[i] = open(filenames[i], O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) {
printf("Error while opening file %ld.\n", i);
exit(1);
}
}
puts("After file open.\n");
for(i = 0; i < n_proc; i++) {
if((buffers[i] = (char *) mmap(NULL, 1028, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0)) == NULL) {
printf("Error in mapping buffer %ld.\n", i);
exit(1);
}
}
puts("After mapping.\n");
i = 0;
while(i < n_proc) {
printf("Fork %ld started.\n", i);
pid = fork();
if(pid < 0) {
printf("Error while forking %ld.\n", i);
exit(1);
} else if(pid == 0) {
puts("Please insert an input of max 5 characters.\n");
printf("Son %ld.\n", i);
fflush(stdout);
fgets(buffers[i], 6, stdin);
buffers[i][strcspn(buffers[i], "\n")] = 0;
//int j;
//for(j = 0; j < 5; j++)
//buffers[i][j] = getchar();
//printf("Buff has %s inside.\n", buff);
//fflush(stdout);
fflush(stdin);
//strcpy(buffers[i], buff);
printf("Buffer %d has string %s inside.\n", i, buffers[i]);
fflush(stdout);
write(files[i], buffers[i], 6);
} else {
printf("Parent %ld.\n", i);
wait(&status);
}
i++;
}
}
This is only a prototype of the code, since there's still synchronization needed and signal handling
Code requires when to write on command line N files and creating N processes that each take 5 bytes from stdin and put in their own file.
As an example if i try with
./a.out hello.txt hello1.txt
Bef malloc buff.
After malloc buff.
After file open.
After mapping.
Fork 0 started.
Parent 0.
Please insert an input of max 5 characters.
Son 0.
Hello
Hello
Buffer 0 has string Hello inside.
Hello
Fork 1 started.
Parent 1.
Please insert an input of max 5 characters.
Son 1.
Hello
Hello
Buffer 1 has string Hello inside.
Hello
Fork 1 started.
Parent 1.
Please insert an input of max 5 characters.
Son 1.
As you can see it doesn't take the input and keeps asking for it, same problem with the getchar().
Note that in case stdin is associated with a terminal, there may also be input buffering in the terminal driver, entirely unrelated to stdio buffering. (Indeed, normally terminal input is line buffered in the kernel.) This kernel input handling can be modified using calls like tcsetattr(3); (stdin(3) man page)
If you give it the input "12345\n":
#include <stdio.h>
int main(void) {
char buffers[1][5];
unsigned i = 0;
for(unsigned j = 0; j < 5; j++)
buffers[i][j] = getchar();
printf("%.5s", buffers[i]);
// read the newline. You may need to discard others.
int ch = getchar();
if(ch == '\n')
printf("\n");
return 0;
}
it will print:
12345

How do I print stored data from the shared memory?

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().

inter process communication using shared memory(mmap) and semaphores

I am trying to improvise a program I had written for single producer multiple consumer multi threading using counting semaphores. I want to implement inter process communication using shared memory (mmap() system call). I want to use anonymous mapping with no backing file.
This is the structure I want to share between the parent and its multiple child processes.
typedef struct Buffer{
char **Tuples;
sem_t buf_mutex,empty_count,fill_count;
} Buffer;
Buffer buffer[100];
The parent process is the mapper() function which does produces something and puts it in buffer[i], based on some inputs. The child processes go to reducer() function which consumes what is put in its buffer[j]. Each reducer or child process should have access to its buffer. The child processes are forked() in the main function and then the parent process control goes to mapper(). I have initialized the synchronization primitives to be process shared.
Is my main() method the correct way of doing it ? I am also getting type casting errors for return value of mmap(), which is a pointer, but I am not sure how to handle it and then use it. I also think malloc() should not be used in line 47 for allocating space to tuples but instead mmap() itself should be used. Can anyone please help ?
This is my program -
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/mman.h>
typedef struct Buffer{
char **Tuples;
// int count;
sem_t buf_mutex,empty_count,fill_count;
} Buffer;
Buffer buffer[100];
int numOfSlots;
int numOfReducers;
void mapper(){
//Synchronization primitives (counting semaphores) used for synchronization
//Produce something and put it in buffer[i]
}
void reducer(long b){
//Synchronization primitives (counting semaphores) for synchronization
//Consume from buffer[b]
}
int main (int argc, char *argv[]){
if(argc != 3) {
if(argc < 3)
printf("Insufficient arguments passed\n");
else
printf("Too many arguments passed\n");
return 1;
}
int i;
long r;
numOfSlots = atoi(argv[1]);
numOfReducers = atoi(argv[2]);
for(i=0; i<numOfReducers; i++){
buffer[i] = (struct Buffer *) mmap(NULL, sizeof(buffer[i]), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (buffer[i] == MAP_FAILED)
errExit("mmap");
buffer[i].Tuples = malloc(numOfSlots * sizeof(char *));
sem_init(&buffer[i].buf_mutex, 1, 1);
sem_init(&buffer[i].fill_count, 1, 0);
sem_init(&buffer[i].empty_count, 1, numOfSlots);
}
for(r=0;r<numOfReducers;r++){ // loop will run n times (n=5)
if(fork() == 0){
printf("[son] pid %d from [parent] pid %d\n",getpid(),getppid());
Reducer(r);
exit(0);
}
}
mapper();
for(r=0;r<numOfReducers;r++) // loop will run n times (n=5)
wait(NULL);
}
These are the links I am trying to follow -
https://computing.llnl.gov/tutorials/pthreads/man/pthread_mutexattr_init.txt
https://github.com/bradfa/tlpi-dist/blob/master/mmap/anon_mmap.c
Thanks,
Harrish
After researching on the net. I came up with this solution which works.
One more thing I learnt is char *var="hello" is stored in read only memory in the text segment, which means the child processes can also access it. So strcpy() would be a better option for anything else.
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <string.h>
typedef struct Buffer{
char **Tuples;
// int count;
sem_t buf_mutex,empty_count,fill_count;
int inSlotIndex;
int outSlotIndex;
} Buffer;
Buffer *buffer;
int numOfSlots;
int numOfReducers;
int *done;
void mapper(){
//Synchronization primitives (counting semaphores) used for synchronization
//read continuously from a file, produce something and put it in buffer[i]
//Here is an example
char *temp = "trail";
sem_wait(&buffer[1].empty_count);
sem_wait(&buffer[1].buf_mutex);
// Use strcpy() for anything other than string literal
buffer[1].Tuples[0] = temp;
buffer[1].inSlotIndex = 3;
buffer[1].outSlotIndex = 4;
sem_post(&buffer[1].buf_mutex);
sem_post(&buffer[1].fill_count);
*done = 1;
}
void reducer(long tid, Buffer *buffer, int *done){
//Synchronization primitives (counting semaphores) used for synchronization
//Consume from buffer[b]
sem_wait(&buffer[tid].fill_count);
sem_wait(&buffer[tid].buf_mutex);
printf("%s\n", buffer[tid].Tuples[0]);
printf("%d\n", buffer[tid].inSlotIndex);
printf("%d\n", buffer[tid].outSlotIndex);
sem_post(&buffer[tid].buf_mutex);
sem_post(&buffer[tid].empty_count);
if(*done == 1)
printf("DONE\n");
}
int main (int argc, char *argv[])
{
if(argc != 3) {
if(argc < 3)
printf("Insufficient arguments passed\n");
else
printf("Too many arguments passed\n");
return 1;
}
srand(time(NULL));
int i, j;
long r;
char *temp;
numOfSlots = atoi(argv[1]);
numOfReducers = atoi(argv[2]);
buffer = (struct Buffer *)mmap(NULL, numOfReducers * sizeof(struct Buffer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if(buffer == MAP_FAILED){
printf("EXITING");
exit(EXIT_FAILURE);
}
done = (int *)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (done == MAP_FAILED){
printf("exiting\n");
}
*done = 0;
for(i=0; i<numOfReducers; i++){
buffer[i].Tuples = mmap(NULL, numOfSlots * sizeof(char *), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if(buffer[i].Tuples == MAP_FAILED){
printf("EXITING");
exit(EXIT_FAILURE);
}
for(j=0; j<numOfSlots; j++){
temp = (char *)mmap(NULL, 30 * sizeof(char), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
temp = strcpy(temp, "");
buffer[i].Tuples[j] = temp;
}
sem_init(&buffer[i].buf_mutex, 1, 1);
sem_init(&buffer[i].fill_count, 1, 0);
sem_init(&buffer[i].empty_count, 1, numOfSlots);
}
for(r=0;r<numOfReducers;r++){ // loop will run n times (n=5)
if(fork() == 0){
printf("[son] pid %d from [parent] pid %d\n",getpid(),getppid());
reducer(r, buffer, done);
exit(0);
}
}
mapper();
for(r=0;r<numOfReducers;r++) // loop will run n times (n=5)
wait(NULL);
}

Named Semaphore just not working

we had an exam today and we had a task to implement a "train-handler".
There are 7 trains represented by one process each. Each train arrives after a couple of seconds, checks if 1 of our 3 traintracks is available. If not, wait...
If track is free, enter it and lock it.
Stay for at the train station for a few seconds, leave and unlock it.
Me and a few friends are trying to make our program run but we just can't get it done. It seems to be the problem that our shared memory is not synchronized properly (semaphore). Using a mac, so I have to use named semaphores.
compiled with: "gcc -Wall -Werror -std=gnu99 -lpthread process_trains.c -o test"
CODE:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <errno.h>
sem_t *sem;
int *shm_ptr;
int *initShm (int size) {
int shm_fd = 0;
if((shm_fd = shm_open("/shm", O_CREAT | O_RDWR, 0777)) == -1) {
perror("Error creating shared memory segment!");
}
if ((ftruncate(shm_fd, size)) == -1) {
perror("Error sizing shared memory segment!");
}
return (int*) mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
}
int trainAboutToArrive(int arrive, int stay, int Y){
int Z=0;
//Zug kommt in "arrive" Sekunden an
sleep(arrive);
while (shm_ptr[Z]!=0) {
Z++;
if(Z==3){
Z=0;
}
}
sem_wait(sem);
shm_ptr[Z]=1;
sem_post(sem);
printf("Zug %d ist auf Gleis %d eingefahren\n", Y, 1+Z);
//Zug hat einen Aufenthalt von "stay" Sekunden
sleep(stay);
sem_wait(sem);
shm_ptr[Z]=0;
sem_post(sem);
sem_close(sem);
printf("Zug %d verlässt Gleis %d\n", Y, 1+Z);
return EXIT_SUCCESS;
}
int main(int argc, char const *argv[]) {
shm_unlink("shm");
int i=0, tracks=3, trains=7, status;
int arrival[]={0,0,3,2,5,4,2};
int stay[]={2,3,7,2,1,4,3};
off_t size = sizeof(int)*tracks;
shm_ptr = initShm(size);
if((sem = sem_open("/semap",O_CREAT,0644,1)) == SEM_FAILED) {
perror("client sem_open");
}
for (i=0; i < tracks; i++) {
shm_ptr[i]= 0;
}
pid_t pids[trains];
for (i = 0; i < trains; i++) {
pids[i] = fork();
if(pids[i] == -1) {
perror("Error creating train-process!!");
} else if (pids[i] == 0) {
trainAboutToArrive(arrival[i], stay[i], 1+i);
exit(0);
}else if (pids[i] > 0) {
}
}
for(i=0; i < trains; i++){
waitpid(pids[i], &status, 0);
}
shm_unlink("shm");
return EXIT_SUCCESS;
}
Link with -pthread!!!! man page of all used semaphore functions tells us >.<
Thanks for everyones help!!
And for everyone who's interested, this is my code now. I improved a lot of things I didn't have time for in the exam. This runs perfectly and in my "beginners-eyes" this is not improvable by using the given functions (semaphores, shared mem...). If it is, I'd be grateful for tips & tricks ;)
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <errno.h>
int *shm_ptr;
int *initShm (off_t size) {
int shm_fd = 0;
if((shm_fd = shm_open("/shm", O_CREAT | O_RDWR, 0777)) == -1) {
perror("Error creating shared memory segment!");
}
if ((ftruncate(shm_fd, size)) == -1) {
perror("Error sizing shared memory segment!");
}
return (int*) mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
}
void initSem(sem_t **plats) {
if((plats[0] = sem_open("/one",O_CREAT,0644,1)) == SEM_FAILED) {
perror("client sem_open");
}
if((plats[1] = sem_open("/two",O_CREAT,0644,1)) == SEM_FAILED) {
perror("client sem_open");
}
if((plats[2] = sem_open("/three",O_CREAT,0644,1)) == SEM_FAILED) {
perror("client sem_open");
}
}
int trainAboutToArrive(int arrive, int stay, int train, sem_t **plats){
srand(getpid());
int platform = rand()%3;
sleep(arrive);
while (3) {
sem_wait(plats[platform]);
if(shm_ptr[platform]==0){
shm_ptr[platform]=1;
break;
}
sem_post(plats[platform]);
platform = rand() % 3;
}
printf("Train %d enters platform %d\n", train, 1+platform);
sleep(stay);
shm_ptr[platform]=0;
printf("Train %d leaves platform %d\n", train, 1+platform);
sem_post(plats[platform]);
sem_close(plats[platform]);
return EXIT_SUCCESS;
}
int main(int argc, char const *argv[]) {
shm_unlink("/shm");
sem_unlink("/one");
sem_unlink("/two");
sem_unlink("/three");
int i=0, tracks=3, trains=7, status;
int arrival[]={0,0,3,2,5,4,2};
int stay[]={2,3,7,2,1,4,3};
sem_t *plats[3];
off_t size = sizeof(int)*tracks;
shm_ptr = initShm(size);
initSem(plats);
for (i=0; i < tracks; i++) {
shm_ptr[i]= 0;
}
pid_t pids[trains];
for (i = 0; i < trains; i++) {
pids[i] = fork();
if(pids[i] == -1) {
perror("Error creating train-process!!");
} else if (pids[i] == 0) {
trainAboutToArrive(arrival[i], stay[i], 1+i, plats);
exit(0);
}else if (pids[i] > 0) {
}
}
for(i=0; i < trains; i++){
waitpid(pids[i], &status, 0);
}
shm_unlink("/shm");
sem_unlink("/one");
sem_unlink("/two");
sem_unlink("/three");
return EXIT_SUCCESS;
}

working with named pipes and semaphores in linux

I've been trying to get my program to work for several hours now and I just can't fgure out what's wrong with my code. It's about passing a variable between processess using pipes. Each process increments it M times. The program works perfectly when I use shared memory, but when I change it to using pipes it's a disaster. Creating or using named pipes doesn't seem to work at all, or I guess I'm just doing it the wrong way. Here's the source code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <memory.h>
#include <fcntl.h>
#include <sys/stat.h>
#define PIPE_NAME "MY_PIPE"
#define N 5
#define M 10
struct sembuf operations;
int semid;
key_t key;
int marker;
void semWait(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop wait\n");
exit(-1);
}
}
void semPost(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop post\n");
exit(-1);
}
}
void worker(int id) {
int j, nmarker;
int fd = open(PIPE_NAME, O_RDWR);
read(fd, &nmarker, sizeof(int));
for (j = 0 ; j < M; j++) {
semWait(semid, id);
nmarker = nmarker + 1 ;
printf("%d ", marker);
semPost(semid, N);
}
write(fd, &nmarker, sizeof(nmarker));
close(fd);
}
main() {
int i, tempPID;
int sarray[N+1] = {0};
key = 23;
marker = 0;
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1) {
perror("ERROR: semget\n");
exit(-1);
}
if ((semctl(semid, N+1, SETALL, sarray)) < 0) {
perror("ERROR: semctl - val\n");
exit(-1);
}
if(mkfifo(PIPE_NAME, S_IFIFO | 0666) < 0) {
perror("ERROR:pipe\n");
exit(-1);
}
int fd;
if( fd = open(PIPE_NAME, O_WRONLY) < 0 ){
perror("ERROR:open\n");
exit(-1);
}
write(fd, &marker, sizeof(marker));
close(fd);
for(i = 0; i < N; i++) {
tempPID = fork();
if (tempPID < 0) {
perror("ERROR: fork\n");
exit(-1);
}
else if (tempPID == 0) { // if child
worker(i);
exit(0);
}
}
for (i = 0 ; i < (M*N); i++) {
semPost(semid, i%N);
semWait(semid, N);
}
printf("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
unlinc(PIPE_NAME);
}
I create N worker processes and each one has to increment the marker value M times. I have to create a pool of 'sleeping' processes and waken them one by one using semaphores but it's all a blur so the current source code is all I came up with... :\
This is a version of the same program but with shared memory instead of pipes:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#define N 5
#define M 10
struct sembuf operations;
int semid;
key_t key;
int *sharedmem;
void semWait(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop wait\n");
exit(-1);
}
}
void semPost(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop post\n");
exit(-1);
}
}
void worker(int id) {
int j;
for (j = 0 ; j < M; j++) {
semWait(semid, id);
(*sharedmem)++;
semPost(semid, N);
}
}
main() {
int i, tempPID;
int sarray[N+1] = {0};
int protect = PROT_READ | PROT_WRITE;
int flags = MAP_SHARED | MAP_ANONYMOUS;
if ((key = ftok("/dev/null", 4343)) == -1) {
perror("ERROR: ftok\n");
exit(-1);
}
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1) {
perror("ERROR: semget\n");
exit(-1);
}
if ((semctl(semid, N+1, SETALL, sarray)) < 0) {
perror("ERROR: semctl - val\n");
exit(-1);
}
sharedmem = (int*)mmap(NULL, sizeof(int), protect, flags, 0, 0);
*(sharedmem) = 0;
for(i = 0; i < N; i++) {
tempPID = fork();
if (tempPID < 0) {
perror("ERROR: fork\n");
exit(-1);
}
else if (tempPID == 0) { // if child
worker(i);
exit(0);
}
}
for (i = 0 ; i < (M*N); i++) {
semPost(semid, i%N);
semWait(semid, N);
}
printf("Marker = %d\n", *sharedmem);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
munmap(sharedmem, sizeof(int));
}
Some of your problems are in the worker code - these two lines:
int fd = open(PIPE_NAME, O_RDWR);
read(fd, &nmarker, sizeof(int));
If you open the pipe for reading and writing, you are asking for trouble (IMNSHO). Open it for reading only, read it, close it. Then open it for writing only, write to it, close it. Now you have to consider where the semaphore operation should occur. You actually need to wake the next process before you try to open the pipe for writing, because the open for writing will block until there is a process available to read from it. Similarly, the process that opens for reading will block until there is a process available to write to it. So, the kernel will coordinate the processes.
You don't check the return value from open(), so you've no idea whether you got a valid file descriptor. Always check the return status of open().
You don't check the return value from read(), so you've no idea whether you got anything valid off the pipe. Always check the return status of read().
(You can decide to ignore the return status of write() if there is no meaningful error recovery possible for a failed write, but it is not a bad idea to check that it did work. You can decide to ignore the return status of close() for similar reasons, though you might not get to know about problems until you do the close().)
Continuing in the worker code:
for (j = 0 ; j < M; j++) {
semWait(semid, id);
nmarker = nmarker + 1 ;
printf("%d ", marker);
semPost(semid, N);
}
It is surprising to see you printing marker rather than nmarker; and surely, basic diagnostic technique prints the value of nmarker when it is read. You might or might not print j and nmarker on each iteration. Note that since nothing in this code increments marker, the value printed won't change.
The logic sequence here is interesting...it combines with the loop in main() most oddly. The parent process writes one value to the FIFO. Only one child gets to read that value - the rest get EOF immediately, or hang indefinitely (depending on whether you use O_RDONLY or O_RDWR in the children). Each child gets signalled to increment its value, does so, and then goes back to sleep until woken again. There is nothing that sends the incremented value to the next child. So each child is independently incrementing whatever value it chooses - which is probably garbage. With shared memory, if you had a pointer to the shared value, then the increments were seen by all processes at once - that's why it is called shared memory. But here there is no shared memory, so you have to communicate explicitly to get it to work. (I wonder if your FIFO plus shared memory implementation worked because the communication was via shared memory - by accident, in other words?)
So, if the child is to increment the variable it reads each time, it must both read the current value and write the new value each time around the loop. This would be an error-checked read, of course. You might be OK with O_RDWR because of the semaphores, but I'd personally be happier with the separate opens for read and write - on each iteration if need so be. But I haven't implemented this to check that it really does run into problems; it is simply aconventional to use O_RDWR on a FIFO.
After your child has incremented its value N times, it writes the result to the pipe.
write(fd, &nmarker, sizeof(nmarker));
close(fd);
The main program then does:
printf("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
unlinc(PIPE_NAME);
Since it has not modified marker, the value printed will be 0. You should be having the main process read the replies from each of the children.
The correct function for unlinking a FIFO is unlink() or remove().
Discussion
As noted in a comment, one problem was that opening the FIFO was blocking - no readers. However, that was far from the only problem.
The code below runs. I haven't verified that the number is being incremented as it should (but it is being incremented). I've not checked that every process is getting its turn. I've revised the error handling (one line per call instead of 3 or 4), and added a printing function that includes the PID in the output. I've error checked every system call (but none of the printing statements). I fixed a problem if (fd = open(...) < 0). As far as I could tell, closing the FIFO in the master process discarded the content written to it - so the parent no longer closes the FIFO immediately. But mainly I moved the read and write of the FIFO into the worker loop - leaving open and close outside. The code is also laced with diagnostic printing so I can see where it is going wrong when it is going wrong. I haven't done header minimization or any of a number of other cleanups that should occur. However, everything except main() is static so it doesn't have to be pre-declared. It compiles clean under:
/usr/bin/gcc -O3 -g -std=c99 -Wall -Wextra fifocircle.c -o fifocircle
Code
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <memory.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
static const char *arg0 = "undefined";
static void err_error(const char *fmt, ...)
{
int errnum = errno;
va_list args;
fflush(0);
fprintf(stderr, "%s: pid %d:", arg0, (int)getpid());
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)", errnum, strerror(errnum));
fputc('\n', stderr);
exit(1);
}
static void print(const char *fmt, ...)
{
va_list args;
printf("pid %d: ", (int)getpid());
va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);
fflush(0);
}
#define PIPE_NAME "MY_PIPE"
#define N 5
#define M 10
static struct sembuf operations;
static int semid;
static key_t key;
static int marker;
static void semWait(int semid, int sempos)
{
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0)
err_error("semop wait");
}
static void semPost(int semid, int sempos)
{
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0)
err_error("semop post");
}
static void worker(int id)
{
int j;
int fd = open(PIPE_NAME, O_RDWR);
if (fd < 0)
err_error("failed to open FIFO %s for read & write", PIPE_NAME);
print("Worker %d: fd %d\n", id, fd);
for (j = 0 ; j < M; j++)
{
int nmarker;
print("waiting for %d\n", id);
semWait(semid, id);
if (read(fd, &nmarker, sizeof(int)) != sizeof(int))
err_error("short read from FIFO");
print("Got %d from FIFO\n", nmarker);
nmarker = nmarker + 1 ;
if (write(fd, &nmarker, sizeof(nmarker)) != sizeof(nmarker))
err_error("short write to FIFO");
print("Wrote %d to FIFO\n", nmarker);
print("posting %d\n", id);
semPost(semid, N);
}
if (close(fd) != 0)
err_error("failed to close FIFO");
print("done\n");
}
int main(int argc, char **argv)
{
int i;
int sarray[N+1] = {0};
key = 23;
marker = 0;
arg0 = argv[0];
if (argc != 1)
err_error("Usage: %s\n", arg0);
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1)
err_error("semget");
if ((semctl(semid, N+1, SETALL, sarray)) < 0)
{
perror("ERROR: semctl - val\n");
exit(-1);
}
if (mkfifo(PIPE_NAME, S_IFIFO | 0666) < 0)
err_error("failed to create FIFO %s\n", PIPE_NAME);
print("FIFO created\n");
int fd;
if ((fd = open(PIPE_NAME, O_RDWR)) < 0 )
err_error("failed to open FIFO %s\n", PIPE_NAME);
print("FIFO opened\n");
if (write(fd, &marker, sizeof(marker)) != sizeof(marker))
err_error("short write to FIFO");
print("FIFO loaded\n");
print("Master: about to fork\n");
for (i = 0; i < N; i++)
{
pid_t pid = fork();
if (pid < 0)
err_error("failed to fork");
else if (pid == 0)
{
worker(i);
exit(0);
}
}
print("Master: about to loop\n");
for (i = 0 ; i < (M*N); i++)
{
print("posting to %d\n", i%N);
semPost(semid, i%N);
print("waiting for %d\n", N);
semWait(semid, N);
}
if (close(fd) != 0)
err_error("failed to close FIFO");
print("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1)
err_error("semctl remove");
if (unlink(PIPE_NAME) != 0)
err_error("failed to remove FIFO %s", PIPE_NAME);
return(0);
}

Resources