I am trying to understand POSIX file-region locks in C. The program below is really simple, sets the lock to F_WRLCK and then gets locks. There is no errors during opening/setting lock. Unfortunatelly it's always returning F_UNLCK. Where is the mistake ? Is it possible that it doesnt work on OSX correctly ?
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
void printLockType(int lock) {
if ( lock == F_RDLCK ) {
printf("readlock %i \n", lock);
}else if ( lock == F_WRLCK ) {
printf("writelock %i \n", lock);
}else if ( lock == F_UNLCK ) {
printf("unlock %i \n", lock);
} else {
printf("other %i\n", lock);
}
}
int main(int argc, char *argv[])
{
int fd;
struct flock fl ,fl2;
fl2.l_type = F_RDLCK; /* read/write lock */
fl2.l_whence = 0; /* beginning of file */
fl2.l_start = 0; /* offset from l_whence */
fl2.l_len = 100; /* length, 0 = to EOF */
fl2.l_pid = getpid();
fl.l_type = F_WRLCK; /* read/write lock */
fl.l_whence = 0; /* beginning of file */
fl.l_start = 0; /* offset from l_whence */
fl.l_len = 1000; /* length, 0 = to EOF */
fl.l_pid = getpid();
if ((fd = open("xxx", O_RDWR)) == -1) {
perror("open");
exit(1);
}
if (fcntl(fd, F_SETLK, &fl) == -1) {
perror("fcntl");
exit(1);
}
if(fcntl(fd, F_GETLK, &fl2) == -1) {
printf("%s \n", strerror(errno));
} else {
printLockType(fl2.l_type);
}
return 0;
}
You're misunderstanding the F_GETLK query. It returns F_UNLCK when nothing blocks the calling process from placing a lock of the given type at the given position.
Since the calling process is the one that created these existing locks, it can also create this new lock.
The Mac OS X manuals say
F_GETLK
Get the first lock that blocks the lock description pointed to by the third argument, arg,
taken as a pointer to a struct flock (see above). The information retrieved overwrites the
information passed to fcntl in the flock structure. If no lock is found that would prevent
this lock from being created, the structure is left unchanged by this function call except
for the lock type which is set to F_UNLCK.
Related
My English is poor so you may get confused from my description below.
In Linux, multiple processes were requesting a file lock (flock or fcntl lock), then the previous exclusive file lock was released. I think which process can gain the lock is random (not specified).
But every time I try, it always seems like in FIFO (like the following photo). (And I have already tried many times).
I want to figure out is something wrong with my code or anything else?
#include <sys/file.h>
#include <fcntl.h>
#include <string.h>
#include "tlpi_hdr.h"
char *currTime(const char *format);
int main(int argc, char *argv[])
{
int fd;
struct flock fl;
fd = open("./file", O_RDWR); /* Open file to be locked */
if (fd == -1)
errExit("open");
fl.l_len = 0;
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_type = F_WRLCK;
if (fcntl(fd, F_SETLKW, &fl) == -1)
{
if (errno == EAGAIN || errno == EACCES)
printf("already locked");
else if (errno == EDEADLK)
printf("dead lock");
else
errExit("fcntl");
}
else
printf("PID %ld: have got write lock at %s\n", (long)getpid(), currTime("%T"));
sleep(atoi(argv[1]));
exit(EXIT_SUCCESS); // close fd and this cause unlock flock's lock
}
Hello I have a single file in c that shares memory from the parent to child but I need my code separated into two separate files while still sharing the memory. I need the parent to create the shared memory and get the input of the fib number. Then the child process opens the share memory object to read the value n and overwrite the value n by the value of fib(n). and displays the fib series. This is what I have now
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
// So we could use other sizes without editing the source.
#ifndef MAX_SEQUENCE
# define MAX_SEQUENCE 10
#endif
// Check that MAX_SEQUENCE is large enough!
#if MAX_SEQUENCE < 2
#error MAX_SEQUENCE must be at least 2
#endif
typedef struct{
long fib_sequence[MAX_SEQUENCE];
int sequence_size;
} shared_data;
int main()
{
int a, b, m, n, i;
a = 0; b = 1;
printf("Enter the number of a Fibonacci Sequence:\n");
// Always check whether input conversion worked
if (scanf("%d", &m) != 1) {
printf("Invalid input, couldn't be converted.\n");
return EXIT_FAILURE;
}
if (m <= 0) {
printf("Please enter a positive integer\n");
return EXIT_FAILURE; // exit if input is invalid
} else if (m > MAX_SEQUENCE) {
printf("Please enter an integer less than %d\n", MAX_SEQUENCE);
return EXIT_FAILURE; // exit if input is invalid
}
/* the identifier for the shared memory segment */
int segment_id;
/* the size (in bytes) of the shared memory segment */
size_t segment_size = sizeof(shared_data);
/* allocate a shared memory segment */
segment_id = shmget(IPC_PRIVATE, segment_size, S_IRUSR | S_IWUSR);
// Check result of shmget
if (segment_id == -1) {
perror("shmget failed");
return EXIT_FAILURE;
}
/* attach the shared memory segment */
shared_data *shared_memory = shmat(segment_id, NULL, 0);
// Check whether attaching succeeded
if ((void*)shared_memory == (void*)-1) {
perror("shmat failed");
goto destroy; // clean up
}
printf("\nshared memory segment %d attached at address %p\n", segment_id, (void*)shared_memory);
shared_memory->sequence_size = m;
pid_t pid;
pid = fork();
if (pid == 0){
printf("Child is producing the Fibonacci Sequence...\n");
shared_memory->fib_sequence[0] = a;
shared_memory->fib_sequence[1] = b;
for (i = 2; i < shared_memory->sequence_size; i++){
n = a+b;
shared_memory->fib_sequence[i] = n;
a = b;
b = n;
}
printf("\nChild ends\n");
}
else{
printf("Parent is waiting for child to complete...\n");
wait(NULL);
printf("Parent ends\n");
for(i = 0; i < shared_memory->sequence_size; i++) {
printf("%ld ", shared_memory->fib_sequence[i]);
}
printf("\n");
}
/* now detach the shared memory segment */
if (shmdt(shared_memory) == -1) {
fprintf(stderr, "Unable to detach\n");
}
destroy:
/* now remove the shared memory segment */
shmctl(segment_id, IPC_RMID, NULL);
return 0;
}
There are a couple of options for shared memories :
Datapools - Datapool is an allocated location that kernel provides to a process upon request. Then other processes use the name which was used to create the data pool to connect to it and read/write from it.
Pipelines - Pipeline is another form of sharing resources which again kernel provides upon request. The difference is pipeline is usually one-way whereas data pool can be read and written by all the processes. Also, reads from the pipeline are destructive.
Files - You can also use files which is the most basic and you are probably familiar with it.
These are basic explanations, you have to research on these topics to fully understand and use them. Also, each operating system has a specific way of using these concepts, but all of them provide it (in their own way).
Instead of attaching the shared memory in the parent and then inheriting it in the client, use ftok() to get a common shared memory key that's used by both processes.
Create a file fibonacci in your current directory, this will be used in the calls to ftok().
When the parent forks the child process, it calls execl() to execute the child program, rather than including the child code directly. The child program doesn't need any of the fork() code, it just needs to attach to the same shared memory segment and fill in the results.
fibonacci.h
#ifndef FIBONACCI_H
#define FIBONACCI_H
// So we could use other sizes without editing the source.
#ifndef MAX_SEQUENCE
# define MAX_SEQUENCE 10
#endif
// Check that MAX_SEQUENCE is large enough!
#if MAX_SEQUENCE < 2
#error MAX_SEQUENCE must be at least 2
#endif
#define TOKEN_PATH "fibonacci"
typedef struct{
long fib_sequence[MAX_SEQUENCE];
int sequence_size;
} shared_data;
#endif
testparent.c
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include "fibonacci.h"
int main()
{
int m, i;
printf("Enter the number of a Fibonacci Sequence:\n");
// Always check whether input conversion worked
if (scanf("%d", &m) != 1) {
printf("Invalid input, couldn't be converted.\n");
return EXIT_FAILURE;
}
if (m <= 0) {
printf("Please enter a positive integer\n");
return EXIT_FAILURE; // exit if input is invalid
} else if (m > MAX_SEQUENCE) {
printf("Please enter an integer less than %d\n", MAX_SEQUENCE);
return EXIT_FAILURE; // exit if input is invalid
}
/* the identifier for the shared memory segment */
int segment_id;
/* the size (in bytes) of the shared memory segment */
size_t segment_size = sizeof(shared_data);
/* Get shared memory token */
key_t token = ftok(TOKEN_PATH, 0);
if (token == -1) {
perror("ftok");
return EXIT_FAILURE;
}
/* allocate a shared memory segment */
segment_id = shmget(token, segment_size, S_IRUSR | S_IWUSR | IPC_CREAT);
// Check result of shmget
if (segment_id == -1) {
perror("shmget failed");
return EXIT_FAILURE;
}
/* attach the shared memory segment */
shared_data *shared_memory = shmat(segment_id, NULL, 0);
// Check whether attaching succeeded
if ((void*)shared_memory == (void*)-1) {
perror("shmat failed");
goto destroy; // clean up
}
printf("\nshared memory segment %d attached at address %p\n", segment_id, (void*)shared_memory);
shared_memory->sequence_size = m;
pid_t pid;
pid = fork();
if (pid == 0){
execl("./testchild", "./testchild", (char *)NULL);
perror("execl"); // If it returns it must have failed
return EXIT_FAILURE;
}
else{
printf("Parent is waiting for child to complete...\n");
wait(NULL);
printf("Parent ends\n");
for(i = 0; i < shared_memory->sequence_size; i++) {
printf("%ld ", shared_memory->fib_sequence[i]);
}
printf("\n");
}
/* now detach the shared memory segment */
if (shmdt(shared_memory) == -1) {
fprintf(stderr, "Unable to detach\n");
}
destroy:
/* now remove the shared memory segment */
shmctl(segment_id, IPC_RMID, NULL);
return 0;
}
testchild.c
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include "fibonacci.h"
int main()
{
int a, b, n, i;
a = 0; b = 1;
/* the identifier for the shared memory segment */
int segment_id;
/* the size (in bytes) of the shared memory segment */
size_t segment_size = sizeof(shared_data);
/* Get shared memory token */
key_t token = ftok(TOKEN_PATH, 0);
if (token == -1) {
perror("ftok");
return EXIT_FAILURE;
}
/* allocate a shared memory segment */
segment_id = shmget(token, segment_size, S_IRUSR | S_IWUSR);
// Check result of shmget
if (segment_id == -1) {
perror("shmget failed");
return EXIT_FAILURE;
}
/* attach the shared memory segment */
shared_data *shared_memory = shmat(segment_id, NULL, 0);
// Check whether attaching succeeded
if ((void*)shared_memory == (void*)-1) {
perror("shmat failed");
return EXIT_FAILURE;
}
printf("\nshared memory segment %d attached at address %p\n", segment_id, (void*)shared_memory);
printf("Child is producing the Fibonacci Sequence...\n");
shared_memory->fib_sequence[0] = a;
shared_memory->fib_sequence[1] = b;
for (i = 2; i < shared_memory->sequence_size; i++){
n = a+b;
shared_memory->fib_sequence[i] = n;
a = b;
b = n;
}
printf("\nChild ends\n");
/* now detach the shared memory segment */
if (shmdt(shared_memory) == -1) {
fprintf(stderr, "Unable to detach\n");
}
return 0;
}
Hy.
I'm trying to execute omxplayer (http://elinux.org/Omxplayer) on the Raspberry Pi after a C fork() via the execve or execl functions so that I can save the PID for the video playing process (so System will not do the work). If I execute the program on a X console/terminal it works but if its via a standard terminal (without starting X) it will run but not output the video it to the screen if execve is called on the child process. By the way, executing the player via "omxplayer ..." commnad in the console will play the video and output to the screen. I'm a bit new to this kind of things so this is a situation I haven't been able to solve or find an answer to. Anyone here has an ideia on how to solve this or a direction to give for me to find a possible solution?
Note: The code is just a execve call wich I know is right because in X it works perfectly.
The execve() call supplies a new environment to the executed program. For the program to be able to access the X display, you need to retain certain environment variables -- DISPLAY at minimum. Have you inadvertently omitted DISPLAY from the new environment?
For OMXPlayer to work without X, it has to have access to the video device itself (/dev/video, in this case; see OMXPlayer builds page for details). It's usually configured so that all members of the video group are allowed to access it.
You can use popen("id -Gn", "r") in your program, to run the id -Gn command which lists the current group memberships. (Read the list as a string from the file handle, then close it using pclose().) If the list does not contain video, then the problem is that the privileges of the user that is running the original program do not include access to the video device. The fix is simple: adding video to the groups that user is a member of.
Here is an example program, run.c, to illustrate basic use of execvp():
#include <unistd.h>
/* For the example main(): */
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Try executing a command in a child process.
* Returns the PID of the child process,
* but does not tell whether the execution was
* successful or not.
* Returns (pid_t)-1 with errno set if fork() fails.
*/
pid_t run(char *const command[])
{
pid_t child;
child = fork();
if (child == (pid_t)-1)
return (pid_t)-1;
if (!child) {
execvp(command[0], command);
_exit(127);
}
return child;
}
int main(int argc, char *argv[])
{
pid_t child, p;
int status;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s COMMAND [ ARGUMENTS .. ]\n", argv[0]);
fprintf(stderr, "\n");
return 1;
}
child = run(argv + 1);
if (child == (pid_t)-1) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return 1;
}
fprintf(stderr, "(%s: PID %d)\n", argv[1], (int)child);
fflush(stderr);
do {
p = waitpid(child, &status, 0);
if (p == (pid_t)-1 && errno == EINTR)
continue;
} while (p != child && p != (pid_t)-1);
if (p == (pid_t)-1) {
fprintf(stderr, "(%s: %s.)\n", argv[1], strerror(errno));
return 1;
}
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == 127)
fprintf(stderr, "(%s: Could not execute command.)\n", argv[1]);
else
if (WEXITSTATUS(status) == 0)
fprintf(stderr, "(%s: Exited successfully.)\n", argv[1]);
else
fprintf(stderr, "(%s: Exited with error %d.)\n", argv[1], WEXITSTATUS(status));
} else
if (WIFSIGNALED(status))
fprintf(stderr, "(%s: Killed by %s.)\n", argv[1], strsignal(WTERMSIG(status)));
else
fprintf(stderr, "(%s: Died from unknown causes.)\n", argv[1]);
return status;
}
You can compile and test it using e.g.
gcc -W -Wall -O3 run.c -o run
./run date --utc
Note that the run() function does not attempt to check whether the command was actually executed or not; it just returns the child process PID, or (pid_t)-1 if fork() fails.
Many implementations, including GNU C library popen(), use the 127 exit status as an indication that the execution failed. That is, it is not returned by the command that should have been executed, but by the child process, because the command execution failed. The above run() does so too.
You can use a close-on-exec pipe between the parent and child processes in the run() function, to let the parent process know whether the child process successfully started the desired command or not, and if not, why not. The parent process can then also immediately reap the defunct child process. This leaves very little extra effort to the caller in case of errors, so I personally highly recommend this approach. Here is an example implementation:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
/* Helper function: Close file descriptor, without modifying errno.
* Returns 0 if successful, otherwise the errno reported by close().
*/
static int closefd(const int fd)
{
int saved_errno, result;
/* Invalid descriptor? */
if (fd == -1)
return EINVAL;
/* Save errno. It's thread-local, so as long as we restore
* it before returning, no-one will notice any change in it. */
saved_errno = errno;
/* Close descriptor, and save errno (or 0) in result. */
do {
result = close(fd);
} while (result == -1 && errno == EINTR);
if (result == -1)
result = errno;
else
result = 0;
/* Restore errno. Done. */
errno = saved_errno;
return result;
}
/* Helper function: Create a close-on-exec pipe.
* Return 0 if success, errno otherwise.
*/
int close_on_exec_pipe(int fds[2])
{
int result;
result = pipe(fds);
if (result == -1) {
fds[0] = -1;
fds[1] = -1;
return errno;
}
do {
do {
result = fcntl(fds[0], F_SETFD, FD_CLOEXEC);
} while (result == -1 && errno == EINTR);
if (result == -1)
break;
do {
result = fcntl(fds[1], F_SETFD, FD_CLOEXEC);
} while (result == -1 && errno == EINTR);
if (result == -1)
break;
/* Success. */
return 0;
} while (0);
/* Failed. */
closefd(fds[0]);
closefd(fds[1]);
fds[0] = -1;
fds[1] = -1;
return errno;
}
/* Run an external command in a child process.
* command[0] is the path or name of the command,
* and the array must be terminated with a NULL.
*
* If successful, this function will return the PID
* of the child process. Otherwise, it will return
* (pid_t)-1, with errno indicating the error.
*/
pid_t run(char *const command[])
{
pid_t child, p;
int commfd[2], errcode;
/* Create a close-on-exec pipe between the parent and child. */
if (close_on_exec_pipe(commfd))
return (pid_t)-1;
/* Fork the new child process. */
child = fork();
if (child == (pid_t)-1) {
closefd(commfd[0]);
closefd(commfd[1]);
return (pid_t)-1;
}
if (!child) {
/* Child process: */
/* Close the read/parent end of the pipe. */
closefd(commfd[0]);
/* In case of C library bugs, prepare errno. */
errno = EINVAL;
/* Execute the desired command. */
execvp(command[0], command);
/* Failed. errno describes why. */
errcode = errno;
/* Send it to the parent via the pipe. */
{
const char *p = (char *)&errcode;
const char *const q = (char *)&errcode + sizeof errcode;
ssize_t n;
while (p < q) {
n = write(commfd[1], p, (size_t)(q - p));
if (n > (ssize_t)0)
p += n;
else
if (n != (ssize_t)-1)
break;
else
if (errno != EINTR)
break;
}
}
/* Close write/child end of the pipe. */
closefd(commfd[1]);
/* Exit with a failure (127). */
_exit(127);
}
/* Parent: */
/* Close the write/child end of the pipe. */
closefd(commfd[1]);
/* Try to read the execution error. */
{
char *p = (char *)&errcode;
char *const q = (char *)&errcode + sizeof errcode;
ssize_t n;
errcode = 0;
while (p < q) {
n = read(commfd[0], p, (size_t)(q - p));
if (n > (ssize_t)0)
p += n;
else
if (n != (ssize_t)-1)
break; /* n == 0 is pipe closed */
else
if (errno != EINTR)
break;
}
/* Close the read/parent end of the pipe. */
closefd(commfd[0]);
/* Pipe closed (on exec), no data read? */
if (n == (ssize_t)0 && p == (char *)&errcode) {
/* Yes, success! */
errno = 0;
return child;
}
/* Execution failed.
* If we didn't get the reason, use EINVAL. */
if (!errcode || p != q)
errcode = EINVAL;
}
/* Reap the child process. */
do {
p = waitpid(child, NULL, 0);
if (p == (pid_t)-1) {
if (errno == EINTR)
continue;
else
break;
}
} while (p != child);
/* Return with failure. */
errno = errcode;
return (pid_t)-1;
}
The only downside to this approach, in my opinion, is the extra two descriptors used in the parent process, albeit only temporarily. In almost all cases this is irrelevant, but if you have a server-type application that uses a lot of file descriptors, this is something you should be aware of.
The Phidgets library uses threads. The thread that executes the callbacks is different than the one that say, waits for the keypress in the RFID Phidgets example. One option would be to use posix_spawn() to execute the player (from a non-main thread).
However, in general, it is better to have the main thread monitor both the player using waitpid(child, &status, WNOHANG) to check if the player has exited, and to handle any new RFID events, launching the player as needed (killing an existing instance if new RFID), and even killing the player if the RFID is moved outside the reader range.
This requires a simple threaded event queue:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
/* RFID tag event types: tag read, tag lost.
*/
typedef enum {
RFID_TAG_LOST = 0,
RFID_TAG_READ
} rfid_event_type_t;
/* Structure describing all possible RFID tag events.
*/
typedef struct rfid_event_st rfid_event_t;
struct rfid_event_st {
struct rfid_event_st *next;
rfid_event_type_t type;
CPhidgetRFIDHandle rfid;
CPhidgetRFID_Protocol protocol;
void *userptr;
char tag[];
};
static pthread_mutex_t event_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t event_wait = PTHREAD_COND_INITIALIZER;
static rfid_event_t *event_queue = NULL;
/* Add event to event queue.
*/
static int add_event(const CPhidgetRFIDHandle rfid,
const CPhidgetRFID_Protocol protocol,
const rfid_event_type_t type,
const char *const tag,
void *const userptr)
{
const size_t taglen = (tag) ? strlen(tag) : 0;
rfid_event_t *ev;
/* Allocate memory for a new event. */
ev = malloc(sizeof (rfid_event_t) + taglen + 1);
if (!ev)
return errno = ENOMEM;
/* Fill in the fields. */
ev->next = NULL;
ev->type = type;
ev->rfid = rfid;
ev->protocol = protocol;
ev->userptr = userptr;
if (taglen > 0)
memcpy(ev->tag, tag, taglen);
ev->tag[taglen] = '\0';
/* Lock event queue. */
pthread_mutex_lock(&event_lock);
/* Append to the event queue. */
if (event_queue) {
rfid_event_t *prev = event_queue;
while (prev->next)
prev = prev->next;
prev->next = ev;
} else
event_queue = ev;
/* Signal and unlock. */
pthread_cond_signal(&event_wait);
pthread_mutex_unlock(&event_lock);
return 0;
}
/* Get next event, waiting at most 'maxwait' seconds.
*/
static rfid_event_t *get_event(const long maxwait)
{
struct timespec until;
rfid_event_t *ev;
pthread_mutex_lock(&event_lock);
/* Event already in the queue? */
if (event_queue) {
ev = event_queue;
event_queue = ev->next;
ev->next = NULL;
pthread_mutex_unlock(&event_lock);
return ev;
}
/* No waiting requested? */
if (maxwait <= 0L) {
pthread_mutex_unlock(&event_lock);
return NULL;
}
/* Get current wall clock time, */
clock_gettime(CLOCK_REALTIME, &until);
/* and add maxwait seconds. */
until.tv_sec += maxwait;
/* Wait for a signal. */
pthread_cond_timedwait(&event_wait, &event_lock, &until);
/* Event arrived in the queue? */
if (event_queue) {
ev = event_queue;
event_queue = ev->next;
ev->next = NULL;
pthread_mutex_unlock(&event_lock);
return ev;
}
/* No event; timed out. */
pthread_mutex_unlock(&event_lock);
return NULL;
}
As per the Phidgets RFID example, the tag and tag lost handlers are
int CCONV TagHandler(CPhidgetRFIDHandle RFID, void *usrptr, char *TagVal, CPhidgetRFID_Protocol proto)
{
return add_event(RFID, proto, RFID_TAG_READ, TagVal, usrptr);
}
int CCONV TagLostHandler(CPhidgetRFIDHandle RFID, void *usrptr, char *TagVal, CPhidgetRFID_Protocol proto)
{
return add_event(RFID, proto, RFID_TAG_LOST, TagVal, usrptr);
}
Instead of waiting for a keypress after everything has been set up, you create a loop, something like
pid_t child = (pid_t)-1; /* Not running */
pid_t p;
rfid_event_t *event;
/* Infinite loop */
while (1) {
/* Do we have a player child process? */
if (child != (pid_t)-1) {
/* Yes. Has it exited yet? */
p = waitpid(child, NULL, WNOHANG);
if (p == child) {
/* Yes. No more player. */
child == (pid_t)-1;
}
}
/* Check for a new event.
* If we have a child, only wait one second only
* for the event; otherwise, wait up to 30 secs.
*/
if (child == (pid_t)-1)
event = get_event(30L);
else
event = get_event(1L);
/* If no event yet, start at the beginning of the loop. */
if (!event)
continue;
/*
* TODO: Handle the event.
* You can stop the existing player via e.g.
* if (child != (pid_t)-1)
* kill(child, SIGKILL);
* and then start a new one.
*/
/* Discard the event. It's dynamically allocated. */
free(event);
}
If you start the player, the above loop detects it is not playing within a second. If there is no player running, then it's okay for the loop to wait for an RFID signal for as long as it wants -- I used 30 seconds.
I am attempting to follow a tutorial which asks me to edit example code to get a program to run two processes which take turns to output the lyrics to a song ('There's a hole in the bucket').
My problem is that the file gets outputted as a whole and not alternativley like it should see screen shot for what i am talking about : http://imgur.com/NusvhVA
My code is below. Thanks.
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#define KEY 87654 //Unique semaphore key
int main()
{
int id; /* Number by which the semaphore is known within a program */
FILE *file;
file = fopen("207song.txt", "r" );
int c;
union semun {
int val;
struct semid_ds *buf;
ushort * array;
} argument;
argument.val = 1;
/* Create the semaphore with external key KEY if it doesn't already
exists. Give permissions to the world. */
id = semget(KEY, 1, 0666 | IPC_CREAT);
/* Always check system returns. */
if(id < 0) {
fprintf(stderr, "Unable to obtain semaphore.\n");
exit(0);
}
/* What we actually get is an array of semaphores. The second
argument to semget() was the array dimension - in our case
1. */
/* Set the value of the number 0 semaphore in semaphore array
# id to the value 0. */
if( semctl(id, 0, SETVAL, argument) < 0) {
fprintf( stderr, "Cannot set semaphore value.\n");
} else {
fprintf(stderr, "Semaphore %d initialized.\n", KEY);
}
int pid=fork();
const int HENRY_DONE = 0;
const int LIZA_DONE = 1;
volatile int flag = HENRY_DONE;
if(pid) {
struct sembuf operations[1];
int retval; /* Return value from semop() */
/* Get the index for the semaphore with external name KEY. */
id = semget(KEY, 1, 0666);
if(id < 0){
/* Semaphore does not exist. */
fprintf(stderr, "Program sema cannot find semaphore, exiting.\n");
exit(0);
}
operations[0].sem_num = 0;
/* Which operation? Subtract 1 from semaphore value : */
operations[0].sem_op = -1;
/* Set the flag so we will wait : */
operations[0].sem_flg = 0;
while(1){
//Process 1
//wait
operations[0].sem_op = -1;
retval = semop(id, operations, 1);
//critical section
printf("Liza's Part: \n");
fflush(stdout);
sleep(1);
while ((c = getc(file)) !=EOF)
if (c == "\n") {
putchar(c);
break;
}
else
putchar(c);
fflush(stdout);
operations[0].sem_op = 1;
//signal
retval = semop(id, operations, 1);
}
}else{
//Process 2
struct sembuf operations[1];
int retval; /* Return value from semop() */
/* Get the index for the semaphore with external name KEY. */
id = semget(KEY, 1, 0666);
if(id < 0){
/* Semaphore does not exist. */
fprintf(stderr, "Program sema cannot find semaphore, exiting.\n");
exit(0);
}
operations[0].sem_num = 0;
/* Which operation? Subtract 1 from semaphore value : */
operations[0].sem_op = -1;
/* Set the flag so we will wait : */
operations[0].sem_flg = 0;
while(1){
//wait
operations[0].sem_op = -1;
retval = semop(id, operations, 1);
//critical section
printf("Henry's Part: \n");
fflush(stdout);
sleep(1);
while ((c = getc(file)) !=EOF)
if (c == "\n") {
putchar(c);
break;
}
else
putchar(c);
fflush(stdout);
//signal
operations[0].sem_op = 1;
retval = semop(id, operations, 1);
}
}
}
If your while loop you have:
while ((c = getc(file)) !=EOF)
if (c == "\n") {
getc returns an integer, "\n" is a c-string of type char*. That
comparison will not match, leading the first comsumer to show the whole file.
You probably want
c == '\n'
note the single quotes 'rather than double " The single quote will be a char, which will compare reasonably with an int.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define __USE_GNU
#include <sched.h>
void init_lock(struct flock *f)
{
f->l_type = F_WRLCK; /* write lock set */
f->l_whence = SEEK_SET;
f->l_start = 0;
f->l_len = 0;
f->l_pid = getpid();
}
int lock(int fd, struct flock *f)
{
init_lock(f);
if(fcntl(fd, F_SETLKW, f) == -1) {
fprintf(stderr,"fcntl() failed: %s\n", strerror(errno));
return -1;
}
return 0;
}
int unlock(int fd, struct flock *f)
{
f->l_type = F_UNLCK;
if(fcntl(fd, F_SETLK, f) == -1) {
fprintf(stderr, "fcntl() failed: %s\n", strerror(errno));
return -1;
}
return 0;
}
int file_op(void *arg)
{
char buff[256];
int fd = (int) arg, n;
struct flock my_lock;
printf("Trying to get lock\n");
if(lock(fd, &my_lock) == -1) { /* lock acquired by a thread */
return -1;
}
printf("Got lock: %d\n", getpid()); /* I am printing thread id after lock() */
printf("Enter string to write in file : ");
scanf("%s", buff);
if((n=write(fd, &buff, strlen(buff))) == -1) {
fprintf(stderr, "write() failed: %s\n", strerror(errno));
}
if(unlock(fd, &my_lock) == -1) {
return -1;
}
printf("Lock Released: %d\n", getpid());
return 0;
}
int main()
{
char *stack;
int fd, i=0, cid, stacksize;
if((fd = open("sample.txt", O_CREAT | O_WRONLY | O_APPEND, 0644)) == -1) {
printf("Error in file opening\n");
exit(1);
}
stacksize = 3*1024*1024;
for(i=0; i<5; i++) {
stack = malloc(stacksize);
if((cid = clone(&file_op, stack + stacksize, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD, (void *) fd)) == -1) {
fprintf(stderr,"clone() failed: %s\n", strerror(errno));
break;
}
}
sleep(30);
close(fd);
return 0;
}
I want that every clone() will wait for lock.
But Output of this code (something like this):
Trying to get lock
Trying to get lock
Trying to get lock
Got lock: Got lock: 10287
Got lock: Got lock: 10287
Enter string to write in file : Trying to get lock
Enter string to wriGot lock: 10287
Got lock: 10287
Got lock: 10287
Enter string to write in file : Trying to get lock
Got lock: 10287
Got lock: Enter string to write in file :
But when i am removing CLONE_FILES field set from clone(2), it goes all well. Other clone threads will wait for a lock().
Output of that:
Trying to get lock
Got lock: 10311
Trying to get lock
Trying to get lock
Trying to get lock
Trying to get lock
Any other alternatives (with CLONE_FILES)? And Why this kind of behavior?
Beginner in this field.
The locking provided by flock is per process, not per thread.
From http://linux.die.net/man/2/flock (emphasis mine):
A call to flock() may block if an incompatible lock is held by another process.
Subsequent flock() calls on an already locked file will convert an existing lock to the new lock mode.
Locks created by flock() are associated with an open file table entry.
Although threads are not explicitly mentioned multiple threads share a file table entry whereas multiple processes do not. Passing CLONE_FILES to clone causes your 'processes' to share file tables.
A solution might be to call dup to make more file descriptors. From the documentation:
If a process uses open(2) (or similar) to obtain more than one descriptor for the same
file, these descriptors are treated independently by flock(). An attempt to lock the file
using one of these file descriptors may be denied by a lock that the calling process has
already placed via another descriptor.