Related
EDIT:
Thanks for the suggestions given so far. I changed the program and now the parent handles some of the signals, but it looks like it doesn't handle all of them. New code and results are posted below.
EDIT2:
I changed the random number generation as proposed. Now the parent catches only two signals but it always catches the right bits (two last bits).
"Unfortunately, I am not experienced in C POSIX and I have to write a program that will take one argument (filename containing a binary number) and parse this file. Each bit denoted in the file means that one child should be created (each bit is dedicated to one child). The value of the bit (0 or 1) decides which signals should be sent to the parent (0 - SIGUSR1, 1 - SIGUSR2).
The child process should choose a random interval (10-200ms) and send appropriate signal to the parent.
The parent should receive the signals and print the last 5 bits received every time a new signal arrives.
The final step is the matching process - the parent checks the received signals (bits assigned to SIGUSR1 or SIGUSR2) and if there's a match it prints SUCCESS. If there's no match (whenever a wrong bit is sent - compared to the file) the parent starts matching from the beginning."
The updated version:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <signal.h>
volatile sig_atomic_t last_signal = 0;
char * str;
char * received;
int count = 0;
#define ERR(source) (fprintf(stderr,"%s:%d\n",__FILE__,__LINE__),\
perror(source),kill(0,SIGKILL),\
exit(EXIT_FAILURE))
void sethandler( void (*f)(int), int sigNo)
{
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = f;
if (-1==sigaction(sigNo, &act, NULL)) ERR("sigaction");
}
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL) return NULL; //could not open file
fseek(file, 0, SEEK_END);
long f_size = ftell(file);
fseek(file, 0, SEEK_SET);
code = malloc(f_size);
received = malloc(f_size);
while ((c = fgetc(file)) != EOF) {
code[n++] = (char)c;
}
code[n] = '\0';
return code;
}
void append(char* s, char c)
{
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
}
static void sig_handle(int signum)
{
last_signal = signum;
}
void child_w(int number_of)
{
if(str[number_of] == '0')
{
if (kill(getppid(), SIGUSR1)==0) printf("[SIGUSR1] sent \n");
else
{
printf("ERROR kill. \n");
exit(EXIT_FAILURE);
}
}
if(str[number_of] == '1')
{
if (kill(getppid(), SIGUSR2) == 0) printf("[SIGUSR2] sent \n");
else
{
printf("ERROR kill. \n");
exit(EXIT_FAILURE);
}
}
}
void create_children(int n)
{
pid_t s;
int j = n;
int time = rand() % 191 + 10; // range 10 - 200
struct timespec time_wait = { .tv_sec = 0, .tv_nsec = time * 1000000L };
while(j-->0)
{
nanosleep(&time_wait, NULL);
if((s=fork())<0) ERR("Fork ERROR");
if(!s) {
printf("Child %d started ", j);
printf("with bit: %c \n", str[j]);
child_w(j);
exit(EXIT_SUCCESS);
}
}
}
void parent_w(sigset_t oldmask)
{
int count = 0;
int match = 0;
while(1)
{
last_signal = 0;
while(last_signal != SIGUSR1 && last_signal != SIGUSR2)
{
sigsuspend(&oldmask);
}
printf("\n");
if(last_signal == SIGUSR1)
{
received[count] = '0';
for(int i=0; i<sizeof(received); ++i)
{
printf("%c ", received[i]);
}
count++;
}
else if(last_signal == SIGUSR2)
{
received[count] = '1';
for(int i=0; i<sizeof(received); ++i)
{
printf("%c ", received[i]);
}
count++;
}
printf("\n");
}
}
int main(int argc, char ** argv)
{
char filename[250];
if(argc!=2)
{
printf("Provide one parameter - filename. \n");
return EXIT_FAILURE;
}
strcpy(filename, argv[1]);
str = readFile(filename);
printf("FILE: ");
for(int i=0; i<sizeof(str); ++i)
{
printf("%c ", str[i]);
}
printf("\n");
for(int i=0; i<sizeof(received); ++i)
{
received[i] = '-';
}
sethandler(sig_handle, SIGUSR1);
sethandler(sig_handle, SIGUSR2);
sigset_t mask, oldmask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigaddset(&mask, SIGUSR2);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
create_children(sizeof(str));
parent_w(oldmask);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
free(str);
free(received);
return EXIT_SUCCESS;
}
Now the output always looks like this:
FILE: 1 0 0 1 1 0 1 0
Child 7 started with bit: 0
[SIGUSR1] sent
Child 6 started with bit: 1
[SIGUSR2] sent
Child 5 started with bit: 0
[SIGUSR1] sent
Child 4 started with bit: 1
[SIGUSR2] sent
Child 3 started with bit: 1
[SIGUSR2] sent
Child 2 started with bit: 0
[SIGUSR1] sent
Child 1 started with bit: 0
[SIGUSR1] sent
0 - - - - - - -
Child 0 started with bit: 1
[SIGUSR2] sent
0 1 - - - - - -
Any further suggestions will be appreciated :).
In addtition to the problems mentioned by others, your readFile() function invokes undefined behavior by overrunning the buffer you allocate for the file contents:
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL) return NULL; //could not open file
fseek(file, 0, SEEK_END);
long f_size = ftell(file);
fseek(file, 0, SEEK_SET);
code = malloc(f_size);
received = malloc(f_size);
while ((c = fgetc(file)) != EOF) {
code[n++] = (char)c;
}
code[n] = '\0'; // <- this is f_size + 1 bytes into the code array
return code;
}
When you terminate the data with code[n] = '\0'; you write past the end of the buffer code points to, thus invoking undefined behavior.
And, off-topic...
Strictly speaking you can't use fseek()/ftell() to get the size of a file. In your case, you're opening the file in text mode with FILE *file = fopen(fileName, "r");, but in text mode ftell() does not return a byte offset. Per 7.21.9.4 The ftell function, paragraph 2 of the C11 standard:
The ftell function obtains the current value of the file position indicator for the stream pointed to by stream. For a binary stream, the value is the number of characters from the beginning of the file. For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call; the difference between two such return values is not necessarily a meaningful measure of the number of characters written or read.
On POSIX systems, you won't have a problem as POSIX defines ftell() to always return an accurate byte offset. But on Windows you will likely read fewer bytes than the file size would otherwise indicate as the \r\n character sequence actually in the file contents is read as a single \n character.
But on some systems you will truly get "unspecified information" and your code will fail completely.
And seeking to the end of a binary stream isn't portable either. In fact, it's explicitly undefined behavior:
Setting the file position indicator to end-of-file, as with fseek(file, 0, SEEK_END), has undefined behavior for a binary stream...
Again, not a problem on POSIX or Windows systems.
One real problem with fseek()/ftell(), though, is that the long value returned from ftell() doesn't have enough range on many systems to represent file sizes larger than 2 GB. long is 32 bits on 32-bit Linux systems, and its also only 32 bits on all Windows systems, both 32- and 64-bit.
As #KamilCuk observed, it is not necessary or appropriate to re-register signal handlers when they fire. That was standard at one time because of uncertainty about the implementation of the signal() function (which remains to this day): some implementations register handlers such that after they fire once, the signal disposition is reset. With sigaction(), however, once can specify whether they want that "one-shot" behavior or whether they instead want the signal handler to remain registered when it fires, with the latter being the default with that function.
sigaction() allows control of some other details on which signal() implementations vary. In practice, signal() has very few appropriate uses, and none that sigaction() cannot also cover. If you're programming for POSIX then its best to forget that signal() exists.
With all that said, however, I don't think your usage of signal() is the key problem here.
Another issue is that signal handlers are rather restricted in what they may safely do:
they may access file-scope variables of type sig_atomic_t
they may call async-signal-safe standard functions
they may declare and access local variables
they may call any of the program's other functions that comply with these restrictions
HOWEVER, signal handlers are typically called with their own, separate stacks, and these are often very small, so in practice, they cannot safely declare very much in the way of local variables, nor start a very deep call tree. Exactly what the limits are is unspecified, so signal handlers should generally do as little as possible.
In particular, neither printf() nor any of the other stdio functions are async-signal safe. Signal handlers produce undefined behavior if they call any of them. They may call write() if you wish, but there is probably a better alternative here. For example, the parent could pause() or sigsuspend() to await a signal, and then print whatever it needs to do outside the handler. The handler need only set a variable to indicate which signal was received. This will avoid the parent busy-waiting as it presently does, though you still have an issue with potential collisions.
This is more likely to be part of your problem, but I suspect that it's not the key issue, either.
I think the real problem is probably that signals are being lost. Ordinary signals do not queue, so if a signal is received while that signal is already pending for the process then it has no additional effect. The problem is structured to avoid that by asking for each child to delay a random amount of time before firing its signal, but
That's not truly safe, just less likely to manifest a collision, and
Your implementation does not actually delay.
Consider:
int time = rand()%100 + 10;
struct timespec time_wait = {time/1000, 0};
nanosleep(&time_wait, NULL);
Variable time will be assigned a value between 10 and 109, so time / 1000 -- an integer division -- will always evaluate to 0.
Something like this would be more appropriate:
int time = rand() % 191 + 10; // range 10 - 200
struct timespec time_wait = { .tv_sec = 0, .tv_nsec = time * 1000000L };
nanosleep(&time_wait, NULL);
Additionally, instead of seeding a separate (P)RNG in each child, I would seed one, once, in the parent, and generate the delays there, before each fork. Drawing random numbers from the same RNG produces a more uniform distribution.
First of all, good code.
Second:
man pages are your friends. man signal
signal() registers handler for the signal. So after signal(SIGUSR1, some_function) the function some_function will be executed after the signal is received.
Removoe signal() calls from signal handlers (why would you re-register the same handler from inside the handler? It already is the handler for this signal.)
Remove signal() call from the loop in parent. Just register functions once, that's all.
Your sethandler function is the same as signal.
After some fixing:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <signal.h>
char * str; // the array consisting of bits from the file
char * received; //array of the parent receiving the signals
int count = 0;
//Error macro
#define ERR(source) (fprintf(stderr,"%s:%d\n",__FILE__,__LINE__),\
perror(source),exit(EXIT_FAILURE))
//Reading file char by char and returning allocated char array
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL) return NULL; //could not open file
fseek(file, 0, SEEK_END);
long f_size = ftell(file);
fseek(file, 0, SEEK_SET);
code = malloc(f_size);
received = malloc(f_size);
while ((c = fgetc(file)) != EOF) {
code[n++] = (char)c;
}
code[n] = '\0';
return code;
}
// Append the character to the received array
void append(char* s, char c)
{
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
}
// SIGUSR1 handler. I tried to implement simple counter to check if the parent is receiving the signals, then proceed to printing last 5 bits received. Unfortunately this part seems to not work at all.
static void sig_handle1(int signum) {
count++;
printf("%s %d \n", __func__, count);
}
// Handler for SIGUSR2 - same as handler for SIGUSR1
static void sig_handle2(int signum) {
count++;
printf("%s %d \n", __func__, count);
}
// Child function - set the random interval, wait and then send the appropriate signal to the parent
void child_w(int number_of)
{
srand(time(NULL)*getpid());
int time = rand()%100 + 10;
struct timespec time_wait = {time/1000, 0};
nanosleep(&time_wait, NULL);
if(str[number_of] == '0')
{
if (kill(getppid(), SIGUSR1)==0) printf("[SIGUSR1] sent \n");
else
{
printf("ERROR kill. \n");
exit(EXIT_FAILURE);
}
}
if(str[number_of] == '1')
{
if (kill(getppid(), SIGUSR2) == 0) printf("[SIGUSR2] sent \n");
else
{
printf("ERROR kill. \n");
exit(EXIT_FAILURE);
}
}
}
// Function which will create children (number of children = number of bits in the file)
void create_children(int n)
{
pid_t s;
int j = n;
while(j-->0)
{
if((s=fork())<0) ERR("Fork ERROR");
if(!s) {
printf("Child %d started ", j);
printf("with bit: %c \n", str[j]);
child_w(j);
//if(j==1) kill(getppid(), SIGUSR2);
exit(EXIT_SUCCESS);
}
}
}
// Parent function to check the received signals
void parent_w()
{
signal(SIGUSR1, sig_handle1);
signal(SIGUSR2, sig_handle2);
while(1)
{
pause();
}
}
int main(int argc, char ** argv)
{
char filename[250];
if(argc!=2)
{
printf("Provide one parameter - filename. \n");
return EXIT_FAILURE;
}
strcpy(filename, argv[1]);
str = readFile(filename);
printf("FILE: ");
for(int i=0; i<sizeof(str); ++i)
{
printf("%c ", str[i]);
}
printf("\n");
create_children(sizeof(str)-1);
parent_w();
free(str);
free(received);
return EXIT_SUCCESS;
}
Example exeuction:
FILE: 1 0 0 1 1 0 1
Child 0 started with bit: 1
Child 1 started with bit: 0
Child 2 started with bit: 0
Child 3 started with bit: 1
[SIGUSR2] sent
sig_handle2 1
[SIGUSR1] sent
sig_handle1 2
[SIGUSR1] sent
sig_handle1 3
[SIGUSR2] sent
sig_handle2 4
Child 4 started with bit: 1
Child 5 started with bit: 0
sig_handle2 5
[SIGUSR2] sent
Child 6 started with bit: 1
sig_handle1 6
[SIGUSR1] sent
sig_handle2 7
[SIGUSR2] sent
^C
I am still a baby in C world and I was doing some "system" programming in order to do some exercises when I stumbled upon some error which is obvious, but I can't find the problem within my application
This is the code
const int __WRITE_ERROR = 44;
const int __READ_ERROR = 43;
const int READ = 0;
const int WRITE = 1;
const int MAX = 1024;
int main(int argc, char *argv[]) {
int fd[2], n;
char buff[MAX];
if(pipe(&fd[2]) < 0)
exit(__PIPE_ERROR);
printf("Hello, from pipe: write: %d and read: %d\n", fd[WRITE], fd[READ]);
if(write(fd[WRITE], "Hello World\n", 12) != 12) {
printf("Explanation: %i", errno); // <- constantly comes here with errno 9 for some reason.
exit(__WRITE_ERROR);
}
if((n = read(fd[READ], buff, MAX)) != 0)
exit(__READ_ERROR);
write(1, buff, n);
exit(0);
}
Could you help me out, cause I ran out of options, thanks.
There is a problem with:
if (pipe(&fd[2]) < 0)
It should be instead:
if (pipe(fd) < 0)
The former is passing to pipe() the address of one element past of the bounds of the array fd (i.e.: the &fd[2]).
Also, read() and write() return the number of bytes read or written, respectively. However, if an error occurs -1 is returned for both functions.
As someone already pointed out first mistake was
if (pipe(&fd[2]) < 0)
had to be
if (pipe(fd) < 0)
then read was failing because of wrong conition
if((n = read(fd[READ], buff, MAX)) != 0)
should have been
if((n = read(fd[READ], buff, MAX)) <= 0)
So I'm trying to code a multi-threading server. I've spent an enormous time on the internet figuring out the correct way to do this and the answer as always seems to be it depends. Whenever I execute my code, the client successfully connects, and executes but when the thread terminates and returns to the while loop the whole program segfaults.
I probably could use a good spanking on a few other things as well such as my usage of global variables. The entirety of code is below, sorry for the inconsistent space/tabbing.
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <signal.h>
#include <math.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
/* ---------------------------------------------------------------------
This is a basic whiteboard server. You can query it, append to it and
clear in it. It understands both encrypted and unencrypted data.
--------------------------------------------------------------------- */
struct whiteboard {
int line;
char type;
int bytes;
char string[1024];
} *Server;
int serverSize, threadcount, id[5];
bool debug = true;
struct whiteboard *Server;
pthread_mutex_t mutex;
pthread_t thread[5];
/* -------------------------------------------
function: sigint_handler
Opens a file "whiteboard.all" in writemode
and writes all white board information in
command mode.
------------------------------------------- */
void sigint_handler(int sig)
{
if (debug) printf("\nInduced SIGINT.\n");
FILE *fp;
fp=fopen("whiteboard.all","w");
int x=0;
for (x;x<serverSize;x++) // Loop Responsible for iterating all the whiteboard entries.
{
if (debug) printf("#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
fprintf(fp,"#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
}
if (debug) printf("All values stored.\n");
free(Server); // Free dynamically allocated memory
exit(1);
}
/* -------------------------------------------
function: processMessage
Parses '!' messages into their parts -
returns struct in response.
------------------------------------------- */
struct whiteboard processMessage(char * message)
{
int lineNumber, numBytes;
char stringType, entry[1028];
if (debug) printf("Update Statement!\n");
// Read line sent by Socket
sscanf(message,"%*c%d%c%d\n%[^\n]s",&lineNumber,&stringType,&numBytes,entry);
if (debug) printf("Processed: Line: %d, Text: %s\n",lineNumber,entry);
// Parse information into local Struct
struct whiteboard Server;
Server.line = lineNumber;
Server.type = stringType;
Server.bytes = numBytes;
strcpy(Server.string,entry);
// If there is no bytes, give nothing
if (numBytes == 0)
{
strcpy(Server.string,"");
}
return Server;
}
/* -------------------------------------------
function: handleEverything
Determines type of message recieved and
process and parses accordingly.
------------------------------------------- */
char * handleEverything(char* message, struct whiteboard *Server, char* newMessage)
{
bool updateFlag = false, queryFlag = false;
// If message is an Entry
if (message[0] == '#')
{
if (debug) printf("Triggered Entry!\n");
// Create Temporary Struct
struct whiteboard messageReturn;
messageReturn = processMessage(message);
// Store Temporary Struct in Correct Heap Struct
Server[messageReturn.line] = messageReturn;
sprintf(newMessage,"!%d%c%d\n%s\n",messageReturn.line, messageReturn.type, messageReturn.bytes, messageReturn.string);
return newMessage;
}
// If message is a query
if (message[0] == '?')
{
if (debug) printf("Triggered Query!\n");
int x;
queryFlag = true;
sscanf(message,"%*c%d",&x); // Parse Query
if (x > serverSize) // Check if Query out of Range
{
strcpy(newMessage,"ERROR: Query out of Range.\n");
return newMessage;
}
sprintf(newMessage,"!%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
if (debug) printf("newMessage as of handleEverything:%s\n",newMessage);
return newMessage;
}
}
/* -------------------------------------------
function: readFile
If argument -f given, read file
process and parse into heap memory.
------------------------------------------- */
void readFile(char * filename)
{
FILE *fp;
fp=fopen(filename,"r");
int line, bytes, count = 0, totalSize = 0;
char type, check, string[1028], individualLine[1028];
// Loop to determine size of file. **I know this is sloppy.
while (fgets(individualLine, sizeof(individualLine), fp))
{
totalSize++;
}
// Each line shoud have totalSize - 2 (to account for 0)
// (answer) / 2 to account for string line and instruction.
totalSize = (totalSize - 2) / 2;
serverSize = totalSize+1;
if (debug) printf("Total Size is: %d\n",serverSize);
// Open and Allocate Memory
fp=fopen(filename,"r");
if (debug) printf("File Mode Calloc Initialize\n");
Server = calloc(serverSize+2, sizeof(*Server));
// Write to Heap Loop
while (fgets(individualLine, sizeof(individualLine), fp)) {
if (individualLine[0] == '#') // Case of Header Line
{
sscanf(individualLine,"%c%d%c%d",&check,&line,&type,&bytes);
if (debug) printf("Count: %d, Check:%c, Line:%d, Type: %c, Bytes:%d \n",count,check,line,type,bytes);
Server[count].line = line;
Server[count].type = type;
Server[count].bytes = bytes;
count++;
}
else
{
// For case of no data
if (individualLine[0] == '\n')
{
strcpy(string,"");
}
// Then scan data line
sscanf(individualLine,"%[^\n]s",string);
if (debug) printf("String: %s\n",string);
strcpy(Server[count-1].string,string);
}
}
return;
}
void *threadFunction(int snew)
{
char tempmessage[1024], message[2048];
// Compile and Send Server Message
strcpy(tempmessage, "CMPUT379 Whiteboard Server v0\n");
send(snew, tempmessage, sizeof(tempmessage), 0);
// Recieve Message
char n = recv(snew, message, sizeof(message), 0);
pthread_mutex_lock(&mutex);
if (debug) printf("Attempt to Malloc for newMessage\n");
char * newMessage = malloc(1024 * sizeof(char));
if (debug) printf("goto: handleEverything\n");
newMessage = handleEverything(message, Server, newMessage);
if (debug) printf("returnMessage:%s\n",newMessage);
strcpy(message,newMessage);
free(newMessage);
pthread_mutex_unlock(&mutex);
if (debug) printf("message = %s\n", message);
send(snew, message, sizeof(message), 0);
printf("End of threadFunction\n");
return;
}
/* -------------------------------------------
function: main
Function Body of Server
------------------------------------------- */
int main(int argc, char * argv[])
{
int sock, fromlength, outnum, i, socketNumber, snew;
bool cleanMode;
// Initialize Signal Handling
struct sigaction act;
act.sa_handler = sigint_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
// For correct number of arguments.
if (argc == 4)
{
// If "-n" parameter (cleanMode)
if (strcmp(argv[2], "-n") == 0)
{
// Get size + 1
cleanMode = true;
sscanf(argv[3],"%d",&serverSize);
serverSize += 1;
if (debug) printf("== Clean Mode Properly Initiated == \n");
if (debug) printf("serverSize: %d\n",serverSize);
if (debug) printf("Clean Mode Calloc\n");
Server = calloc(serverSize, sizeof(*Server));
int i = 0;
for (i; i < serverSize; i++) // Initialize allocated Memory
{
Server[i].line = i;
Server[i].type = 'p';
Server[i].bytes = 0;
strcpy(Server[i].string,"");
}
}
// If "-f" parameter (filemode)
else if (strcmp(argv[2], "-f") == 0)
{
// Read File
cleanMode = false;
readFile(argv[3]);
if (debug) printf("== Statefile Mode Properly Initiated == \n");
if (debug) printf("serverSize: %d\n",serverSize);
}
// Otherwise incorrect parameter.
else
{
printf("Incorrect Argument. \n");
printf("Usage: wbs279 pornumber {-n number | -f statefile}\n");
exit(1);
}
sscanf(argv[1],"%d",&socketNumber);
}
// Send Error for Incorrect Number of Arguments
if (argc != 4)
{
printf("Error: Incorrect Number of Input Arguments.\n");
printf("Usage: wbs279 portnumber {-n number | -f statefile}\n");
exit(1);
}
// == Do socket stuff ==
char tempmessage[1024], message[2048];
struct sockaddr_in master, from;
if (debug) printf("Assrt Socket\n");
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror ("Server: cannot open master socket");
exit (1);
}
master.sin_family = AF_INET;
master.sin_addr.s_addr = INADDR_ANY;
master.sin_port = htons (socketNumber);
if (bind (sock, (struct sockaddr*) &master, sizeof (master)))
{
perror ("Server: cannot bind master socket");
exit (1);
}
// == Done socket stuff ==
listen (sock, 5);
int threadNumber = 0;
while(1)
{
printf("But what about now.\n");
if (debug) printf("-- Wait for Input --\n");
printf("Enie, ");
fromlength = sizeof (from);
printf("Meanie, ");
snew = accept (sock, (struct sockaddr*) & from, & fromlength);
printf("Miney, ");
if (snew < 0)
{
perror ("Server: accept failed");
exit (1);
}
printf("Moe\n");
pthread_create(&thread[threadNumber],NULL,threadFunction(snew), &id[threadNumber]);
//printf("Can I join?!\n");
//pthread_join(thread[0],NULL);
//printf("Joined?!\n");
threadNumber++;
close (snew);
}
}
I'm also curious as to how exactly to let multiple clients use the server at once. Is how I've allocated the whiteboard structure data appropriate for this process?
I'm very sorry if these don't make any sense.
You seem to somehow expect this:
pthread_create(&thread[threadNumber],NULL,threadFunction(snew), &id[threadNumber]);
/* ... */
close (snew);
To make sense, while it clearly doesn't.
Instead of starting a thread that runs threadFunction, passing it snew, you call the thread function and pass the return value to pthread_create(), which will interpret it as a function pointer. This will break, especially considering that the thread function incorrectly ends with:
return;
This shouldn't compile, since it's declared to return void *.
Also assuming you managed to start the thread, passing it snew to use as its socket: then you immediately close that socket, causing any reference to it from the thread to be invalid!
Please note that pthread_create() does not block and wait for the thread to exit, that would be kind of ... pointless. It starts off the new thread to run in parallel with the main thread, so of course you can't yank the carpet away from under it.
This signal handler is completely unsafe:
void sigint_handler(int sig)
{
if (debug) printf("\nInduced SIGINT.\n");
FILE *fp;
fp=fopen("whiteboard.all","w");
int x=0;
for (x;x<serverSize;x++) // Loop Responsible for iterating all the whiteboard entries.
{
if (debug) printf("#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
fprintf(fp,"#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
}
if (debug) printf("All values stored.\n");
free(Server); // Free dynamically allocated memory
exit(1);
}
Per 2.4.3 Signal Actions of the POSIX standard (emphasis added):
The following table defines a set of functions that shall be
async-signal-safe. Therefore, applications can call them, without
restriction, from signal-catching functions. ...
[list of async-signal-safe functions]
Any function not in the above table may be unsafe with respect to signals. Implementations may make other interfaces
async-signal-safe. In the presence of signals, all functions defined
by this volume of POSIX.1-2008 shall behave as defined when called
from or interrupted by a signal-catching function, with the exception
that when a signal interrupts an unsafe function or equivalent
(such as the processing equivalent to exit() performed after a return
from the initial call to main()) and the signal-catching function
calls an unsafe function, the behavior is undefined. Additional
exceptions are specified in the descriptions of individual functions
such as longjmp().
Your signal handler invokes undefined behavior.
I write a C code that reads a file and do some works on it, using multi-threads functions. I read file in the fun1 so I expect that file read linearly, but some tests I do on this code show me that it seems that the file does not read in the right order. What is wrong about my code?!
#include <pthread.h>
#define BUFSIZE 1024*10
#define NUM_THREADS 4
typedef struct _thread_data_t {
unsigned char id;
char *msg;
unsigned int msg_len;
} thread_data_t;
/* thread function */
void *thr_func(void *arg) {
thread_data_t *data = (thread_data_t *)arg;
fun2(data->msg, data->msg_len);
pthread_exit(NULL);
}
void fun1(FILE *file) {
unsigned char i, j, buf[BUFSIZE];
pthread_t thr[NUM_THREADS];
thread_data_t thr_data[NUM_THREADS];
int rc, fr, fd = fileno(file);
for (;;) {
for (i = 0; i < NUM_THREADS; i++) {
fr = read(fd, buf, BUFSIZE);
if (fr <= 0) break;
thr_data[i].id = i;
thr_data[i].msg = buf;
thr_data[i].msg_len = fr;
if ((rc = pthread_create(&thr[i], NULL, thr_func, &thr_data[i]))) {
fprintf(stderr, "error: pthread_create, rc: %d\n", rc);
fr = -1;
break;
}
}
for (j = 0; j < i; j++) {
pthread_join(thr[j], NULL);
}
if (fr <= 0) break;
}
}
Edit:
I think that until all threads finish their works nothing new read from the file. Is it true?
I think your problem is the single buffer:
buf[BUFSIZE];
In each loop you read data into that buffer and then prepare data for the thread
thr_data[i].msg = buf;
which I assume doesn't include a copy of the buffer itself. I assume msg is just a pointer.
So in the next iteration, you'll overwrite the buf with new data from the file and consequently change data for already created threads.
I guess you'll need
buf[NUM_THREADS][BUFSIZE];
so that each thread gets its own data area to work on.
Quote:
I think that until all threads finish their works nothing new read from the file. Is it true?
Correct, that's what pthread_join does for you
I'm trying to write some code which uses pipes to communicate between a parent process and it's children. However, my pipe seems to give up after the first time I use it (that is, it stops working after the first use of the pipe). I'm not really sure how to fix this problem, and any help will be greatly appreciated. I also know that some of the coding practice I am using in this are not really ideal (mainly use of sleep).
const int READ = 0;
const int WRITE = 1;
char* COOP = "Criminal cooperates\n";
char* SIL = "Criminal doesn't talk\n";
char* reader(int);
void writer(int, char *c);
int main()
{
int c1pipe1[2];
int c1pipe2[2];
int c2pipe1[2];
int c2pipe2[2];
int c1sentence = 0;
int c2sentence = 0;
int r;
int c;
pipe(c1pipe1);
pipe(c1pipe2);
pipe(c2pipe1);
pipe(c2pipe2);
int C2;
int C1 = fork();
if(C1 > 0)
C2 = fork();
if(C1 < 0 || C2 < 0) //error
{
perror("fork() failed");
exit(1);
}
else if(C1 == 0)
{
close(c1pipe1[WRITE]);
close(c1pipe2[READ]);
for(c = 0; c < 10; c++)
{
r = rand();
//printf("C1 rand = %d\n", r%2);
if(r % 2 == 1)
writer(c1pipe2[WRITE], "1");
else
writer(c1pipe2[WRITE], "0");
sleep(1);
}
exit(0);
}
else if(C2 == 0)
{
close(c2pipe1[WRITE]);
close(c2pipe2[READ]);
for(c = 0; c < 10; c++)
{
r = rand();
//printf("C2 rand = %d\n", r%2);
if(r % 2 == 1)
writer(c2pipe2[WRITE], "1");
else
writer(c2pipe2[WRITE], "0");
sleep(1);
}
exit(0);
}
else //parent
{
int buff1; //stores choice of c1
int buff2; //stores choice of c2
close(c1pipe1[READ]);
close(c1pipe2[WRITE]);
close(c2pipe1[READ]);
close(c2pipe2[WRITE]);
for(c = 0; c< 10; c++)
{
buff1 = atoi(reader(c1pipe2[READ]));
buff2 = atoi(reader(c2pipe2[READ]));
printf("C1's \(%d)\ choice trial %d : %d\n", C1, c+1, buff1);
printf("C2's \(%d)\ choice trial %d : %d\n", C2, c+1, buff2);
if(buff1 && buff2) //c1 and c2 cooperate with police
{
c1sentence = c1sentence + 6;
c2sentence = c2sentence + 6;
}
else if(buff1 || buff2) // one cooperates, one is silent
{
if(buff1) // if c1 cooperates and c2 is silent
{
c1sentence = c1sentence + 0;
c2sentence = c2sentence + 10;
}
else // if c2 cooperates and c1 is silent
{
c1sentence = c1sentence + 10;
c2sentence = c2sentence + 0;
}
}
else if(!(buff1 && buff2)) //both c1 and c2 are silent
{
c1sentence = c1sentence + 1;
c2sentence = c2sentence + 1;
}
sleep(1);
}
printf("C1 is in jail for %d years total\n", c1sentence);
printf("C2 is in jail for %d years total\n", c2sentence);
exit(0);
}
exit(0);
}
void writer(int pipe_write_fd, char *c)
{
open(pipe_write_fd);
char* choice = c;
// Write to the pipe
write(pipe_write_fd, choice, strlen(choice));
// Close the pipe
// (Sends 'end of file' to reader)
close(pipe_write_fd);
}
char* reader(int pipe_read_fd)
{
open(pipe_read_fd);
// Allocate buffer to store
// result of read
int buffer_size = 1024;
char buffer[buffer_size];
// Keep reading until we exhaust
// buffer or reach end of file
int i = 0;
while (i < buffer_size
&& read(pipe_read_fd, &buffer[i], 1) > 0)
{ i++; }
if (i < buffer_size) {
// Add null termination
buffer[i] = '\0';
} else {
// We exhausted buffer
fprintf(stderr, "Warning: buffer full.\n");
buffer[buffer_size-1] = '\0';
}
//printf("%s", buffer);
// Close the pipe
close(pipe_read_fd);
return buffer;
}
You need to close more of the pipes. The child processes must close every pipe file descriptor that they are not using. You have 8 pipe file descriptors; each child process has to close 6 of those - at least! You would be very well advised not to create all the pipes up front as you have done - it is complicated to control things and get all the right descriptors closed.
Looking at the code more closely, the parent does not write messages to the child processes, so you have twice as many pipes as you need - you only need one pipe for each child process to write back to the parent with.
You also do not open() already open file descriptors to the pipes...but how did you get the code to compile? You must be missing the correct header (#include <fcntl.h>) for open() and compiling without enough warning options enabled.
Your variables COOP and SIL are unused in the code presented.
Your writer() function not only mistakenly tries to open an already closed file descriptor, it also closes it, which means that there is no way to send back the extra messages after the first. You should only close the file descriptor once finished - after the loop in the main program for each child. This is why you only see one message.
It is also worth getting into the habit of error-checking the return from every system call that can fail. There are a few that can't fail - getpid() is one such. But I/O operations are notorious for failing for reasons outside the direct control of the program (or, in this case, within the control of the program), so you should check that writes succeed. When you get back an EBADF - bad file descriptor - error, you know something is up.
You have similar problems with close() (and open()) in reader(), plus the additional problem that you attempt to return a pointer to a local automatic variable - which is not a good idea, ever. Again, a decent compiler (like GCC) with warnings enabled will tell you about such things. I used this command to compile your program:
gcc -O -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
pipe.c -o pipe
Your child processes are always going to generate the same sequence of (pseudo-)random numbers, which isn't very exciting. You should probably use something like:
srand(getpid());
to ensure they get different sequences.
Your reader() function is both not enthusiastic enough and too enthusiastic about reading the data. You read a single byte at a time, but you then loop to accumulate single bytes, so the code waits around for all 10 results to be known, and then spits everything out at once. Since a 32-bit integer can store a number up to 1,111,111,111 without problem, you would get just one number back from your call to atoi() on the first iteration, which isn't quite what you wanted.
Reads and writes on pipes are atomic - in the sense that if the writing process writes 6 bytes and the reading process attempts to read more than 6 bytes, then the packet of 6 bytes will be returned by a single read, even if there are other bytes in the pipe waiting to be read; those extra bytes will be returned on subsequent calls to read().
So, your reader() function should be passed in a buffer to use, along with its size; the code should attempt to read that buffer size; it should null terminate what it does receive; it can return the pointer to the buffer it was passed; it should error check the returned value from read().
The code for the two child processes is essentially the same - you should use an appropriately parameterized function rather than writing out the code twice.
Putting it all together, you end up with something like this (which works fine for me on MacOS X 10.6.6 with GCC 4.5.2):
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
const int READ = 0;
const int WRITE = 1;
static char* reader(int fd, char *buffer, size_t bufsiz);
static void writer(int fd, const char *c);
static void child_process(int *my_pipe, int *his_pipe);
static void err_exit(const char *fmt, ...)
{
va_list args;
int errnum = errno;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
exit(1);
}
int main(void)
{
int c1pipe[2];
int c2pipe[2];
int c1sentence = 0;
int c2sentence = 0;
int c;
if (pipe(c1pipe) != 0 || pipe(c2pipe) != 0)
err_exit("Failed to open a pipe\n");
int C2 = 0;
int C1 = fork();
if (C1 > 0)
C2 = fork();
if (C1 < 0 || C2 < 0) //error
err_exit("fork() failed\n");
else if (C1 == 0)
child_process(c1pipe, c2pipe);
else if (C2 == 0)
child_process(c2pipe, c1pipe);
else //parent
{
int choice1; //stores choice of c1
int choice2; //stores choice of c2
char buffer1[BUFSIZ];
char buffer2[BUFSIZ];
close(c1pipe[WRITE]);
close(c2pipe[WRITE]);
for (c = 0; c< 10; c++)
{
choice1 = atoi(reader(c1pipe[READ], buffer1, sizeof(buffer1)));
choice2 = atoi(reader(c2pipe[READ], buffer2, sizeof(buffer1)));
printf("C1's (%d) choice trial %d : %d\n", C1, c+1, choice1);
printf("C2's (%d) choice trial %d : %d\n", C2, c+1, choice2);
if (choice1 && choice2) //c1 and c2 cooperate with police
{
c1sentence = c1sentence + 6;
c2sentence = c2sentence + 6;
}
else if (!(choice1 && choice2)) //both c1 and c2 are silent
{
c1sentence = c1sentence + 1;
c2sentence = c2sentence + 1;
}
else if (choice1) // if c1 cooperates and c2 is silent
{
c1sentence = c1sentence + 0;
c2sentence = c2sentence + 10;
}
else // if c2 cooperates and c1 is silent
{
c1sentence = c1sentence + 10;
c2sentence = c2sentence + 0;
}
}
printf("C1 is in jail for %d years total\n", c1sentence);
printf("C2 is in jail for %d years total\n", c2sentence);
}
return(0);
}
static void writer(int pipe_write_fd, const char *c)
{
int len = strlen(c);
if (write(pipe_write_fd, c, len) != len)
err_exit("Write failed\n");
}
static char* reader(int pipe_read_fd, char *buffer, size_t bufsiz)
{
int i = read(pipe_read_fd, buffer, bufsiz-1);
if (i < 0)
err_exit("Read failed\n");
buffer[i] = '\0';
return buffer;
}
static void child_process(int *my_pipe, int *his_pipe)
{
int c;
srand(getpid());
close(my_pipe[READ]);
close(his_pipe[READ]);
close(his_pipe[WRITE]);
for (c = 0; c < 10; c++)
{
writer(my_pipe[WRITE], ((rand() % 2) == 1) ? "1" : "0");
sleep(1);
}
close(my_pipe[WRITE]);
}
Note how the error routine captures errno early - to avoid damaging it. It is one of the perils of using global variables; they may change when you call a function. Don't use them when you can avoid them (but note that you can't avoid using errno completely, in general).
void writer(int pipe_write_fd, char *c)
{
open(pipe_write_fd);
char* choice = c;
// Write to the pipe
write(pipe_write_fd, choice, strlen(choice));
// Close the pipe
// (Sends 'end of file' to reader)
close(pipe_write_fd);
}
I'm not sure which function open you are trying yo use but the usual one takes a filename and returns a file descriptor. In any case you are discarding the return value so I suppose that this doesn't matter.
What is clear is that you close the pipe immediately after the first write so it is "correct" that the next write will fail; the pipe has been closed.
If you fix this problem then the next problem is that reader will, one byte at a time, all of the available input - up to 1024 bytes - before closing the read pipe. As reader is called in a loop, the read attempt in the second iteration will fail.