Linux system call to retrieve process status

N

Nick Birnie

Hi all,

Is it possible to retrieve some parts of task_struct from user space.
In particular I would like access to the process state, that is to say,
TASK_RUNNING or TASK_STOPPED, as defined here:

http://www.ibm.com/developerworks/linux/library/l-task-killable/index.html

Looking at /usr/include/linux/sched.h, it appears that another version
might be being used by the kernel, since none of the defines that I
require appear in /usr/include/linux/sched.h.

Probably this would be quite easy to write as a kernel module, but does
anyone know of a system call that gets the job done?

Many thanks,

Nick
 
F

Flash Gordon

Nick said:
Hi all,

Is it possible to retrieve some parts of task_struct from user space.
In particular I would like access to the process state, that is to say,
TASK_RUNNING or TASK_STOPPED, as defined here:

http://www.ibm.com/developerworks/linux/library/l-task-killable/index.html

This is very much a Linux specific problem (unliss Posix provides a
method to do it). The comp.unix.programmer people will tell you if there
is a Unix solution, but if not you will need to ask in one of the Linux
groups.
Looking at /usr/include/linux/sched.h, it appears that another version
might be being used by the kernel, since none of the defines that I
require appear in /usr/include/linux/sched.h.

I don't think the headers in /usr/include/linux are intended for use
directly by user level code. A Linux person can confirm this.
Probably this would be quite easy to write as a kernel module, but does
anyone know of a system call that gets the job done?


Standard C definitely doesn't, so you have to hope there is a Posix of
Linux specific solution.

Follow-ups set to comp.unix.programmer where the Unix/Posix answer will
be topical.
 
S

Scott Lurndal

Nick Birnie said:
Hi all,

Is it possible to retrieve some parts of task_struct from user space.
In particular I would like access to the process state, that is to say,
TASK_RUNNING or TASK_STOPPED, as defined here:

http://www.ibm.com/developerworks/linux/library/l-task-killable/index.html

Looking at /usr/include/linux/sched.h, it appears that another version
might be being used by the kernel, since none of the defines that I
require appear in /usr/include/linux/sched.h.

Probably this would be quite easy to write as a kernel module, but does
anyone know of a system call that gets the job done?

Many thanks,

that state for a process is avaliable via /proc/<pid>/stat[us].

$ cat /proc/$$/status
Name: ksh
State: R (running)
SleepAVG: 98%
Tgid: 9779
Pid: 9779
PPid: 9775
TracerPid: 0
Uid: 500 500 500 500
Gid: 500 500 500 500
FDSize: 64
Groups: 500 10718 10719 10720
VmPeak: 70784 kB
VmSize: 70728 kB
VmLck: 0 kB
VmHWM: 2476 kB
VmRSS: 1332 kB
VmData: 608 kB
VmStk: 84 kB
VmExe: 1332 kB
VmLib: 2092 kB
VmPTE: 72 kB
StaBrk: 00758000 kB
Brk: 007e0000 kB
StaStk: 7fffc36241f0 kB
Threads: 1
SigQ: 1/39936
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000020300000
SigCgt: 000000005fcb7aff
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
Cpus_allowed: 00000000,0000000f
Mems_allowed: 00000000,00000001
 
N

Nick Birnie

Scott said:
Nick Birnie said:
Hi all,

Is it possible to retrieve some parts of task_struct from user space.
In particular I would like access to the process state, that is to say,
TASK_RUNNING or TASK_STOPPED, as defined here:

http://www.ibm.com/developerworks/linux/library/l-task-killable/index.html

Looking at /usr/include/linux/sched.h, it appears that another version
might be being used by the kernel, since none of the defines that I
require appear in /usr/include/linux/sched.h.

Probably this would be quite easy to write as a kernel module, but does
anyone know of a system call that gets the job done?

Many thanks,

that state for a process is avaliable via /proc/<pid>/stat[us].

Hi,

Thanks for your reply. Yes indeed this provides a satisfactory solution
to the problem. Better still I would also expect that reading /proc is
provably faster using a system call. The only downside to this is a
little parser function, I have pasted it below for future reference.

Many thanks,

Nick

=======================================================================

thread_status_t
process_state(int pid) {
#define BUFLEN 64
#define LINUM 2
char buffer[BUFLEN], state = 0;
FILE *proc_fs_p;
int coln_count = LINUM, tmp = 0;
if (snprintf(buffer, BUFLEN, "/proc/%d/status", pid) == BUFLEN)
fatal_error("buffer overflow detected");
proc_fs_p = fopen(buffer , "r");
if (!proc_fs_p)
syserr("/proc open failed");
#define CONSUME (tmp = fgetc(proc_fs_p))
while ((CONSUME) >= 0) {
if (((char) tmp) == ':'){
if (--coln_count == 0) {
while (CONSUME != EOF && (((char) tmp) == ' '));
state = (char) tmp;
break;
}
}
}
fclose(proc_fs_p);
switch (state){
case 'R': //fall through
case 'W': //fall through
case 'S': return TS_RUNNING;
case 'T': return TS_SLEEP;
case 'Z': return TS_FINNISHED;
default:
info("unable to determine process state");
return TS_ERR;
}
return TS_ERR; // unreachable
}
 
B

Barry Margolin

Nick Birnie said:
Thanks for your reply. Yes indeed this provides a satisfactory solution
to the problem. Better still I would also expect that reading /proc is
provably faster using a system call.

read() is a system call.

A system call that returned the information as a structure would
probably be faster than /proc, since it wouldn't have to format it, and
you wouldn't have to parse it. Before /proc was invented, this was
typically done by reading, and you simply read the process's u-block
into a struct. The problem with this was that programs needed to be
setuid or setgid to be able to read this file, whereas /proc implements
fine-grained access control.
 
S

Scott Lurndal

Barry Margolin said:
read() is a system call.

A system call that returned the information as a structure would
probably be faster than /proc, since it wouldn't have to format it, and
you wouldn't have to parse it. Before /proc was invented, this was
typically done by reading, and you simply read the process's u-block
into a struct. The problem with this was that programs needed to be
setuid or setgid to be able to read this file, whereas /proc implements
fine-grained access control.

An additional problem with reading the u block or proc struct from
/dev/kmem is that the format changes as the kernel evolves requiring
the application to change as well.

SVR4 /proc provided the data in binary form, unlike linux which provides
it in UTF-8.

scott
 
C

CBFalconer

Scott said:
.... snip ...

An additional problem with reading the u block or proc struct
from /dev/kmem is that the format changes as the kernel evolves
requiring the application to change as well.

Linux is not part of standard C, and neither is any 'read'
function. Thus this is off-topic on c.l.c. Rerouted to
comp.unix.programmer.
 
N

Nick Birnie

Han said:
That doesn't work on any version of glibc of which I'm aware.

Older glibc returned -1 upon attempted overflow.

Newer glibc follows the C99 definition of snprintf(): The return
value will be >= BUFLEN upon attempted overflow, depending
on the size of the attempted overflow. In effect, you're calling
fatal_error() only in the case of an attempted *one-byte* overflow.


Yours,
Han from China

Thanks for your input. This is quire correct. May I ask, do you own a
hard copy of the C standard? I am going to buy a copy of a C text soon
as my existing text is very much a textbook. Would I be well advised to
go for Kernighan & Ritchie's circa 1989 "The C Programming Lanaguage" as
opposed to a copy of the C99 standard?

Cheers,

Nick

Attached updated proc fs parser follows.

thread_status_t
process_state(int pid) {
#define BUFLEN 64
#define LINUM 2
char buffer[BUFLEN], state = -1;
FILE *proc_fs_p;
int coln_count = 1, tmp = 0;
if (snprintf(buffer, BUFLEN, "/proc/%d/status", pid) >= BUFLEN)
fatal_error("buffer overflow detected");
proc_fs_p = fopen(buffer , "r");
if (!proc_fs_p)
syserr("/proc open failed");puts("state found");
#define CONSUME (fgets(buffer, BUFLEN, proc_fs_p))
while (CONSUME != NULL){
if (++tmp == LINUM) {
for (int i = 0; i < BUFLEN; i++){
if (buffer == ':')
coln_count--;
else if (!coln_count && !isblank(buffer)){
state = buffer;
break;
}
}
break;
}
}
fclose(proc_fs_p);
switch (state){
case 'R': //fall through
case 'W': //fall through
case 'S': return TS_RUNNING;
case 'T': return TS_SLEEP;
case 'Z': return TS_FINNISHED;
default:
info("unable to determine process state");
return TS_ERR;
}
return TS_ERR; // unreachable
#undef BUFLEN
#undef LINUM
#undef CONSUME
}
 
F

Flash Gordon

Nick Birnie wrote:

Thanks for your input. This is quire correct. May I ask, do you own a
hard copy of the C standard? I am going to buy a copy of a C text soon
as my existing text is very much a textbook. Would I be well advised to
go for Kernighan & Ritchie's circa 1989 "The C Programming Lanaguage" as
opposed to a copy of the C99 standard?

You can get drafts of the C standards for free, one of which is C99 with
all three approved TCs. See http://clc-wiki.net/wiki/c_standard for
appropriate links.

The C standard is not designed as a programmers reference. Personally I
like K&R2, but there are other reference books people here recommend as
well. A quick search should find them.
Attached updated proc fs parser follows.

thread_status_t
process_state(int pid) {
#define BUFLEN 64
#define LINUM 2

As you are trying to do these with a limited scope, and they are small
integer values, you could use the enum "trick" which automatically
scopes them.

enum { BUFLEN=64, LINUM=2 };

Also, using values from limits.h and sizeof you can calculate the
maximum buffer size at compile time and so not have to worry about
buffer overflow. The number of bits in an int is CHAR_BIT * sizeof(int).
Now, some of those could be padding bits, so the number of value bits
could be smaller, but not larger. 3 bits can represent a number from 0
to 7. So a slight over-estimate (unless there are lots of padding bits)
of the number of decimal digits is (CHAR_BIT * sizeof(int)) / 3. Then
just add on space for the rest of the string (including the nul
termination character) and you have a buffer guaranteed to be large
enough, and probably only a little larger than required.
char buffer[BUFLEN], state = -1;

char could be unsigned! Personally I would use
state = 0
since that is also not a "valid" state for your usage, and sidesteps the
issuf of char being unsigned nicely. Note that gcc has an option to
select this behaviour, so on Linux you cannot guarantee that char is
signed when someone else compiles your code.
FILE *proc_fs_p;
int coln_count = 1, tmp = 0;
if (snprintf(buffer, BUFLEN, "/proc/%d/status", pid) >= BUFLEN)
fatal_error("buffer overflow detected");

Having created a buffer definitely large enough you can then use sprintf
with safety and additional portability (to older Linux before glibc had
sprintf) or be completely paranoid and leave in the check.
proc_fs_p = fopen(buffer , "r");
if (!proc_fs_p)
syserr("/proc open failed");puts("state found");
#define CONSUME (fgets(buffer, BUFLEN, proc_fs_p))
while (CONSUME != NULL){

Why the CONSUME macro when you only use it the once? In my opinion it
would be clearer to not have it and simply write
while (fgets(buffer, BUFLEN, proc_fs_p) != NULL)
or even, if you prefer
while (fgets(buffer, BUFLEN, proc_fs_p))
if (++tmp == LINUM) {
for (int i = 0; i < BUFLEN; i++){

If you are paranoid (which is not unreasonable, and you are allowing for
other failures) you should also terminate the loop on buffer==0
if (buffer == ':')
coln_count--;
else if (!coln_count && !isblank(buffer)){
state = buffer;
break;
}
}
break;
}
}
fclose(proc_fs_p);
switch (state){
case 'R': //fall through
case 'W': //fall through
case 'S': return TS_RUNNING;
case 'T': return TS_SLEEP;
case 'Z': return TS_FINNISHED;
default:
info("unable to determine process state");
return TS_ERR;
}
return TS_ERR; // unreachable


If you are going to have this return, then I would say get rid of the
default case and have it's code after the switch. Then you don't have
unreachable code!
#undef BUFLEN
#undef LINUM
#undef CONSUME

Using enum as I suggested (which some consider to be abusing it) and not
using the CONSUME macro would allow you to get rid of all of these #undefs.
 
F

Flash Gordon

Heh.

This post said more about Chucks' claims about you than any fibs the
others might spout. Keep it up Han.

"Keep it real. Yet really 'real world' real too."

I can almost see Keighley and Vippstar going cross eyed trying to catch
you out ...

Of course, rather than imagining a strange machine he could have just
pointed out you need to add 1 for the sign, which would have been
perfectly correct, far simpler, and more useful to the OP because it
would have been more obviously relevant not referring to a strange
system with CHAR_BIT==18.

So yes, I think it does show that Han is not trying to help people when
posting correct answers. He was correct about the error but not helpful
to the OP.
 
G

Guest

Nick Birnie wrote:

You can get drafts of the C standards for free, one of which is C99 with
all three approved TCs. Seehttp://clc-wiki.net/wiki/c_standardfor
appropriate links.

The C standard is not designed as a programmers reference. Personally I
like K&R2, but there are other reference books people here recommend as
well. A quick search should find them.

it may not be designed as one but it actually makes a pretty good one!
As standards documents go the C standard is a gem of lucidity!

<snip>

--
Nick Keighley

smart pointers are no picnic, as are virtually all
automatic devices with something like "smart",
"simple" or "fast" in their name.
(C++ Frequently Questioned Answers (FQA))
 
G

Guest

Seconded. Keighley needs recalibrating if he thinks the C standard is
lucid.

I was exposed to The Revised Report at a formative age.
Perhaps it did lasting damage!
 
I

Ike Naar

Of course, rather than imagining a strange machine he could have just
pointed out you need to add 1 for the sign, which would have been
perfectly correct, far simpler, and more useful to the OP because it
would have been more obviously relevant not referring to a strange
system with CHAR_BIT==18.

"Adding 1 for the sign" is not the only issue:
The number of decimal digits is ceil(CHAR_BIT * sizeof(int) / log2(10)).
It is safe to replace log2(10) by 3, but it is not safe to replace
``ceil'' by ``floor'' (which is what integer division does).

``(CHAR_BIT * sizeof(int) + 2) / 3'' divides by three, rounding up.
 
F

Flash Gordon

Ike said:
"Adding 1 for the sign" is not the only issue:

The number of decimal digits is ceil(CHAR_BIT * sizeof(int) / log2(10)).
It is safe to replace log2(10) by 3, but it is not safe to replace
``ceil'' by ``floor'' (which is what integer division does).

``(CHAR_BIT * sizeof(int) + 2) / 3'' divides by three, rounding up.

OK, so I was having a bad day. Thanks for the correction.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top