Link to home
Start Free TrialLog in
Avatar of ljaques
ljaques

asked on

Getting PROCESS ID and LOCKING a file??

I have used Perl for some time now allowing my C skills to wither away.  Perl has given me options like
getting the presently launched perl program's process id with the constant variable $$.  This gives me a unique number which I can identify this launched program as (unique from the many that can be launched at one time on a multitasking OS).  Perl also allows me to use a command called FLOCK which will lock a file from being written over by someone else giving exclusive rights to whoever.  

Those commands are great but I need them for C. I am translating my Perl code and really need to know how to get the PROCESS ID for my launched compiled C program. I also need to LOCK a file so that the launched program can have exclusive rights to a file.  And all this in ANSI-C because I want it to work on any platform (Unix/NT) that I compile it for.  

Can you please help me identify these commands in ANSI-C?  Or possibly give me alternative methods to use.  Thank you VERY much!!
Avatar of ozo
ozo
Flag of United States of America image

getpid and fcntl are in the POSIX standard, but not the ANSI-C standard,
since that does not even require a hosted environment.
Many Unix environments will also support flock and/or lockf,
But if you want to be as portable as Perl, you may need to do the same conditional compilation that perl does.
Avatar of ljaques
ljaques

ASKER

Ozo, you mentioned using GETPID and FCNTL but these are only commands that I've discovered under Perl.  Is there any command I can use under C right now without any problems to get the Process ID and/or lock a file?

Essentially all I need to do is just establish exclusive rights to a file. So if another process of my ADS.EXE is running, I want the new process to sit by waiting for the previous to finish. For me to do this I realize I must make a signature file that contains the process ID of the launched ADS.EXE that has the rights now; the first ADS.EXE that is launched will look for the signature filename ADS.TMP, and won't find it.  It will move on and create ADS.TMP writing in its process ID of lets say 1234.  Then it rechecks what it wrote into ADS.TMP and compares to its process ID.  If they equal, it has exclusive right right now.  If a second process has launched it would have checked for ADS.TMP and found it.  It waits until ADS.TMP disaapears.  Once the laucnehd ADS.EXE that has rights is completed it removeds ADS.TMP allowing another to establish rights.

Hope this all makes sense.  Is this the best way to do file locking for ANSI C?  And is there a way to get that unique process ID (or any unique # for that matter)?

Thanks a million!!
I believe that the only way to do this in C ( at least on many unix machines) is to use getpid and lockf.  
The following may be available:

Low level (file handles):
    locking() - lock areas of a file
    sopen() - open a file for sharing

Translation between low-level and streams:
    fdopen() - associate stream with handle to open file

Process handling:
    getpid() - get process ID number


I dont't think you need file locking.. You just check for the existence of a file the block until it disappers. Do you intend to change the contents of an existing ADS.TMP?
If more than one processes check for the existence of a file and block until it disappers, you still need to be sure that only one acquires the lock when it does.
This can be tricky without flock, lockf, fcntl, or some other system locking function.
Opening the file with O_EXCL or O_APPEND may help on some operating systems,
But if your OS doesn't even have getpid, then I'm not sure what open flags you can count on either.
Avatar of ljaques

ASKER

Ozo, would you then say my method of waiting for the AD.TMP file to disappear, writing a new AD.TMP file with a unique #, then reading in the AD.TMP file and comparing its # with my unique # for a match and thus establishing that this instance of AD.EXE created AD.TMP, is flawed?  Or will it work?

Does that make sense?   :>   Or do I still have to figure out the FLOCK method for ANSI C?

Thanks everyone so far for the most enlightening talk.
It could be flawed, depending on how you code it, and what operating system you're on.

One potential flaw could show up if two processes similtaneously see AD.TMP disappear and decide to write a new file, and one process writes its pid and reads it, then the other process overwrites the file with its pid and reads it.

Any of the various interlock mechanisms mentioned above might prevent that,
(as might a more complicated handshaking protocol without atomic locks)

Many systems have a flock function in their system library, but it is not part of ANSI C
fopen("AD.TMP","a+") is part of the ANSI C standard, and on many system will guarantee that you won't overwrite anything that another process might write to the same file, but it doesn't look like the ANSI C standard itself says that explicitly.
(it doesn't even guarantee that "AD.TMP" will be a valid file name on your operating system)

Whatever you do, you should check the documentation for each system to which you will be porting to make sure that your code will function properly there,
and if not, you may have to add some OS specific code under a compile conditional

Avatar of ljaques

ASKER

Ozo, what if I use FOPEN("AD.TMP","a+"), as you suggested, for my locking this way if simultaneous calls checking for the existence of AD.TMP occurs one will create the nonexistant file and write in its data and the other will just append to it.  Then I will slap in an FSEEK(0,0) to set the file pointer to the beginning of the file and read in the data just written.  This data will be compared to a constant in the calling program.  If they match we have rights to AD.TMP; locking succeeded for the program while the other will get  improper, unmatching data and will sit and wait till AD.TMP is removed.

Is this better?  Sorry about all the questions, it's just I really need to get this problem solved.

The unqiue # I was going to write into AD.TMP was going to be the address of a variable in the program: ie. CHAR *myID.  Simulataneous calls of my ADS.EXE file will create CHAR *myID at different memory addresses, right?  Is this a good alternative to GETPID()?  

The only reason I'm desperately looking for alternatives to LOCK and GETPID is because I want to "make sure" my C program is compatible with UNIX (whatever the machine) and WINDOWS NT.  I know in a few months I'm going to forget all my C knowledge and I'd hate to tackle my C program then.  

One more thing, I just played with the FOPEN("AD.TMP","a+") under the console window of Windows 95. I launched 2 MS-DOS windows and had them both run the same file that had the FOPEN command.  One of them said "SHARING VIOLATION, retry, abort, fail?". How annyoing.  I hope this doesn't pop up on Windows NT server causing any problems.  Does this occur because I'm on Windows 95 and running thru the DOS window?

Thanks a billion...Again!

I think that has a better chance, since many system promise that with "a" it is impossible to overwrite information already in the file.
But since ANCI C is less explicit about that, it may still depend on what platform you do this on.
Also, even if no writes are ever lost, there may still be a possibility of one write of "12345" and another of "6789" coming out as "162738495", depending on how writes are buffered.
I would probably prefer open("AD.TMP",O_CREAT|O_EXCL) on systems that support it.

char *myID; is not a good alternative to getpid() since simultaneous calls may use identical addresses if the process space is remapped.
getpid() is probably the best alternative on most unix or unixlike systems.
 
I can't help you much with your Windows 95 issues.
You may be able to find more specific help in a Windows 95 topic area.
Avatar of ljaques

ASKER

Ozo, just a few more questions.

As ytou know I'm putting together an AD.EXE file which will be called by my web page to rotate the banners I have on my web site.  I am programming it in C as opposed to Perl because I'm hoping to reduce the load on the server and improve performance.  Is this a smart approach or should I just stop where I am in programming the AD.EXE and just go back to making it for the much simpler, more portable Perl?  That is, am I really reducing that much server stress and getting significant performance increases with C over Perl or not that much?

And, heres a strange question.  I am reading in my binary graphic file (.jpg).  I am using FREAD to read in a chunk of data (about 7kb at a time) and then trying to output this chunk to STDOUT for CGI purposes, all this to get a visual image displayed on the client's browser.  For some reason I can't display the 7kb chunk to STDOUT.  If I use PRINTF I have no idea on how to FORMAT this raw binary data (printf("%s",IMG_DATA); ?).  All I want to do is to output this raw data to STDOUT.  And also, I don't know if I'm making a mistake or something but somethimes the reading doesn't read all of the imgae file; it's as if it encountered an EOF and stopped prematurely.  What the heck?

I hope you undertsood what I said above.  And thank you and EVERYONE for being so "imformative".  My deepest appreciations!
Well, C can get significant performance increases over Perl in applications that do a lot of number crunching, but for more I/O intensive applications there may be little to be gained.
Load time could be significant for small programs, but there are other ways around that too, like using a server that keeps Perl resident.
On the other hand, that could reduce your portability if you don't have control over the server.
But another way to make your C code as portable as Perl could be just to look at the Perl source to see how it does it on different systems.

printf("%s",IMG_DATA) will stop at the first null byte in *IMG_DATA
You probably want to use fwrite instead.  
Also, on some systems you may need to open the file in "binary" mode, to prevent it from treating certain characters as End Of File, or limiting the length of lines, or what ever other mangling the OS does when it thinks a file is "text"
Avatar of ljaques

ASKER

Ozo, just one more.

I am seriously considering going back to Perl.  The FLOCK command you've suggested. It doesn't appear to function under my Windows 95 DOS window console ( I am using Perl from www.ActiveState.com for Win95).  Anyway, if I use FLOCK for Windows NT and Unix in my Perl code does this mean that when use FOPEN I will get back an error that it can't open the file since it has been locked?  Or does lock only kick in when you try to read/write to the open file?

Ie.  if (open(IN,"+<mydata.dat"))
     {
         flock(IN,2);  #Lock this file

         #Work with this file data here

         flock((IN,8);  #When done...Unlock the file
         close(IN);
    }

If multiple instances of this .CGI file is called will the IF(OPEN...) line return 0 becasue the file is already locked?  And if so then I must put an ELSE and tell it to check for the existence of the file, and if it exists "retry" the open over and over again.

Is this correct?

Thank you OZO!!
Are you saying perl isn't implementing flock correctly on Windows NT?
(If so, it may not do you any good to copy the my_flock routine from win32io.c to use in your C program)
But if flock is properly following the unix standard, it should have no effect on any open's read's or write's
The only thing it should do is cause another flock(F,LOCK_EX) to wailt until the lock is released.
Or, if you want to return immediately with an error, set LOCK_NB|LOCK_EX

By the way, if your Perl is earlier than 5.004, you should be careful about unlocking the file before closing.
If you have any writes still buffered, the unlock may allow others access to the file before the close flushes the buffer.
Avatar of ljaques

ASKER

Ok OZO here's my LOCking procedure.  Does it look acceptable or did I make a booboo. I call with LOCKME("L","A","ads/adhld.dat") to LOCK a temp file and LOCKME("U","A","ads/adhld.dat") to unlock the temp file.  Does it look decent and maybe you have some code I can check out (in Perl and/or C).  Thanks again....

sub LOCKME
{
  my($who,$type,$file)=@_;
  my($res);

  if ($who eq "L")
  {
     if ($type eq "A") #Lock ADCOUNT.TXT for the particular area
     {
       open(LIN,">ads/$file/adhld.dat");
       $res=eval("flock(LIN,$LOCK_EX)");
       if ($@ eq "" && $res){$LOCKED_AD=1;}
       else {close(LIN); unlink("ads/$file/adhld.dat");}
     }
     else
     {
       open(LINC,">ads/$file");
       $res=eval("flock(LINC,$LOCK_EX)");
       if ($@ eq "" && $res){$LOCKED_CL=1;}
       else {close(LINC); unlink("ads/$file");}
     }
  }
  else
  {
    if ($type eq "A")
    {
      if ($LOCKED_AD)
      {flock(LIN,$LOCK_UN); close(LIN); unlink("ads/$file/adhld.dat"); $LOCKED_AD=0;}
    }
    else
    {
      if ($LOCKED_CL)
      {flock(LINC,$LOCK_UN); close(LINC); unlink("ads/$file"); $LOCKED_CL=0;}
    }
  }
}

That doesn't look much like C, but I guess it could work.
I can think of a few of things that could go wrong though...
What if the open fails?
How do you define $LOCK_EX and $LOCK_UN?
(and  why do you use eval?  do you do anything with the value of $@ if it's not blank?)
What if you call
LOCKME('L','',"foo"); LOCKME('L','',"bar"); LOCKME('','',"foo");
Avatar of ljaques

ASKER

Ozo,
Yeah I've gone back to Perl for my programming of the AD.CGI code.

I believe $LOCK_EX=2  and $LOCK_UN=8

and I use EVAL because on my computer (486 Windows 95 in the DOS Windows console) the FLOCK command doesn't function so I use EVAL to tell me that FLOCK won't run on my machine instead of it reporting a default "visual" error message.  If the line is run on a computer that can support FLOCK it will run just like normal (FLOCK will work).  I hope this is what it does.  If an error occurs $@ will ne "" and I just leave, this tells me that FLOCK couldn't run on the computer since FLOCK wasn't supported.  If it was on the web server (NT or UNIX) it should work...I hope.

And when I call LOCKME("L","foo"), etc. the foo part is only used as a path to a drawer.  It will always be set with the proper path name. Unless foo and bar are something special?

If the open fails, what should I do?  Should i just "sleep" until it doesn't fail and then try to lock or just report back and error and exit the AD.CGI altogether and right away.  BTW when exactly does a write/read fail (it there is plenty of disk space for writing and the file exists for reading)?

Thanks again Ozo.  It baffles me how you can hold such vasts amounts of data in your skull. I feel so small.  Your head must throb late at night!  :>

Oh, did you say you had some programming code (Perl or C) that demonstartes "correct" locking to me?
ASKER CERTIFIED SOLUTION
Avatar of sganta
sganta

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
That can work in many, but not all, operating systems.
It seems careless not to check the success of the open, but then, that's not much worse than the proposed Perl procedure.
And if the perl implementation is having trouble with the eval failing on some platforms, then neither has a clear portability advantage either.
(But what's the verion of your perl port on that platform?  Have you checked to see if the latest version adresses that problem?)
Hi

In my answer 2nd line after main() some spelling mistake is there. Please read it as
fd = open("locktest",O_RDWR);