Semaphore in C - How can I make it alternate? - c

//I think the final code now in this post, should have fixed all the issues I've had. Thank you everyone!
I have written code in C that should lock and unlock shared memory via semaphores and write one Byte into. I would want the written data to alternate between "message" and "read".
Maybe I am misunderstanding semaphores and how their lock functionality works?
I added the while(1) to only to have to loop in one process with the other contiously running, only doing something when the if-condition applies.
I seem to end up in nirvana however, as the program does not complete.
I'd appreciate any help!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <semaphore.h>
void latenz_Ausgabe(time_t tv_secStartLatenz, time_t tv_secEndLatenz, long tv_nsecStartLatenz, long tv_nsecEndLatenz)
{
struct timespec startLatenz = {tv_secStartLatenz, tv_nsecStartLatenz};
struct timespec endLatenz = {tv_secEndLatenz, tv_nsecEndLatenz};
double nanoSekunden = difftime(endLatenz.tv_nsec, startLatenz.tv_nsec);
double sekunden = difftime(endLatenz.tv_sec, startLatenz.tv_sec);
double differenzZeit = (sekunden + nanoSekunden / ((double) 1e9)) / 1000.0; //Zeit berechnen in Sekunden, sowie auf eine Messung runterrechnen
double latenz = (differenzZeit * ((double) 1e3)) / 2; //1e3 für Millisekunden, / 2 Da hin und zurück gemessen
printf("Durchschnittliche Latenz: %.6lf milliseconds\n", latenz);
}
int main() {
struct timespec startLatenz, endLatenz;
int sem_id;
int sem_idtwo;
int shm_id;
int value;
key_t key = ftok("shared_memory", 1); // Create key shared memory
key_t keytwo = ftok("semaphore", 1); // Create key semaphore
key_t keythree = ftok("semaphoretwo", 1); // Create key semaphore
if((shm_id = shmget(key, 16777216, IPC_CREAT | 0666)) == -1){ // -1 indicates error //create shared memory
perror("semctl");
exit(1);
} else {
printf("Shared memory created with id: %d \n", shm_id);
}
if((sem_id = semget(keytwo, 1, IPC_CREAT | 0666)) == -1){ // -1 indicates error // create semaphore
perror("semctl");
exit(1);
} else {
printf("Semaphore created with id: %d \n", shm_id);
}
if((sem_idtwo = semget(keythree, 1, IPC_CREAT | 0666)) == -1){ // -1 indicates error // create semaphore
perror("semctl");
exit(1);
} else {
printf("Semaphore created with id: %d \n", shm_id);
}
struct sembuf sem_lock = {0, -1, 0}; // Struct for semop to lock
struct sembuf sem_unlock = {0, 1, 0}; // Struct for semop to unlock
char *data = (char *)shmat(shm_id, NULL, 0); // Connect Shared-Memorys to Process
pid_t pid;
if((semctl(sem_id, 0, SETVAL, 1)) == -1){ // -1 indicates error //initialize the semaphore with value 1
perror("semctl");
exit(1);
} else {
printf("Semaphore initialized \n");
}
if((semctl(sem_idtwo, 0, SETVAL, 1)) == -1){ // -1 indicates error //initialize the semaphore with value 1
perror("semctl");
exit(1);
} else {
printf("Semaphore initialized \n");
}
pid = fork();
if(pid < 0){
printf("Error");
return 1;
}
//malloc allocates exactly one Byte
char *message = (char *) malloc(1);
char *read = (char *) malloc(1);
message[0] = 'a';
read[0] = 'b';
clock_gettime(CLOCK_MONOTONIC, &startLatenz);
if (pid == 0) {
// Process 2:
while(1){
semop(sem_idtwo, &sem_lock, 1);
if (data[0] == message[0]){
memcpy(data, read, 1);
}
semop(sem_idtwo, &sem_unlock, 1);
}
}
else {
// Process 1:
for(int i = 0; i < 1000; i++){
//1
if (i > 0){ //because data[0] is never equal to read[0] during first loop
semop(sem_id, &sem_lock, 1);
//char dummy = data[0]; // Could save current value before overwrite
if (data[0] == read[0]){
memcpy(data, message, 1);
}
semop(sem_id, &sem_unlock, 1);
//printf("Values: %c %c \n", dummy, data[0]); //Could print
}
else{ //because data[0] is never equal to read[0] during first loop
semop(sem_id, &sem_lock, 1);
//char dummy = data[0]; // Could save current value before overwrite
memcpy(data, message, 1);
semop(sem_id, &sem_unlock, 1);
}
}
}
clock_gettime(CLOCK_MONOTONIC, &endLatenz);
latenz_Ausgabe(startLatenz.tv_sec, endLatenz.tv_sec, startLatenz.tv_nsec, endLatenz.tv_nsec);
shmdt(data); // Remove shared-memory from process
shmctl(shm_id, IPC_RMID, NULL); // Delete shared memory
semctl(sem_id, 0, IPC_RMID); // Delete semaphore
return 0;
}
I tried doing it with two locks, I tried with three operations, I tried different loops (inside/outside of the child/parent process)

As far as I understand, you want to see an output that sometimes prints a and sometimes prints b. The printing is done here:
semop(sem_id, &sem_lock, 1);
memcpy(data, message, 1);
semop(sem_id, &sem_unlock, 1);
printf("%c \n", data[0]); <----------
So you have just copied a into data and then you do the printing. So it's not extremely surprising that your print out will mostly show a
I changed the code a little to use while(1) for both loops. When running the program and looking at the terminal, it seemed that a was printed all the time.
Then I send the output to grep and grep'ed for b, i.e "./a.out | grep b" and now I do see b being printed.
My conclusion: Since you do the print just after setting data to a, you will print an a in most cases. Further, if you only look at the terminal, you most likely won't be able to notice the few prints of b.
Instead try:
semop(sem_id, &sem_lock, 1);
char dummy = data[0]; // Save current value before overwrite
memcpy(data, message, 1);
semop(sem_id, &sem_unlock, 1);
printf("%c %c \n", dummy, data[0]);
then maybe you'll see a different result.
But... you don't have any code ensuring an alternating access. Alternating access don't come by itself. You need code to ensure alternating write access. For instance you could add code so that writes to data is only done when data contains the value written by the other process.
Something like:
if (data[0] == message[0]) memcpy(data, read, 1);
and
if (data[0] == read[0]) memcpy(data, message, 1);

Related

Why my program stops running after detaching from shared memory when i added som code

I have a program that creates two processes. The first one gets a command-line argument provided by the user and sends it via shared memory to the second process. The second process has to interpret the argument as a file name and check the permissions of the designated file. But after passing the directory to second process my program hangs. What's wrong with it?
#include <unistd.h> // fork
#include <stdio.h> // printf
#include <sys/ipc.h> // ftok
#include <sys/sem.h> // operacje na semaforach
#include <stdlib.h> // exit
#include <err.h> // errx
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHM_SIZE 1024
/* plik, w ktorym bedziemy zapisywali dane. */
#define buffer "test"
/* unia dla semafora */
union semun {
int val; // wartosc dla SETVAL
struct semid_ds *buf; // bufor dla IPC_STAT, IPC_SET
unsigned short int *array; // tablica dla GETALL, SETALL
struct seminfo *__buf; // bufor dla IPC_INFO
};
/* blokowanie semafora */
int semlock(int semid) {
/* Struktura opisujaca operacje na semaforach */
struct sembuf opr;
/* podgld struktury z opisem pol:
struct sembuf {
ushort semnum;
short sem_op;
ushort sem_flg;
};
*
* gdzie:
* semnum - numer semafora,
* sem_op - operacja na semaforze:
* sem_op > 0 - (V) zwiekszenie semafora o wartosc "sem_op"
* sem_op < 0 - (P) polozenie semafora (wstrzymanie procesu)
* lub zmniejszenie semafora o wartosc "sem_op"
* sem_op = 0 - (Z) "przejscie pod semaforem", odwrotnosc (P)
*/
opr.sem_num = 0;
opr.sem_op = -1; // blokowanie
opr.sem_flg = 0; // operacja blokujaca
if (semop(semid, &opr, 1) == -1){
warn("Blad blokowania semafora!");
return 0;
}else{
return 1;
}
}
/* odblokowanie semafora */
int semunlock(int semid) {
/* Struktura opisujaca operacje na semaforach */
struct sembuf opr;
opr.sem_num = 0;
opr.sem_op = 1;
opr.sem_flg = 0;
if (semop(semid, &opr, 1) == -1){
warn("Blad odblokowania semafora!");
return 0;
}else{
return 1;
}
}
int main(int argc, char** argv){
/*----------------------------------------------------------------*/
key_t key1, key2; // klucz dla semaforow
int semid1, semid2; // ID semaforow
union semun ctl; // unia do kontroli semafora
/*----------------------------------------------------------------*/
/*****************************************************************/
/* Tworzymy klucz dla semafora I */
if ((key1 = ftok(".", 'A')) == -1)
errx(1, "Blad tworzenia klucza!");
/* Pobieramy ID semafora. Jesli semafor nie istnieje - tworzymy. */
/* semget(klucz, liczba semaforow, flagi/parametry) */
if ((semid1 = semget(key1, 1, IPC_CREAT | 0600)) == -1)
errx(2, "Blad tworzenia semafora!");
/* Inicjujemy semafor I jako podniesiony (S = 1). */
ctl.val = 1;
if (semctl(semid1, 0, SETVAL, ctl) == -1)
errx(3, "Blad ustawiania semafora!");
/*****************************************************************/
/* Tworzymy klucz dla semafora II */
if ((key2 = ftok(".", 'B')) == -1)
errx(1, "Blad tworzenia klucza!");
/* Pobieramy ID semafora. Jesli semafor nie istnieje - tworzymy. */
/* semget(klucz, liczba semaforow, flagi/parametry) */
if ((semid2 = semget(key2, 1, IPC_CREAT | 0600)) == -1)
errx(2, "Blad tworzenia semafora!");
/* Inicjujemy semafor II jako podniesiony (S = 1). */
ctl.val = 1;
if (semctl(semid2, 0, SETVAL, ctl) == -1)
errx(3, "Blad ustawiania semafora!");
/*****************************************************************/
/* Blokujemy semafor procesu, ktory nie powinien wystartowac pierwszy */
semlock(semid2);
if(fork()){ //proces 1
/* proces blokuje swoj semafor */
semlock(semid1);
// ftok to generate unique key
key_t key = ftok("shmfile",65);
// shmget returns an identifier in shmid
int shmid = shmget(key,1024,0666|IPC_CREAT);
// shmat to attach to shared memory
char *str = (char*) shmat(shmid,(void*)0,0);
sleep(1);
strcpy(str,argv[1]);
printf("Data written in memory: %s\n",str);
//detach from shared memory
shmdt(str);
sleep(1);
/* Odblokowujemy semafor drugiemu procesowi */
semunlock(semid2);
}else{ //proces 2
semlock(semid2); //proces blokuje swoj semafor -
// ftok to generate unique key
key_t key = ftok("shmfile",65);
// shmget returns an identifier in shmid
int shmid = shmget(key,1024,0666|IPC_CREAT);
// shmat to attach to shared memory
char *str = (char*) shmat(shmid,(void*)0,0);
char *sciezka;
printf("Data read from memory: %s\n",str);
strcpy(sciezka,str);
//detach from shared memory
shmdt(str);
// destroy the shared memory
semunlock(semid2);
printf("Data : %s\n",sciezka);
FILE *fptr;
struct stat info;
if ( !( fptr = fopen( sciezka, "r" ))){
printf( "Plik nie zostal otwarty poniewaz nie istnieje\n" );
}
else {
fseek(fptr, 0, SEEK_END);
unsigned long len = (unsigned long)ftell(fptr);
if (len > 0) { //sprawdza czy plik jest pusty czy nie
printf("Podany plik nie jest pusty.\n");
}
else
{
printf( "Podany plik jest pusty.\n" );
}
stat(sciezka, &info);
uid_t uid = info.st_uid;
mode_t tryb = info.st_mode;
if (tryb == S_IRWXU)
{
printf("Podany plik mozna modyfikowac i uruchomic\n");
}
else
printf( "Podanego pliku nie mozna modyfikowac lub uruchomic\n" );
printf( "ID wlasciciela : %d \n", uid );
fclose( fptr );
return 0;
}
/* Odblokowujemy semafor pierwszemu procesowi */
semunlock(semid1);
}
/* UWAGA! Procesy sa w nieskonczonej petli, wiec nie
* usuwamy semaforow. Normalnie trzeba to jednak robic.
reczne usuwanie semaforow - ipcrm -s ID_SEMAFORA*/
semctl(semid1, 0, IPC_RMID, ctl);
semctl(semid2, 0, IPC_RMID, ctl);
return 0;
}
Although they are mostly reasonable, I see these issues with your semaphore and shared memory usage:
you do not remove the shared memory segment when you have finished with it.
the initial process may remove one or both semaphores before the child process is done using them. Removal of System V semaphores is immediate, and this will cause concurrent and subsequent attempts at using those semaphores to fail. I suspect that avoiding this is the intended purpose of the first semaphore, but to achieve that, the main process has to block on that semaphore before the removals, and the child has to unlock it before terminating.
Personally, I would also initialize both semaphores with value 0 instead of initializing them with value 1 and then decrementing them.
Additionally, you have an issue here:
char *sciezka;
printf("Data read from memory: %s\n",str);
strcpy(sciezka,str);
You are copying the data from the shared memory segment to the address specified by uninitialized pointer sciezka. Undefined behavior results.
A pretty easy solution would be to use strdup() instead:
sciezka = strdup(str);
Do be aware that this allocates memory for the duplicate, which the caller has responsibility to free when they no longer need it.
It is also a bit odd that you attempt to open the specified file before stat()ing it. If you don't intend to read the file's contents then the stat function gives you more information about the file than you can get from opening it (or attempting to do so). That including the file's length, which you instead compute via a different (valid) means.

How to block and open Clients with Semaphore? Semaphore problem

I have a multiclient Server Socket and I want to block the acces for other Clients, when sending "BEG" to the Server. To open the other Clients again, the Client has to send "END" to the Server. While other Clients are blocked off, they only can use "quit" to exit the Server and if they use conditions() they fall asleep.
So other Clients are blocked for the function conditions() if one process used "BEG", but the process himself has still acces to the function.
If I compile my Code, the Server is running, everything is fine but the Mutexe doesn't work.
The Code is going into the if statement of "BEG" and the Mutex should be locked, but other Clients aren't blocked off.
If I connect a second Client, the Client gets kicked if I use conditions().
My question is, why does the mutex not work for other Clients or in generell? How to check if the Mutex is working?
Edit:
Now my Semaphore doesn't block other processes
Edit 2: I found a way, not the best but one. Now some clients are getting kicked from the Server after using one condition().
main.c:
int state = 0;
int beg() {
state = 1;
return 0;
}
int end() {
state = 0;
return 0;
}
int main() {
int pid, t;
char *eingabe, *inputBuffer[BUFSIZE];
char delimiter[] = "\n ";
int rfd = erstelleSocket();
int cfd;
semaphor semID1 = semGET();
semaphor semID2 = semGET2();
marker[0] = 1;
t = semctl(semID1, 1, SETALL, marker);
if (t == -1) {
fprintf(stderr, "Error with marker\n");
}
t = semctl(semID2, 1, SETALL, marker);
if (t == -1) {
fprintf(stderr, "Error with marker\n");
}
while(1){
cfd = accept(rfd, (struct sockaddr *) &client, &client_len);
if (cfd < 0) {
close(cfd);
fprintf(stderr, "connection failed\n");
break;
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "Error in new process creation\n");
}
if (pid == 0) {
bzero(input, sizeof(input));
bytes_read = read(cfd, input, BUFSIZE);
strncat(input, " ", strlen(" "));
input[strcspn(input, "\r\n")] = 0;
while (bytes_read > 0) {
eingabe = strtok(input, delimiter);
int i = 0;
while (eingabe != NULL) {
inputBuffer[i++] = eingabe;
eingabe = strtok(NULL, delimiter);
}
if (strncmp("quit", inputBuffer[0], 4) == 0) {
close(cfd);
break;
}
if (state != 1) {
down(semID2, 0); //down is a function with semop()
}
down(semID1, 0);
conditions(inputBuffer[0],
inputBuffer[1],
inputBuffer[2],
cfd, semID1, shmID);
up(semID1, 0);
if (state != 1) {
up(semID2, 0); //up is a function with semop()
}
bzero(input, sizeof(input));
bytes_read = read(cfd, input, BUFSIZE);
strncat(input, " ", strlen(" "));
input[strcspn(input, "\r\n")] = 0;
close(rfd);
}
}
close(cfd);
}
close(rfd);
}
my condition function:
void conditions(char *eingabehalter1,
char *eingabehalter2,
char *eingabehalter3,
int cfd, int shmID) {
if (strncmp("PUT", eingabehalter1, 3) == 0) {
put(eingabehalter2, eingabehalter3, cfd, shmID);
} else if (strncmp("GET", eingabehalter1, 3) == 0) {
get(eingabehalter2, cfd, shmID);
} else if (strncmp("DEL", eingabehalter1, 3) == 0) {
del(eingabehalter2, cfd, shmID);
} else if (strncmp("BEG", eingabehalter1, 3) == 0) {
beg();
} else if (strncmp("END", eingabehalter1, 3) == 0) {
end();
} else {
write(cfd, "cmd_nonexistent\n", strlen("cmd_nonexistent\n"));
}
}
createSocket.c:
int rfd; // Rendevouz-Descriptor
rfd = socket(AF_INET, SOCK_STREAM, 0);
int option = 1;
setsockopt(rfd,SOL_SOCKET, SO_REUSEADDR, (const void *) &option, sizeof(int));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(PORT);
int brt = bind(rfd, (struct sockaddr *) &server, sizeof(server));
int lrt = listen(rfd, 5);
return rfd;
}
main.h:
#include "shmmemory.h"
#include "semaphoren.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <netinet/in.h>
#define PORT 5678
struct sockaddr_in server;
struct sockaddr_in client;
socklen_t client_len;
char input[BUFSIZE];
int bytes_read;
int erstelleSocket();
void conditions(char *eingabehalter1,
char *eingabehalter2,
char *eingabehalter3,
int cfd, int shmID);
int beg();
int end();
unsigned short marker[2];
Your approach cannot work because you're trying to combine fork with threads. fork creates a copy of the parent's address space for each child process, which means that each child process has its own copy of the mutex object. Process-shared mutexes are possible in POSIX, with special attributes, but I suspect even those don't work with fork; they have to be placed in shared memory.
Have you considered creating threads with pthread_create for the service loop? Or else you can implement this entirely using fork (no pthread material). The children can use POSIX named semaphores (sem_open, et al) or possibly, dare I say it, System V IPC.
Also, don't use strtok in multithreaded code, and clearing memory to zero was standardized in 1989's ANSI C as memset(pointer, 0, size). Since that was 31 years ago, it's okay to lay bzero to rest.
The way you initialize the semaphores is wrong for your use case. From the man page of sem_init():
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
If pshared has the value 0, then the semaphore is shared between the
threads of a process, and should be located at some address that is
visible to all threads (e.g., a global variable, or a variable
allocated dynamically on the heap).
If pshared is nonzero, then the semaphore is shared between
processes, and should be located in a region of shared memory
Based on the above explanations from the man page, the things you need change are:
Semaphore declaration
Since you are using semaphores between processes, you need to declare the variable as shared. You can do that via mmap() to create unnamed UNIX semaphore as follows:
sem_t* sem_var = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0)
if (sem_var == MAP_FAILED) // Shared memory creation failed.
goto handle_shm_fail;
Semaphore initialization
Since you are using POSIX semaphores, to make your child use the same semaphores, pshared is set to 1 indicating semaphore is shared between processes.
if (sem_init(sem_var, 1, 1) != 0) // Semaphore initialization failed.
goto handle_sem_fail;
NOTE: In your code sem_var is of type sem_t, now it is a pointer to sem_t. Accordingly, you need to update your code.

Sharing text file between processes with open() and mmap()

I'm trying to share a text file between forked processes on my Ubuntu x86_64: the file will not be absurdly large, since strings will be written only if there is not already another identical string in the file; strings will be hostnames of visited websites, so I'll assume no more than 255 bytes for each hostname.
When it is a process' turn to write in shared object, it is OK; once all the processes wrote in shared object, msync should make the writing effective on the disk, but the mapped.txt file created only contain one string from arrayString, i.e. the string the last process wrote in shared object.
Here's the code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#include <string.h>
// first forked process will write "first" in file, and so on
const char *arrayString[] = {
"first",
"second",
"third"
};
int main(void) {
int index;
int children = 3;
const char *filepath = "mapped.txt";
sem_t *sem;
sem = sem_open("semaphore", O_CREAT | O_EXCL, 0644, 1);
sem_unlink("semaphore");
int fd;
fd = open(filepath, O_RDWR | O_CREAT, 0644);
if (fd < 0) {
perror("open:");
return EXIT_FAILURE;
}
char *data;
data = (char *)mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
perror("mmap:");
return EXIT_FAILURE;
}
for (index=0; index<children; index++) {
if (fork() == 0) {
sem_wait(sem);
size_t textsize = strlen(arrayString[index])+1;
if (ftruncate(fd, sizeof(textsize)) == -1) {
perror("ftruncate:");
return EXIT_FAILURE;
}
for (size_t i = 0; i < textsize; i++) {
printf("%d Writing character %c at %zu\n", getpid(), arrayString[index][i], i);
data[i] = arrayString[index][i];
}
printf("%d wrote ", getpid());
for (size_t i = 0; i < textsize; i++) {
printf("%c", data[i]);
}
printf("\n");
if (msync(data, textsize, MS_SYNC) == -1) {
perror("Could not sync the file to disk");
}
sem_post(sem);
_exit(EXIT_SUCCESS);
}
}
close(fd);
return EXIT_SUCCESS;
}
This is one possible output of the code above for three child processes (this is fine):
20373 Writing character s at 0
20373 Writing character e at 1
20373 Writing character c at 2
20373 Writing character o at 3
20373 Writing character n at 4
20373 Writing character d at 5
20373 Writing character at 6
20373 wrote second
20374 Writing character t at 0
20374 Writing character h at 1
20374 Writing character i at 2
20374 Writing character r at 3
20374 Writing character d at 4
20374 Writing character at 5
20374 wrote third
20372 Writing character f at 0
20372 Writing character i at 1
20372 Writing character r at 2
20372 Writing character s at 3
20372 Writing character t at 4
20372 Writing character at 5
20372 wrote first
And here's the content of mapped.txt (this is bad):
first^#^#^#
I expected:
second
third
first
but all I get is only the string of the last process, with those strange symbols. I'd like to keep this file persistent in memory, but because of the I/O slowness, I'm trying to use memory mapping.
Any idea why my file only contains the string written by the last process accessing the shared file?
Edit: I think I get it, it seems to work now: I hope it will be of help to someone. Compiled with g++ -g -o mapthis mapthis.cpp -lrt -pthread. Beware that some error checking are missing, like for fsync, snprintf and lseek.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
const char *arrayString[] = {
"www.facebook.com",
"www.google.com",
"www.cnn.com",
"www.speechrepository.com",
"www.youtube.com",
"www.facebook.com",
"www.google.com",
"www.cnn.com",
"www.speechrepository.com",
"www.youtube.com",
"www.facebook.com",
"www.google.com",
"www.cnn.com",
"www.speechrepository.com",
"www.youtube.com"
};
int main(void) {
int index;
int children = sizeof(arrayString) / sizeof(char*);;
const char *filepath = "mapped.txt";
sem_t *sem;
char *data;
struct stat filestats;
sem = sem_open("semaphore", O_CREAT | O_EXCL, 0644, 1);
sem_unlink("semaphore");
int fd;
fd = open(filepath, O_RDWR | O_CREAT, 0644);
if (fd < 0) {
perror("open:");
return EXIT_FAILURE;
}
if (fstat(fd, &filestats) < 0) {
close(fd);
perror("fstat:");
return EXIT_FAILURE;
}
data = (char *)mmap(NULL, filestats.st_size ? filestats.st_size : 1, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
perror("first map:");
return EXIT_FAILURE;
}
for (index=0; index<children; index++) {
sleep(1);
pid_t pid = fork();
if (pid == 0) {
int nw = 0;
int hostnameSize = 0;
const size_t origsize = filestats.st_size;
char *hostPos = NULL;
char *numPos = NULL;
char *backslashPos = NULL;
char tempBuff[64];
memset((char *)tempBuff, 0, sizeof(tempBuff));
sem_wait(sem);
// remap to current file size if it changed
fstat(fd, &filestats);
// file empty, just insert
if (filestats.st_size == 0) {
nw = snprintf(tempBuff, sizeof(tempBuff), "%s %010lu\n", arrayString[index], (unsigned long)time(NULL));
write(fd, tempBuff, nw);
fsync(fd);
}
else {
// file not empty, let's look for string
hostPos = strstr(data, arrayString[index]);
if (hostPos) {
// string is already inserted, search for offset of number of seconds
lseek(fd, hostPos-data, SEEK_SET);
numPos = strchr(hostPos, ' ')+1;
backslashPos = strchr(numPos, '\n');
long unsigned before = atoi(numPos);
long unsigned now = (unsigned long)time(NULL);
long unsigned difference = now - before;
printf("%s visited %ld seconds ago (%ld - %ld)\n",
arrayString[index], difference, now, before);
nw = snprintf(tempBuff, backslashPos-hostPos+1, "%s %010lu", arrayString[index], now);
write(fd, tempBuff, nw);
write(fd, "\n", 1);
fsync(fd);
}
else {
data = (char *)mremap(data, origsize, filestats.st_size, MREMAP_MAYMOVE);
if (data == MAP_FAILED) {
close(fd);
sem_post(sem);
perror("mmap:");
_exit(EXIT_FAILURE);
}
lseek(fd, 0, SEEK_END);
nw = snprintf(tempBuff, sizeof(tempBuff), "%s %010lu\n", arrayString[index], (unsigned long)time(NULL));
write(fd, tempBuff, nw);
fsync(fd);
}
}
munmap(data, filestats.st_size);
close(fd);
sem_post(sem);
_exit(EXIT_SUCCESS);
}
else if (pid > 0) {
wait(NULL);
}
}
munmap(data, filestats.st_size);
close(fd);
return EXIT_SUCCESS;
}
This line is problematic:
if (ftruncate(fd, sizeof(textsize)) == -1) {
textsize is a size_t, and taking its sizeof is just going to get 4 or 8 (on 32 and 64 bit systems). Looks like you're on a 64 bit system, so you're unconditionally truncating the file to 8 bytes in this case before every write. The "strange symbols" are just how your editor displays NUL/zero bytes. Even if you used ftruncate(fd, textsize), you'd still truncate down to just the string you're about to write, overwriting any data other children may have written; I doubt you want to ftruncate at all here.
For continual appends from separate processes (where they can't share information about the size or offset of the data they're adding), memory mapping just doesn't make sense; why aren't you just having each of them take the lock, lseek to end of file, then call write? You could still use memory mappings for the duplicate checking (some of it without locking), it would just be a bit different. Something like this:
int main(void) {
struct stat filestats;
int index;
int children = 3;
const char *filepath = "mapped.txt";
sem_t *sem;
char *data;
sem = sem_open("semaphore", O_CREAT | O_EXCL, 0644, 1);
sem_unlink("semaphore");
int fd;
fd = open(filepath, O_RDWR | O_CREAT, 0644);
if (fd < 0) {
perror("open:");
return EXIT_FAILURE;
}
// Mostly just to ensure it's mappable, we map the current size of the file
// If the file might already have values, and many child workers won't add
// to it, this might save some mapping work in the children; you could
// just map in the children when needed though
if (fstat(fd, &filestats) != 0) {
close(fd);
perror("fstat:");
return EXIT_FAILURE;
}
data = mmap(NULL, filestats.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
perror("mmap:");
return EXIT_FAILURE;
}
for (index=0; index<children; index++) {
if (fork() == 0) {
const size_t origsize = filestats.st_size;
sem_wait(sem);
// remap to current file size if it changed
// If you're not on Linux, you'd just have to mmap from scratch
// since mremap isn't standard
fstat(fd, &filestats);
if (origsize != filestats.st_size) {
data = mremap(data, origsize, filestats.st_size, MREMAP_MAYMOVE);
if (data == MAP_FAILED) {
close(fd);
sem_post(sem);
perror("mmap:");
_exit(EXIT_FAILURE);
}
}
// Not safe to use strstr since mapping might not end with NUL byte
// You'd need to workaround this, or implement a your own memstr-like function
if (!memstr(data, arrayString[index])) {
// Move fd to end of file, so we append new data
lseek(fd, 0, SEEK_END);
write(fd, arrayString[index], strlen(arrayString[index]));
write(fd, "\n", 1);
fsync(fd);
}
munmap(data, filestats.st_size);
close(fd);
sem_post(sem);
_exit(EXIT_SUCCESS);
}
}
munmap(data, filestats.st_size);
close(fd);
return EXIT_SUCCESS;
}
That memstr I referenced would need to be hand-implemented (or you'd need to do terrible things like ensure the file always had a NUL byte at the end so you could use strstr on it); you can get some tips on that here.
You're writing all the strings at offset 0 of the file, each over the top of the previous. The core of your loop should be something like
struct stat status;
fstat(fd, &status);
size_t cursize = status.st_size;
ftruncate(fd, cursize + textsize);
for (size_t i = 0; i < textsize; i++) {
data[cursize + i] = arrayString[index][i];
}

Copying Data from Shared memory segment causes seg fault in the client ( semaphores & shared memory)

I am trying to write a simple producer and consumer program( two unrelated processes). , with shared memory & semaphores. I use the semaphores empty & full as conditional variables, and I memcpy the data into the shared memory segment from the producer. And, I try to memcpy the data into a local variable in the consumer, but thats resulting in a seg fault. It is very strange, and I couldnt figure out whats happening. Here is the code.
Common part for the Producer & consumer ( semaphore & shared memorycreation) :
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#include<stdlib.h>
#include <sys/shm.h>
struct a
{
int a;
int b;
}a_s;
void wait(int semid)
{
int err,nsops=1;
struct sembuf *sops = (struct sembuf *) malloc(sizeof(struct sembuf));
sops[0].sem_num = 0;
sops[0].sem_op = -1;
sops[0].sem_flg = 0;
err=semop(semid, sops, nsops);
if(err < 0)
printf(" unable to do the sop \n");
}
void signal(int semid)
{
int err,nsops=1;
struct sembuf *sops = (struct sembuf *) malloc(sizeof(struct sembuf));
sops[0].sem_num = 0;
sops[0].sem_op = 1;
sops[0].sem_flg = 0;
err=semop(semid, sops, nsops);
if(err < 0)
printf(" unable to do the sop \n");
}
int main()
{
int i, err;
int full,empty;
key_t full_key = 1234, empty_key = 5678;
int sem_flg = IPC_CREAT | 0666;
int nsems = 1;
int nsops = 2;
int shmid;
void *string;
void *s;
int shm_key = 9999;
struct a *a_str;
/*****************************************/
empty = semget(empty_key, nsems, sem_flg);
if(empty < 0)
printf(" failed to initialize the semaphore \n");
semctl(empty, 0, SETVAL, 1) ;
/****************************************/
full = semget(full_key, nsems, sem_flg);
if(full < 0)
printf(" failed to initialize the semaphore \n");
semctl(full, 0, SETVAL, 0) ;
/*****************************************/
shmid = shmget(shm_key, 30, IPC_CREAT|0666);
if(shmid < 0)
printf(" unable to create shmem \n");
else
printf(" created shm \n");
string = shmat( shmid, NULL, 0);
if( string == (void * ) (-1))
printf(" unable to attach the string \n");
else
printf(" success with shmat \n");
s = string;
/******************************************/
Producer : inputting the data
while(1)
{
wait(empty);
sleep(1);
memcpy( string, (void *) a_str, sizeof(struct a));
printf(" wrote the string \n");
signal(full);
}
Consumer : copying the data, and displaying
while(1)
{
wait(full);
printf(" after full \n");
memcpy((void *)a_str, (void *)s, sizeof(struct a));
printf(" copied the memory from string \n");
printf(" a %d b %d \n",((struct a *)a_str)->a, ((struct a *)a_str)->b);
sleep(1);
memcpy(s, string, 7);
signal(empty);
}
return 0;
}
Could anybody please let me know why its seg faulting?? I am just copying from a memory segment, with the address. what could possibly go wrong?
Could anybody please let me know why its seg faulting??
You did not initialize a_str, this could be fixed by
a_str = malloc(sizeof(*a_str));
The typical using-uninitialized-pointer, a.k.a. wild pointer, problem.
By the way, POSIX IPC API is better than System V IPC API. See
mq_overview (7) - overview of POSIX message queues
sem_overview (7) - overview of POSIX semaphores
shm_overview (7) - overview of POSIX shared memory

POSIX sem_wait() SIGABRT

I am working on a school project where we have to make a multithreaded web server. I am having a problem where when I call sem_wait on my semaphore (which should be initialized to 0 but already seems to be sem_post()ed to 1). I get a SIGABRT.
I am attaching my code below, and I put a comment on the line that is causing my problem. I've spent a few hours with the debugger with little luck.
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string>
#include <string.h>
#include <iostream>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <vector>
#include <semaphore.h>
#include <stdio.h>
#include <cstdlib>
#include <strings.h>
#define PORTNUM 7000
#define NUM_OF_THREADS 5
#define oops(msg) { perror(msg); exit(1);}
#define FCFS 0
#define SJF 1;
void bindAndListen();
void acceptConnection(int socket_file_descriptor);
void* dispatchJobs(void*);
void* replyToClient(void* pos);
//holds ids of worker threads
pthread_t threads[NUM_OF_THREADS];
//mutex variable for sleep_signal_cond
pthread_mutex_t sleep_signal_mutex[NUM_OF_THREADS];
//holds the condition variables to signal when the thread should be unblocked
pthread_cond_t sleep_signal_cond[NUM_OF_THREADS];
//mutex for accessing sleeping_thread_list
pthread_mutex_t sleeping_threads_mutex = PTHREAD_MUTEX_INITIALIZER;
//list of which threads are sleeping so they can be signaled and given a job
std::vector<bool> *sleeping_threads_list = new std::vector<bool>();
//number of threads ready for jobs
sem_t available_threads;
sem_t waiting_jobs;
//holds requests waiting to be given to one of the threads for execution
//request implemented as int[3] with int[0]== socket_descriptor int[1]== file_size int[2]== file_descriptor of requested file
//if file_size == 0 then HEAD request
std::vector<std::vector<int> >* jobs = new std::vector<std::vector<int> >();
pthread_mutex_t jobs_mutex = PTHREAD_MUTEX_INITIALIZER;
int main (int argc, char * const argv[]) {
//holds id for thread responsible for removing jobs from ready queue and assigning them to worker thread
pthread_t dispatcher_thread;
//initializes semaphores
if(sem_init(&available_threads, 0, NUM_OF_THREADS) != 0){
oops("Error Initializing Semaphore");
}
if(sem_init(&waiting_jobs, 0, 0) !=0){
oops("Error Initializing Semaphore");
}
//initializes condition variables and guarding mutexes
for(int i=0; i<NUM_OF_THREADS; i++){
pthread_cond_init(&sleep_signal_cond[i], NULL);
pthread_mutex_init(&sleep_signal_mutex[i], NULL);
}
if(pthread_create(&dispatcher_thread, NULL, dispatchJobs, (void*)NULL) !=0){
oops("Error Creating Distributer Thread");
}
for (int i=0; i<NUM_OF_THREADS; i++) {
pthread_mutex_lock(&sleeping_threads_mutex);
printf("before");
sleeping_threads_list->push_back(true);
printf("after");
pthread_mutex_unlock(&sleeping_threads_mutex);
}
printf("here");
for (int i=0; i<NUM_OF_THREADS; i++) {
//creates threads and stores ID in threads
if(pthread_create(&threads[i], NULL, replyToClient, (void*)i) !=0){
oops("Error Creating Thread");
}
}
/*
if(sem_init(&available_threads, 0, NUM_OF_THREADS) !=0){
oops("Error Initializing Semaphore");
}
if(sem_init(&waiting_jobs, 0, 0) !=0){ //this is the semaphore thats used in the sem_wait
oops("Error Initializing Semaphore");
}*/
bindAndListen();
}
//binds to socket and listens for connections
//being done by main thead
void bindAndListen(){
struct sockaddr_in saddr;
struct hostent *hp;
char hostname[256];
int sock_id, sock_fd;
gethostname(hostname, 256);
hp = gethostbyname(hostname);
bzero(&saddr, sizeof(saddr));
//errno = 0;
bcopy(hp->h_addr, &saddr.sin_addr, hp->h_length);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORTNUM);
saddr.sin_addr.s_addr = INADDR_ANY;
sock_id = socket(AF_INET, SOCK_STREAM, 0);
if(sock_id == -1){
oops("socket");
printf("socket");
}
if(bind(sock_id, (const sockaddr*)&saddr, sizeof(saddr)) ==0){
if(listen(sock_id, 5) ==-1){
oops("listen");
}
//each time a new connection is accepted, get file info and push to ready queue
while(1){
int addrlen = sizeof(saddr);
sock_fd = accept(sock_id, (sockaddr*)&saddr, (socklen_t*)&addrlen);
if (sock_fd > 0) {
acceptConnection(sock_fd);
}else {
oops("Error Accepting Connection");
}
}
}else{
oops("there was an error binding to socket");
}
}// end of bindAndListen()
//accepts connection and gets file info of requested file
//being done by main thread
void acceptConnection(int sock_fd){
printf("**Server: A new client connected!");
//only using loop so on error we can break out on error
while(true){
//used to hold input from client
char* inputBuff = new char[BUFSIZ];
int slen = read(sock_fd, inputBuff, BUFSIZ);
//will sit on space between HEAD/GET and path
int pos1 = 0;
//will sit on space between path and HTTP version
int pos2 = 0;
//need duplicate ptr so we can manipulate one in the loop
char* buffPtr = inputBuff;
//parses client input breaks up query by spaces
for(int i=0; i<slen; i++){
if(*buffPtr == ' '){
if (pos1 == 0) {
pos1 = i;
}else {
pos2 = i;
break;
}
}
buffPtr++;
}
if((pos1 - pos2) >=0){
std::string str = "Invalid Query";
write(sock_fd, str.c_str(), strlen(str.c_str()));
break;
}
printf("slen length %d\n", slen);
std::string* method = new std::string(inputBuff, pos1);
printf("method length %lu\n",method->length());
//increment the ptr for buff to the starting pos of the path
inputBuff += ++pos1;
printf("pos2 - pos1 %d\n", (pos2 - pos1));
printf("pos1 = %d pos2 = %d\n", pos1, pos2);
std::string* path = new std::string(inputBuff, (pos2 - pos1));
printf("path length %lu\n", path->length());
printf("part1 %s\n", method->c_str());
printf("part2 %s\n", path->c_str());
//opens file requested by client
int fd = open(path->c_str(), O_RDONLY);
if(fd < 0){
std::string* error = new std::string("Error Opening File");
*error += *path + std::string(strerror(errno), strlen(strerror(errno)));
write(sock_fd, error->c_str(), strlen(error->c_str()));
break;
}
int file_size;
if(method->compare("GET") == 0){
//gets file info and puts the resulting struct in file_info
struct stat file_info;
if(fstat(fd, &file_info) !=0){
oops("Error getting file info");
}
file_size = file_info.st_size;
}else if(method->compare("HEAD")){
file_size = 0;
}else{
write(sock_fd, "Invalid Query", strlen("Invalid Query"));
break;
}
//job to be pushed to ready queue
std::vector<int> job;
job.push_back(sock_fd);
job.push_back(file_size);
job.push_back(fd);
//check mutex guarding the ready queue
pthread_mutex_lock(&jobs_mutex);
//push job to back of ready queue
jobs->push_back(job);
//unlock mutex guarding the ready queue
pthread_mutex_unlock(&jobs_mutex);
//increment number of jobs in ready queue
sem_post(&waiting_jobs);
} //end of while(true)
// we only end up here if there was an error
fflush(stdout);
close(sock_fd);
}// end of acceptConnection()
//routine run by dispather thread
void *dispatchJobs(void*){
while(true){
//wait for a thread to be available to execute a job
sem_wait(&available_threads);
//wait for a job to be waiting in the ready queue
sem_wait(&waiting_jobs); //this is the line thats crashing
//aquire lock to check which threads are waiting
pthread_mutex_lock(&sleeping_threads_mutex);
//go through list of threads to see which is waiting
for(int i=0; i<sleeping_threads_list->size(); i++){
if(sleeping_threads_list->at(i)){
//unlocks lock for access to list of waiting threads
pthread_mutex_unlock(&sleeping_threads_mutex);
//allows us access to the list of condition variables to signal the thread to resume execution
pthread_mutex_lock(&sleep_signal_mutex[i]);
pthread_cond_signal(&sleep_signal_cond[i]);
pthread_mutex_unlock(&sleep_signal_mutex[i]);
}
}
}//end of while(true)
}//end of dispatchJobs()
//sends file or metadata to client
//run by worker thread
//pos is position of condition variable that it waits to be signaled in the sleep_signal_cond[] array
void* replyToClient(void* pos){
int position = (long)pos;
while(true){
//waits for dispather thread to signal it
pthread_mutex_lock(&sleep_signal_mutex[position]);
pthread_cond_wait(&sleep_signal_cond[position], &sleep_signal_mutex[position]);
pthread_mutex_unlock(&sleep_signal_mutex[position]);
//lock mutex to get job to be executed
pthread_mutex_lock(&jobs_mutex);
std::vector<int> job = jobs->front();
//removes job from front of vector
jobs->erase(jobs->begin());
//releases mutex
pthread_mutex_unlock(&jobs_mutex);
//socket file descriptor, used for writing to socket
int sock_fd =job[0];
int file_size = job[1];
//file descriptor for requested job
int fd = job[2];
//holds output to be written to socket
char* outputBuffer = new char[BUFSIZ];
//GET request, send file
if(file_size !=0){
int readResult = 0;
while ((readResult = read(fd, outputBuffer, BUFSIZ)) > 0) {
if(write(sock_fd, outputBuffer, readResult) != readResult){
printf("We may have a write error");
}
}
if(readResult < 0){
oops("Error Reading File");
}
if(readResult == 0){
printf("finished sending file");
}
}else{ // HEAD request
}
//increment number of available threads
sem_post(&available_threads);
}
}// end of replyToClient()
Check again the whole logic of the code - it is possible to reach here:
pthread_mutex_lock(&jobs_mutex);
std::vector<int> job = jobs->front();
//removes job from front of vector
jobs->erase(jobs->begin());
//releases mutex
pthread_mutex_unlock(&jobs_mutex);
with jobs->size () == 0, in which case front() and erase() invoke undefined behavior, which may well result in the effects you observe.
Check whether your program still crashes after the following change:
//lock mutex to get job to be executed
pthread_mutex_lock(&jobs_mutex);
if (jobs->size () == 0)
{
pthread_mutex_unlock (&jobs_mutex);
continue;
}
std::vector<int> job = jobs->front();
//removes job from front of vector
jobs->erase(jobs->begin());
//releases mutex
pthread_mutex_unlock(&jobs_mutex);
I haven't used POSIX semaphores, but I believe this is what is happening. I'm only familiar with Linux kernel semaphores, and you don't mention your system. The init function's 3rd parameter probably sets the count variable. You set it to 0 (= busy but no other processes waiting). The wait function probably invokes down(), which begins by decreasing the count variable by 1: to -1, which means the semaphore you mean to use is locked now. There is nothing in your program to ever unlock it I believe (from browsing your code - it's pretty long), so you are in trouble. Try setting it to 1 in init. This might be all that is needed.

Resources