The ppid of a process is not really helpful if I want to reconstruct the
real history of processes on a machine, since it may become 1 when the
parent dies and the process is reparented to init.
I am not aware of concepts in Linux or other unices that apply to this
case. So I made up the "biological parent pid" bioppid (in contrast to the
adoptive parents pid) that just never changes.
Any user of it must of course remember that it doesn't need to be a valid
pid anymore or might even belong to a different process that was forked in
the meantime. bioppid only had to be a valid pid at time btime (it's
a (btime, pid) pair that unambiguously identifies a process).
Comments? (other that I just broke /proc/nnn/status parsing once again :-)
Tim
--- linux-2.6.10/include/linux/sched.h 2004-12-24 22:33:59.000000000 +0100
+++ linux-2.6.10-ppid/include/linux/sched.h 2005-01-31 19:20:00.000000000 +0100
@@ -556,6 +556,7 @@ struct task_struct {
unsigned did_exec:1;
pid_t pid;
pid_t tgid;
+ pid_t bioppid; /* biological parents */
/*
* pointers to (original) parent process, youngest child, younger sibling,
* older sibling, respectively. (p->father can be replaced with
--- linux-2.6.10/kernel/fork.c 2004-12-24 22:33:59.000000000 +0100
+++ linux-2.6.10-ppid/kernel/fork.c 2005-01-31 18:15:39.000000000 +0100
@@ -889,6 +889,7 @@ static task_t *copy_process(unsigned lon
p->tgid = p->pid;
if (clone_flags & CLONE_THREAD)
p->tgid = current->tgid;
+ p->bioppid = current->pid;
if ((retval = security_task_alloc(p)))
goto bad_fork_cleanup_policy;
--- linux-2.6.10/fs/proc/array.c 2004-12-24 22:35:00.000000000 +0100
+++ linux-2.6.10-ppid/fs/proc/array.c 2005-01-31 18:19:02.000000000 +0100
@@ -165,6 +165,7 @@ static inline char * task_state(struct t
"Tgid:\t%d\n"
"Pid:\t%d\n"
"PPid:\t%d\n"
+ "BioPPid:\t%d\n"
"TracerPid:\t%d\n"
"Uid:\t%d\t%d\t%d\t%d\n"
"Gid:\t%d\t%d\t%d\t%d\n",
@@ -172,6 +173,7 @@ static inline char * task_state(struct t
(p->sleep_avg/1024)*100/(1020000000/1024),
p->tgid,
p->pid, pid_alive(p) ? p->group_leader->real_parent->tgid : 0,
+ p->bioppid,
pid_alive(p) && p->ptrace ? p->parent->pid : 0,
p->uid, p->euid, p->suid, p->fsuid,
p->gid, p->egid, p->sgid, p->fsgid);
--- linux-2.6.10/kernel/acct.c 2004-12-24 22:34:58.000000000 +0100
+++ linux-2.6.10-ppid/kernel/acct.c 2005-01-31 18:19:35.000000000 +0100
@@ -446,7 +446,7 @@ static void do_acct_process(long exitcod
#endif
#if ACCT_VERSION==3
ac.ac_pid = current->tgid;
- ac.ac_ppid = current->parent->tgid;
+ ac.ac_ppid = current->bioppid;
#endif
read_lock(&tasklist_lock); /* pin current->signal */
On Mon, 31 Jan 2005, Tim Schmielau wrote:
> The ppid of a process is not really helpful if I want to reconstruct the
> real history of processes on a machine, since it may become 1 when the
> parent dies and the process is reparented to init.
>
> I am not aware of concepts in Linux or other unices that apply to this
> case. So I made up the "biological parent pid" bioppid (in contrast to the
> adoptive parents pid) that just never changes.
> Any user of it must of course remember that it doesn't need to be a valid
> pid anymore or might even belong to a different process that was forked in
> the meantime. bioppid only had to be a valid pid at time btime (it's
> a (btime, pid) pair that unambiguously identifies a process).
>
> Comments? (other that I just broke /proc/nnn/status parsing once again :-)
>
> Tim
It's worthless for the reasons cited plus others and it just adds more
unused stuff to the kernel.
>
>
> --- linux-2.6.10/include/linux/sched.h 2004-12-24 22:33:59.000000000 +0100
> +++ linux-2.6.10-ppid/include/linux/sched.h 2005-01-31 19:20:00.000000000 +0100
> @@ -556,6 +556,7 @@ struct task_struct {
> unsigned did_exec:1;
> pid_t pid;
> pid_t tgid;
> + pid_t bioppid; /* biological parents */
> /*
> * pointers to (original) parent process, youngest child, younger sibling,
> * older sibling, respectively. (p->father can be replaced with
>
> --- linux-2.6.10/kernel/fork.c 2004-12-24 22:33:59.000000000 +0100
> +++ linux-2.6.10-ppid/kernel/fork.c 2005-01-31 18:15:39.000000000 +0100
> @@ -889,6 +889,7 @@ static task_t *copy_process(unsigned lon
> p->tgid = p->pid;
> if (clone_flags & CLONE_THREAD)
> p->tgid = current->tgid;
> + p->bioppid = current->pid;
>
> if ((retval = security_task_alloc(p)))
> goto bad_fork_cleanup_policy;
>
> --- linux-2.6.10/fs/proc/array.c 2004-12-24 22:35:00.000000000 +0100
> +++ linux-2.6.10-ppid/fs/proc/array.c 2005-01-31 18:19:02.000000000 +0100
> @@ -165,6 +165,7 @@ static inline char * task_state(struct t
> "Tgid:\t%d\n"
> "Pid:\t%d\n"
> "PPid:\t%d\n"
> + "BioPPid:\t%d\n"
> "TracerPid:\t%d\n"
> "Uid:\t%d\t%d\t%d\t%d\n"
> "Gid:\t%d\t%d\t%d\t%d\n",
> @@ -172,6 +173,7 @@ static inline char * task_state(struct t
> (p->sleep_avg/1024)*100/(1020000000/1024),
> p->tgid,
> p->pid, pid_alive(p) ? p->group_leader->real_parent->tgid : 0,
> + p->bioppid,
> pid_alive(p) && p->ptrace ? p->parent->pid : 0,
> p->uid, p->euid, p->suid, p->fsuid,
> p->gid, p->egid, p->sgid, p->fsgid);
>
> --- linux-2.6.10/kernel/acct.c 2004-12-24 22:34:58.000000000 +0100
> +++ linux-2.6.10-ppid/kernel/acct.c 2005-01-31 18:19:35.000000000 +0100
> @@ -446,7 +446,7 @@ static void do_acct_process(long exitcod
> #endif
> #if ACCT_VERSION==3
> ac.ac_pid = current->tgid;
> - ac.ac_ppid = current->parent->tgid;
> + ac.ac_ppid = current->bioppid;
> #endif
>
> read_lock(&tasklist_lock); /* pin current->signal */
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>
Cheers,
Dick Johnson
Penguin : Linux version 2.6.9 on an i686 machine (5537.79 BogoMips).
Notice : All mail here is now cached for review by Dictator Bush.
98.36% of all statistics are fiction.
Quoting Tim Schmielau <[email protected]>:
> The ppid of a process is not really helpful if I want to reconstruct the
> real history of processes on a machine, since it may become 1 when the
> parent dies and the process is reparented to init.
>
> I am not aware of concepts in Linux or other unices that apply to this
> case. So I made up the "biological parent pid" bioppid (in contrast to the
> adoptive parents pid) that just never changes.
> Any user of it must of course remember that it doesn't need to be a valid
> pid anymore or might even belong to a different process that was forked in
> the meantime. bioppid only had to be a valid pid at time btime (it's
> a (btime, pid) pair that unambiguously identifies a process).
>
> Comments? (other that I just broke /proc/nnn/status parsing once again :-)
Eh, I can't see how this bioppid would be useful.
Help me. Examples?
> Tim
>
>
> --- linux-2.6.10/include/linux/sched.h 2004-12-24 22:33:59.000000000 +0100
> +++ linux-2.6.10-ppid/include/linux/sched.h 2005-01-31 19:20:00.000000000 +0100
> @@ -556,6 +556,7 @@ struct task_struct {
> unsigned did_exec:1;
> pid_t pid;
> pid_t tgid;
> + pid_t bioppid; /* biological parents */
> /*
> * pointers to (original) parent process, youngest child, younger sibling,
> * older sibling, respectively. (p->father can be replaced with
>
> --- linux-2.6.10/kernel/fork.c 2004-12-24 22:33:59.000000000 +0100
> +++ linux-2.6.10-ppid/kernel/fork.c 2005-01-31 18:15:39.000000000 +0100
> @@ -889,6 +889,7 @@ static task_t *copy_process(unsigned lon
> p->tgid = p->pid;
> if (clone_flags & CLONE_THREAD)
> p->tgid = current->tgid;
> + p->bioppid = current->pid;
>
> if ((retval = security_task_alloc(p)))
> goto bad_fork_cleanup_policy;
>
> --- linux-2.6.10/fs/proc/array.c 2004-12-24 22:35:00.000000000 +0100
> +++ linux-2.6.10-ppid/fs/proc/array.c 2005-01-31 18:19:02.000000000 +0100
> @@ -165,6 +165,7 @@ static inline char * task_state(struct t
> "Tgid:\t%d\n"
> "Pid:\t%d\n"
> "PPid:\t%d\n"
> + "BioPPid:\t%d\n"
> "TracerPid:\t%d\n"
> "Uid:\t%d\t%d\t%d\t%d\n"
> "Gid:\t%d\t%d\t%d\t%d\n",
> @@ -172,6 +173,7 @@ static inline char * task_state(struct t
> (p->sleep_avg/1024)*100/(1020000000/1024),
> p->tgid,
> p->pid, pid_alive(p) ? p->group_leader->real_parent->tgid : 0,
> + p->bioppid,
> pid_alive(p) && p->ptrace ? p->parent->pid : 0,
> p->uid, p->euid, p->suid, p->fsuid,
> p->gid, p->egid, p->sgid, p->fsgid);
>
> --- linux-2.6.10/kernel/acct.c 2004-12-24 22:34:58.000000000 +0100
> +++ linux-2.6.10-ppid/kernel/acct.c 2005-01-31 18:19:35.000000000 +0100
> @@ -446,7 +446,7 @@ static void do_acct_process(long exitcod
> #endif
> #if ACCT_VERSION==3
> ac.ac_pid = current->tgid;
> - ac.ac_ppid = current->parent->tgid;
> + ac.ac_ppid = current->bioppid;
> #endif
>
> read_lock(&tasklist_lock); /* pin current->signal */
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>
>
--
Regards Michael Buesch [ http://www.tuxsoft.de.vu ]
On Mon, 31 Jan 2005, Michael Buesch wrote:
> Quoting Tim Schmielau <[email protected]>:
> > The ppid of a process is not really helpful if I want to reconstruct the
> > real history of processes on a machine, since it may become 1 when the
> > parent dies and the process is reparented to init.
> >
> > I am not aware of concepts in Linux or other unices that apply to this
> > case. So I made up the "biological parent pid" bioppid (in contrast to the
> > adoptive parents pid) that just never changes.
> > Any user of it must of course remember that it doesn't need to be a valid
> > pid anymore or might even belong to a different process that was forked in
> > the meantime. bioppid only had to be a valid pid at time btime (it's
> > a (btime, pid) pair that unambiguously identifies a process).
> >
> > Comments? (other that I just broke /proc/nnn/status parsing once again :-)
>
> Eh, I can't see how this bioppid would be useful.
> Help me. Examples?
I'm trying to reconstruct the complete history of processes from the
BSD accounting records. However, this is not very useful if a large
fraction of the processes look as if they were started by init.
The following program will print the history in a form vaguely resembling
pstree output from the accounting file:
Tim
/* treeacct.c: use ac_pid and ac_ppid fields of struct acct_v3 to
* build a tree of the process history.
*
* $Id: treeacct.c,v 0.8 2005/01/31 23:34:51 tim Exp tim $
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <assert.h>
#include <linux/acct.h>
#define TIME_FUZZ 4
void *alloc(size_t size)
{
void *mem;
mem = malloc(size);
if (mem==NULL) {
fprintf(stderr, "out of memory.\n");
exit(5);
}
return mem;
}
/*
* For each pid the acct records are organized in a singly linked list
* named task_struct that is sorted by process start time (btime).
*
* They also form a tree according to their family relations.
*/
struct task_struct {
/* struct task_struct *prev; seems to be superfluous */
struct task_struct *next, *parent, *children, *siblings;
struct acct_v3 *acct;
};
struct pid_struct {
struct pid_struct *next;
struct task_struct *tasklist;
};
/*
* The task_structs are stored in a hash array of singly linked pid_struct
* lists sorted by pid.
*/
#define PID_HASH_MASK 0xf
struct pid_struct *pid_hash[PID_HASH_MASK + 1];
/*
* List of tasks without parents.
*/
struct task_struct *orphans=NULL;
/*
* Find the task that is uniquely determined by it's pid and the
* time when the pid was valid.
* If create!=0 and the task doesn't exist yet, create it.
*/
struct task_struct *get_task(__u32 pid, __u32 time, int create)
{
struct task_struct **task, *newtask;
struct pid_struct **pidlist, *newpid;
pidlist = &pid_hash[pid & PID_HASH_MASK];
while (*pidlist!=NULL && (*pidlist)->tasklist->acct->ac_pid > pid)
pidlist = &((*pidlist)->next);
if (*pidlist==NULL || (*pidlist)->tasklist->acct->ac_pid < pid) {
/* this pid doesn't exist */
if (create!=0) {
/* so create task */
newtask = alloc(sizeof(struct task_struct));
newtask->next = NULL;
/* as well as a pidlist for it*/
newpid = alloc(sizeof(struct pid_struct));
newpid->next = *pidlist;
newpid->tasklist = newtask;
*pidlist = newpid;
return newtask;
} else {
return NULL;
}
} else {
/* find task on pidlist */
task = &((*pidlist)->tasklist);
while (*task!=NULL
&& (*task)->acct->ac_btime < time)
task = &((*task)->next);
if (*task!=NULL
&& (*task)->acct->ac_btime <= time+TIME_FUZZ
&& (*task)->acct->ac_btime
+ (__u32)((*task)->acct->ac_etime+1)
>= time-TIME_FUZZ) {
/* process found */
if (create!=0) {
fprintf(stderr,
"warning: pid %lu, occurs twice"
" at time $lu.",
pid, time);
} else {
return *task;
}
}
if (create!=0) {
newtask = alloc(sizeof(struct task_struct));
newtask->next = *task;
*task = newtask;
return newtask;
} else {
return NULL;
}
}
}
void fill_hashtab(struct acct_v3 *acct_record)
{
struct task_struct *newtask;
newtask = get_task(acct_record->ac_pid, acct_record->ac_btime, 1);
assert(newtask!=0);
newtask->acct = acct_record;
newtask->children = NULL;
}
void tree_insert(struct task_struct *newtask)
{
struct task_struct **task;
newtask->parent = get_task(newtask->acct->ac_ppid,
newtask->acct->ac_btime, 0);
if (newtask->parent==NULL) {
task = &orphans;
} else {
task = &(newtask->parent->children);
}
/* keep the list sorted */
while ((*task) != NULL
&& ((*task)->acct->ac_pid < newtask->acct->ac_pid
|| ((*task)->acct->ac_pid == newtask->acct->ac_pid
&& (*task)->acct->ac_btime < newtask->acct->ac_btime)))
task = &((*task)->siblings);
newtask->siblings = *task;
*task = newtask;
}
void build_tree(unsigned long count)
{
struct task_struct *newtask;
struct pid_struct *pidlist;
int i;
for (i=PID_HASH_MASK; i>=0; i--)
for (pidlist = pid_hash[i];
pidlist!=NULL;
pidlist = pidlist->next)
for (newtask = pidlist->tasklist;
newtask!=NULL;
newtask = newtask->next) {
tree_insert(newtask);
count--;
}
assert(count==0);
}
void dump_task(struct task_struct *task, unsigned long depth)
{
struct task_struct *parent;
time_t t;
char start[32], end[32];
unsigned long i;
int pos;
/* convert start and end time to ascii */
t = task->acct->ac_btime;
ctime_r(&t, start);
/* start[24] = 0; */
t += (unsigned long)(task->acct->ac_etime+1);
ctime_r(&t, end);
/* end[24] = 0; */
for(i=1; i<2*depth; i++)
putchar(' ');
printf("%.*s (%lu, parent %lu) %nstart %s",
ACCT_COMM,
task->acct->ac_comm,
task->acct->ac_pid,
task->acct->ac_ppid,
&pos,
start);
for(i=1; i<pos+2*depth; i++)
putchar(' ');
printf("end %s", end);
}
void dump_tree(unsigned long count)
{
__u32 pid, maxpid=PID_HASH_MASK;
struct pid_struct *pidlist;
struct task_struct *task, *parent;
unsigned long dumped = 0;
unsigned depth = 1;
task = orphans;
while (task!=NULL) {
dump_task(task, depth);
dumped++;
if (task->children != NULL) {
depth++;
task = task->children;
} else if (task->siblings != NULL) {
task = task->siblings;
} else {
while (1) {
task = task->parent;
depth--;
if (task==NULL)
break;
if (task->siblings != NULL) {
task = task->siblings;
break;
}
}
}
}
fprintf(stderr, "dumped %lu records.\n", dumped);
assert(depth==0);
assert(dumped==count);
}
struct acct_v3
*mmap_acct_file(char *name, size_t *length, off_t *offset)
{
int acctfile;
struct acct_v3 *acct_records;
struct stat acctfile_status;
acctfile = open(name, O_RDONLY);
if (acctfile==-1) {
perror("error opening accounting file");
exit(2);
}
if (fstat(acctfile, &acctfile_status)!=0) {
perror("cannot fstat accounting file");
exit(3);
}
*length = acctfile_status.st_size - *offset;
fprintf(stderr, "length: %lu.\n", *length);
acct_records = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, acctfile,
*offset);
if (acct_records==MAP_FAILED) {
perror("error mmapping accounting file");
exit(4);
}
close(acctfile);
return acct_records;
}
int main(int argc, char *argv[])
{
struct acct_v3 *acct_records;
size_t length = (unsigned long)(-1);
off_t offset = 0;
unsigned long count=0;
if (argc!=2) {
printf("usage: %s <acctfile>\n", argv[0]);
return 1;
}
acct_records = mmap_acct_file(argv[1], &length, &offset);
while (length>=sizeof(struct acct_v3)) {
fill_hashtab(acct_records);
acct_records++;
length -= sizeof(struct acct_v3);
count++;
}
if (length!=0)
fprintf(stderr,"Inclomplete last record of length %lu.\n",
length);
fprintf(stderr, "%lu records sucessfully read.\n", count);
build_tree(count);
dump_tree(count);
return 0;
}
In article <[email protected]> you wrote:
> I am not aware of concepts in Linux or other unices that apply to this
> case.
Normal process accounting.
If you want to keep the pid of the bio-parent, you also need to keep the
start-time to make it unique. Better would be to have a all-time-unqiue
process handle. But I think it is better to not have that field, but use
audit logs. That is especially needed if you want to track chains, because
it doesnt help you to know the bio parent if you have no idea what that was.
Gruss
Bernd
On Tue, 1 Feb 2005, Helge Hafting wrote:
> Tim Schmielau wrote:
>
> >
> >I'm trying to reconstruct the complete history of processes from the
> >BSD accounting records. However, this is not very useful if a large
> >fraction of the processes look as if they were started by init.
> >
> >The following program will print the history in a form vaguely resembling
> >pstree output from the accounting file:
> >
> >
> Just having those original ppids won't really help you, if the process
> is long gone with no trace of what it was. Consider adding logging to
> "fork()" and "exec()" instead of doing this. Then you can reconstruct
> history all the way back to the correct executables.
Why should the process get lost with no trace of what it was - are you
questioning reliability of BSD accounting?
Tim Schmielau wrote:
>
>I'm trying to reconstruct the complete history of processes from the
>BSD accounting records. However, this is not very useful if a large
>fraction of the processes look as if they were started by init.
>
>The following program will print the history in a form vaguely resembling
>pstree output from the accounting file:
>
>
Just having those original ppids won't really help you, if the process
is long gone with no trace of what it was. Consider adding logging to
"fork()" and "exec()" instead of doing this. Then you can reconstruct
history all the way back to the correct executables.
Helge Hafting
On Tue, 1 Feb 2005, Bernd Eckenfels wrote:
> In article <[email protected]> you wrote:
> > I am not aware of concepts in Linux or other unices that apply to this
> > case.
>
> Normal process accounting.
Sure. That's what the patch was made for. Or do you have anything else
in mind than BSD accounting?
> If you want to keep the pid of the bio-parent, you also need to keep the
> start-time to make it unique.
Yes, that's what I wrote: A process would be uniquely identified by the
(btime, pid) pair, in terms of BSD accounting field names. Or
(start_time, pid), if we use the names of task_struct members.
> Better would be to have a all-time-unqiue process handle.
Yes, but that would need new infrastructure. So instead of assigning new
64 bit process handles, we can just just that pair of 32 bit variables.
> But I think it is better to not have that field, but use
> audit logs. That is especially needed if you want to track chains, because
> it doesnt help you to know the bio parent if you have no idea what that was.
That's the kind of comment I was actually seeking - maybe what I'm trying
is not really worth because anyone interested in its reliability and
security would use auditing anyways.
But still it might be useful for 'home use', because I do have an idea of
what the parent was if I keep the BSD accounting records.
Tim Schmielau wrote:
> The ppid of a process is not really helpful if I want to reconstruct the
> real history of processes on a machine, since it may become 1 when the
> parent dies and the process is reparented to init.
>
> I am not aware of concepts in Linux or other unices that apply to this
> case. So I made up the "biological parent pid" bioppid (in contrast to the
> adoptive parents pid) that just never changes.
> Any user of it must of course remember that it doesn't need to be a valid
> pid anymore or might even belong to a different process that was forked in
> the meantime. bioppid only had to be a valid pid at time btime (it's
> a (btime, pid) pair that unambiguously identifies a process).
I think you are not only using a hammer to swat a fly, buy the wrong
fly. Would it not do as well to log reparenting? You could even add that
as an option to init, although if you are being lazy about tracking the
original parent a kernel log saying something like
reparent PID1 from PID2 to PID3
would be best. While I think all current reparenting is done to init, I
could certainly think of a use for a method to reparent back to the
grandparent, just to keep the accounting clean.
The init option would be unintrusive, but I doubt many people would feel
the need for a kernel log feature. On the other hand it doesn't happen
often, I looked at a system up 172 days and it had nothing but the
daemons reparented (at the moment).
--
-bill davidsen ([email protected])
"The secret to procrastination is to put things off until the
last possible moment - but no longer" -me
On Tue, 1 Feb 2005, Bill Davidsen wrote:
> Tim Schmielau wrote:
> > The ppid of a process is not really helpful if I want to reconstruct the
> > real history of processes on a machine, since it may become 1 when the
> > parent dies and the process is reparented to init.
> >
> > I am not aware of concepts in Linux or other unices that apply to this
> > case. So I made up the "biological parent pid" bioppid (in contrast to the
> > adoptive parents pid) that just never changes.
> > Any user of it must of course remember that it doesn't need to be a valid
> > pid anymore or might even belong to a different process that was forked in
> > the meantime. bioppid only had to be a valid pid at time btime (it's
> > a (btime, pid) pair that unambiguously identifies a process).
>
> I think you are not only using a hammer to swat a fly, buy the wrong
> fly. Would it not do as well to log reparenting? You could even add that
> as an option to init, although if you are being lazy about tracking the
> original parent a kernel log saying something like
> reparent PID1 from PID2 to PID3
> would be best. While I think all current reparenting is done to init, I
> could certainly think of a use for a method to reparent back to the
> grandparent, just to keep the accounting clean.
My idea of a hammer seems to be slightly different. After all, I just
added _two_lines_ of code [*] to log useful information where useless info
was logged before.
Any new logging mechanism would be orders of magnitude more expensive.
Tim
[*] or, in other word, 4 bytes per task_struct and one instruction
to fill that.