2 * linux/fs/proc/array.c 4 * Copyright (C) 1992 by Linus Torvalds 5 * based on ideas by Darren Senn 8 * Michael. K. Johnson: stat,statm extensions. 9 * <johnsonm@stolaf.edu> 11 * Pauline Middelink : Made cmdline,envline only break at '\0's, to 12 * make sure SET_PROCTITLE works. Also removed 13 * bad '!' which forced address recalculation for 14 * EVERY character on the current page. 15 * <middelin@polyware.iaf.nl> 17 * Danny ter Haar : added cpuinfo 20 * Alessandro Rubini : profile extension. 21 * <rubini@ipvvis.unipv.it> 23 * Jeff Tranter : added BogoMips field to cpuinfo 24 * <Jeff_Tranter@Mitel.COM> 26 * Bruno Haible : remove 4K limit for the maps file 27 * <haible@ma2s2.mathematik.uni-karlsruhe.de> 29 * Yves Arrouye : remove removal of trailing spaces in get_array. 30 * <Yves.Arrouye@marin.fdn.fr> 32 * Jerome Forissier : added per-CPU time information to /proc/stat 33 * and /proc/<pid>/cpu extension 34 * <forissier@isia.cma.fr> 35 * - Incorporation and non-SMP safe operation 36 * of forissier patch in 2.1.78 by 37 * Hans Marcus <crowbar@concepts.nl> 39 * aeb@cwi.nl : /proc/partitions 42 * Alan Cox : security fixes. 43 * <Alan.Cox@linux.org> 45 * Al Viro : safe handling of mm_struct 47 * Gerhard Wichert : added BIGMEM support 48 * Siemens AG <Gerhard.Wichert@pdb.siemens.de> 50 * Al Viro & Jeff Garzik : moved most of the thing into base.c and 51 * : proc_misc.c. The rest may eventually go into 55 #include <linux/config.h> 56 #include <linux/types.h> 57 #include <linux/errno.h> 58 #include <linux/sched.h> 59 #include <linux/kernel.h> 60 #include <linux/kernel_stat.h> 61 #include <linux/tty.h> 62 #include <linux/string.h> 63 #include <linux/mman.h> 64 #include <linux/proc_fs.h> 65 #include <linux/ioport.h> 67 #include <linux/pagemap.h> 68 #include <linux/swap.h> 69 #include <linux/slab.h> 70 #include <linux/smp.h> 71 #include <linux/signal.h> 72 #include <linux/highmem.h> 74 #include <asm/uaccess.h> 75 #include <asm/pgtable.h> 77 #include <asm/processor.h> 79 /* Gcc optimizes away "strlen(x)" for constant x */ 80 #define ADDBUF(buffer, string) \ 81 do { memcpy(buffer, string, strlen(string)); \ 82 buffer += strlen(string); } while (0) 84 staticinlinechar*task_name(struct task_struct
*p
,char* buf
) 89 ADDBUF(buf
,"Name:\t"); 93 unsigned char c
= *name
; 117 * The task state array is a strange "bitmap" of 118 * reasons to sleep. Thus "running" is zero, and 119 * you can test for combinations of others with 122 static const char*task_state_array
[] = { 123 "R (running)",/* 0 */ 124 "S (sleeping)",/* 1 */ 125 "D (disk sleep)",/* 2 */ 127 "T (stopped)",/* 8 */ 131 staticinlineconst char*get_task_state(struct task_struct
*tsk
) 133 unsigned int state
= tsk
->state
& (TASK_RUNNING
| 135 TASK_UNINTERRUPTIBLE
| 138 const char**p
= &task_state_array
[0]; 147 staticinlinechar*task_state(struct task_struct
*p
,char*buffer
) 151 read_lock(&tasklist_lock
); 152 buffer
+=sprintf(buffer
, 157 "Uid:\t%d\t%d\t%d\t%d\n" 158 "Gid:\t%d\t%d\t%d\t%d\n", 160 p
->pid
, p
->p_opptr
->pid
, p
->p_pptr
->pid
!= p
->p_opptr
->pid
? p
->p_pptr
->pid
:0, 161 p
->uid
, p
->euid
, p
->suid
, p
->fsuid
, 162 p
->gid
, p
->egid
, p
->sgid
, p
->fsgid
); 163 read_unlock(&tasklist_lock
); 165 buffer
+=sprintf(buffer
, 168 p
->files
? p
->files
->max_fds
:0); 171 for(g
=0; g
< p
->ngroups
; g
++) 172 buffer
+=sprintf(buffer
,"%d ", p
->groups
[g
]); 174 buffer
+=sprintf(buffer
,"\n"); 178 staticinlinechar*task_mem(struct mm_struct
*mm
,char*buffer
) 180 struct vm_area_struct
* vma
; 181 unsigned long data
=0, stack
=0; 182 unsigned long exec
=0, lib
=0; 185 for(vma
= mm
->mmap
; vma
; vma
= vma
->vm_next
) { 186 unsigned long len
= (vma
->vm_end
- vma
->vm_start
) >>10; 189 if(vma
->vm_flags
& VM_GROWSDOWN
) 193 if(vma
->vm_flags
& VM_WRITE
) 195 if(vma
->vm_flags
& VM_EXEC
) { 197 if(vma
->vm_flags
& VM_EXECUTABLE
) 202 buffer
+=sprintf(buffer
, 210 mm
->total_vm
<< (PAGE_SHIFT
-10), 211 mm
->locked_vm
<< (PAGE_SHIFT
-10), 212 mm
->rss
<< (PAGE_SHIFT
-10), 219 static voidcollect_sigign_sigcatch(struct task_struct
*p
, sigset_t
*ign
, 222 struct k_sigaction
*k
; 230 for(i
=1; i
<= _NSIG
; ++i
, ++k
) { 231 if(k
->sa
.sa_handler
== SIG_IGN
) 233 else if(k
->sa
.sa_handler
!= SIG_DFL
) 239 staticinlinechar*task_sig(struct task_struct
*p
,char*buffer
) 243 buffer
+=sprintf(buffer
,"SigPnd:\t"); 244 buffer
=render_sigset_t(&p
->pending
.signal
, buffer
); 246 buffer
+=sprintf(buffer
,"SigBlk:\t"); 247 buffer
=render_sigset_t(&p
->blocked
, buffer
); 250 collect_sigign_sigcatch(p
, &ign
, &catch); 251 buffer
+=sprintf(buffer
,"SigIgn:\t"); 252 buffer
=render_sigset_t(&ign
, buffer
); 254 buffer
+=sprintf(buffer
,"SigCgt:\t");/* Linux 2.0 uses "SigCgt" */ 255 buffer
=render_sigset_t(&catch, buffer
); 261 extern inlinechar*task_cap(struct task_struct
*p
,char*buffer
) 263 return buffer
+sprintf(buffer
,"CapInh:\t%016x\n" 266 cap_t(p
->cap_inheritable
), 267 cap_t(p
->cap_permitted
), 268 cap_t(p
->cap_effective
)); 272 intproc_pid_status(struct task_struct
*task
,char* buffer
) 275 struct mm_struct
*mm
; 276 #if defined(CONFIG_ARCH_S390) 280 buffer
=task_name(task
, buffer
); 281 buffer
=task_state(task
, buffer
); 285 atomic_inc(&mm
->mm_users
); 288 buffer
=task_mem(mm
, buffer
); 291 buffer
=task_sig(task
, buffer
); 292 buffer
=task_cap(task
, buffer
); 293 #if defined(CONFIG_ARCH_S390) 294 for(line
=0;(len
=sprintf_regs(line
,buffer
,task
,NULL
,NULL
))!=0;line
++) 297 return buffer
- orig
; 300 intproc_pid_stat(struct task_struct
*task
,char* buffer
) 302 unsigned long vsize
, eip
, esp
, wchan
; 304 int tty_pgrp
= -1, tty_nr
=0; 305 sigset_t sigign
, sigcatch
; 309 struct mm_struct
*mm
; 311 state
= *get_task_state(task
); 312 vsize
= eip
= esp
=0; 316 atomic_inc(&mm
->mm_users
); 318 tty_pgrp
= task
->tty
->pgrp
; 319 tty_nr
=kdev_t_to_nr(task
->tty
->device
); 323 struct vm_area_struct
*vma
; 327 vsize
+= vma
->vm_end
- vma
->vm_start
; 335 wchan
=get_wchan(task
); 337 collect_sigign_sigcatch(task
, &sigign
, &sigcatch
); 339 /* scale priority and nice values from timeslices to -20..20 */ 340 /* to make it look like a "normal" Unix priority/nice value */ 341 priority
= task
->counter
; 342 priority
=20- (priority
*10+ DEF_COUNTER
/2) / DEF_COUNTER
; 345 read_lock(&tasklist_lock
); 346 ppid
= task
->p_opptr
->pid
; 347 read_unlock(&tasklist_lock
); 348 res
=sprintf(buffer
,"%d (%s) %c %d %d %d %d %d %lu %lu \ 349 %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu \ 350 %lu %lu %lu %lu %lu %lu %lu %lu %d %d\n", 364 task
->times
.tms_utime
, 365 task
->times
.tms_stime
, 366 task
->times
.tms_cutime
, 367 task
->times
.tms_cstime
, 374 mm
? mm
->rss
:0,/* you might want to shift this left 3 */ 375 task
->rlim
[RLIMIT_RSS
].rlim_cur
, 376 mm
? mm
->start_code
:0, 377 mm
? mm
->end_code
:0, 378 mm
? mm
->start_stack
:0, 381 /* The signal information here is obsolete. 382 * It must be decimal for Linux 2.0 compatibility. 383 * Use /proc/#/status for real-time signals. 385 task
->pending
.signal
.sig
[0] &0x7fffffffUL
, 386 task
->blocked
.sig
[0] &0x7fffffffUL
, 387 sigign
.sig
[0] &0x7fffffffUL
, 388 sigcatch
.sig
[0] &0x7fffffffUL
, 399 staticinlinevoidstatm_pte_range(pmd_t
* pmd
,unsigned long address
,unsigned long size
, 400 int* pages
,int* shared
,int* dirty
,int* total
) 412 pte
=pte_offset(pmd
, address
); 413 address
&= ~PMD_MASK
; 414 end
= address
+ size
; 421 address
+= PAGE_SIZE
; 426 if(!pte_present(page
)) 431 ptpage
=pte_page(page
); 432 if((!VALID_PAGE(ptpage
)) || 433 PageReserved(ptpage
)) 435 if(page_count(pte_page(page
)) >1) 437 }while(address
< end
); 440 staticinlinevoidstatm_pmd_range(pgd_t
* pgd
,unsigned long address
,unsigned long size
, 441 int* pages
,int* shared
,int* dirty
,int* total
) 453 pmd
=pmd_offset(pgd
, address
); 454 address
&= ~PGDIR_MASK
; 455 end
= address
+ size
; 459 statm_pte_range(pmd
, address
, end
- address
, pages
, shared
, dirty
, total
); 460 address
= (address
+ PMD_SIZE
) & PMD_MASK
; 462 }while(address
< end
); 465 static voidstatm_pgd_range(pgd_t
* pgd
,unsigned long address
,unsigned long end
, 466 int* pages
,int* shared
,int* dirty
,int* total
) 468 while(address
< end
) { 469 statm_pmd_range(pgd
, address
, end
- address
, pages
, shared
, dirty
, total
); 470 address
= (address
+ PGDIR_SIZE
) & PGDIR_MASK
; 475 intproc_pid_statm(struct task_struct
*task
,char* buffer
) 477 struct mm_struct
*mm
; 478 int size
=0, resident
=0, share
=0, trs
=0, lrs
=0, drs
=0, dt
=0; 483 atomic_inc(&mm
->mm_users
); 486 struct vm_area_struct
* vma
; 490 pgd_t
*pgd
=pgd_offset(mm
, vma
->vm_start
); 491 int pages
=0, shared
=0, dirty
=0, total
=0; 493 statm_pgd_range(pgd
, vma
->vm_start
, vma
->vm_end
, &pages
, &shared
, &dirty
, &total
); 498 if(vma
->vm_flags
& VM_EXECUTABLE
) 499 trs
+= pages
;/* text */ 500 else if(vma
->vm_flags
& VM_GROWSDOWN
) 501 drs
+= pages
;/* stack */ 502 else if(vma
->vm_end
>0x60000000) 503 lrs
+= pages
;/* library */ 511 returnsprintf(buffer
,"%d %d %d %d %d %d %d\n", 512 size
, resident
, share
, trs
, lrs
, drs
, dt
); 516 * The way we support synthetic files > 4K 517 * - without storing their contents in some buffer and 518 * - without walking through the entire synthetic file until we reach the 519 * position of the requested data 520 * is to cleverly encode the current position in the file's f_pos field. 521 * There is no requirement that a read() call which returns `count' bytes 522 * of data increases f_pos by exactly `count'. 524 * This idea is Linus' one. Bruno implemented it. 528 * For the /proc/<pid>/maps file, we use fixed length records, each containing 531 #define MAPS_LINE_LENGTH 4096 532 #define MAPS_LINE_SHIFT 12 534 * f_pos = (number of the vma in the task->mm->mmap list) * MAPS_LINE_LENGTH 535 * + (index into the line) 537 /* for systems with sizeof(void*) == 4: */ 538 #define MAPS_LINE_FORMAT4"%08lx-%08lx %s %08lx %s %lu" 539 #define MAPS_LINE_MAX4 49/* sum of 8 1 8 1 4 1 8 1 5 1 10 1 */ 541 /* for systems with sizeof(void*) == 8: */ 542 #define MAPS_LINE_FORMAT8"%016lx-%016lx %s %016lx %s %lu" 543 #define MAPS_LINE_MAX8 73/* sum of 16 1 16 1 4 1 16 1 5 1 10 1 */ 545 #define MAPS_LINE_MAX MAPS_LINE_MAX8 548 ssize_t
proc_pid_read_maps(struct task_struct
*task
,struct file
* file
,char* buf
, 549 size_t count
, loff_t
*ppos
) 551 struct mm_struct
*mm
; 552 struct vm_area_struct
* map
, * next
; 553 char* destptr
= buf
, * buffer
; 560 * We might sleep getting the page, so get it first. 563 buffer
= (char*)__get_free_page(GFP_KERNEL
); 572 atomic_inc(&mm
->mm_users
); 577 /* Check whether the mmaps could change if we sleep */ 578 volatile_task
= (task
!= current
||atomic_read(&mm
->mm_users
) >2); 581 lineno
= *ppos
>> MAPS_LINE_SHIFT
; 582 column
= *ppos
& (MAPS_LINE_LENGTH
-1); 584 /* quickly go to line lineno */ 586 for(map
= mm
->mmap
, i
=0; map
&& (i
< lineno
); map
= map
->vm_next
, i
++) 589 for( ; map
; map
= next
) { 590 /* produce the next line */ 592 char str
[5], *cp
= str
; 596 int maxlen
= (sizeof(void*) ==4) ? 597 MAPS_LINE_MAX4
: MAPS_LINE_MAX8
; 601 * Get the next vma now (but it won't be used if we sleep). 604 flags
= map
->vm_flags
; 606 *cp
++ = flags
& VM_READ
?'r':'-'; 607 *cp
++ = flags
& VM_WRITE
?'w':'-'; 608 *cp
++ = flags
& VM_EXEC
?'x':'-'; 609 *cp
++ = flags
& VM_MAYSHARE
?'s':'p'; 614 if(map
->vm_file
!= NULL
) { 615 dev
= map
->vm_file
->f_dentry
->d_inode
->i_dev
; 616 ino
= map
->vm_file
->f_dentry
->d_inode
->i_ino
; 617 line
=d_path(map
->vm_file
->f_dentry
, 618 map
->vm_file
->f_vfsmnt
, 620 buffer
[PAGE_SIZE
-1] ='\n'; 628 sizeof(void*) ==4? MAPS_LINE_FORMAT4
: MAPS_LINE_FORMAT8
, 629 map
->vm_start
, map
->vm_end
, str
, map
->vm_pgoff
<< PAGE_SHIFT
, 633 for(i
= len
; i
< maxlen
; i
++) 635 len
= buffer
+ PAGE_SIZE
- line
; 639 column
=0;/* continue with next line at column 0 */ 641 continue;/* we haven't slept */ 648 copy_to_user(destptr
, line
+column
, i
);/* may have slept */ 654 column
=0;/* next time: next line at column 0 */ 662 /* By writing to user space, we might have slept. 663 * Stop the loop, to avoid a race condition. 671 *ppos
= (lineno
<< MAPS_LINE_SHIFT
) + column
; 675 retval
= destptr
- buf
; 676 free_page((unsigned long)buffer
); 682 intproc_pid_cpu(struct task_struct
*task
,char* buffer
) 688 task
->times
.tms_utime
, 689 task
->times
.tms_stime
); 691 for(i
=0; i
< smp_num_cpus
; i
++) 692 len
+=sprintf(buffer
+ len
,"cpu%d %lu %lu\n", 694 task
->per_cpu_utime
[cpu_logical_map(i
)], 695 task
->per_cpu_stime
[cpu_logical_map(i
)]);