My initiative here is to make a detection mechanism on process status by a "file lock".
Say I am developing a program in user mode. It will be inappropriate for some sources to be shared within multiple processes of the program(that is, multiple instances of the same executable? I am not very sure about the terminology here), so I am thinking of creating a tmp file to signify the program is currently running.
Let's take output directory as an example, say if I don't want the processes to compete for log file names, etc, I would like to try to create a tmp file, if the creation success, make the file unremovable during the process lifetime so as to signify this process's "ownership" for this output directory. If the creation failed due to the file existing, then try to delete the file, and:
I wonder:
Is the "Make file unchangeable while the process creating it is running while removable if the process has been terminated" somehow achievable? By "terminated" I am any kind of way, SIGKILL, SIGTERM, normal exit, etc.
Should I worry about inter-process-race? what bad could happen if say one process is wrapping up but hasn'y yet fully released while others are trying to create the tmp file? ( possible "misdetection" is tolerable, by "bad" I mean dead-lock, resource leakage, etc. )
Here is a simple solution to ensure only a single process accesses some resources:
s
symlink
, attempt to create a symbolic link with a fixed name in the "/tmp" directory (eg: my_resource_locked_by
) using s
as the target file.errno
:
EEXIST
, report the error and abortreadlink
to get the pid xxx
of the process that has locked the resource:
readlink
fails:
errno == ENOENT
(symlink file was removed) try again step 2stat
on /proc/xxx
where xxx
is the pid (attn: readlink
does not null terminate the symlink contents):unlink
, check for failure and report the reason, otherwise retry step 2.unlink
as soon as exclusive access is no longer required.Here is a simple implementation (error reporting delegated to the caller):
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
// try and acquire a named resource (eg: "/tmp/mylog.lock")
// return 0 if resource locked
// return 1 if resource locked by another process (pid in *pid)
// return <0 and set *err_code to error code in case of other errors
int acquire_resource(const char *name, int *err_code, pid_t *pid) {
char pid_buf[32];
char proc_name[6 + 32] = "/proc/";
struct stat sb;
*pid = 0;
snprintf(pid_buf, sizeof pid_buf, "%ld", (long)getpid());
for (int n = 0; n < 10; n++) {
if (!symlink(name, pid_buf))
return 0;
if (errno != EEXIST) {
*err_code = errno;
return -1;
}
ssize_t linklen = readlink(name, proc_name + 6, sizeof(proc_name) - 6 - 1);
if (linklen < 0) {
if (errno == ENOENT)
continue;
*err_code = errno;
return -2;
}
proc_name[6 + linklen] = '\0';
*pid = strtol(proc_name + 6, NULL, 10);
if (!stat(proc_name, &sb)
return 1;
if (unlink(name) && errno != ENOENT) {
*err_code = errno;
return -3;
}
}
return -4;
}
int release_resource(const char *name) {
return unlink(name);
}