📄 array.c
字号:
/*
* linux/fs/proc/array.c
*
* Copyright (C) 1992 by Linus Torvalds
* based on ideas by Darren Senn
*
* Fixes:
* Michael. K. Johnson: stat,statm extensions.
* <johnsonm@stolaf.edu>
*
* Pauline Middelink : Made cmdline,envline only break at '\0's, to
* make sure SET_PROCTITLE works. Also removed
* bad '!' which forced address recalculation for
* EVERY character on the current page.
* <middelin@polyware.iaf.nl>
*
* Danny ter Haar : added cpuinfo
* <dth@cistron.nl>
*
* Alessandro Rubini : profile extension.
* <rubini@ipvvis.unipv.it>
*
* Jeff Tranter : added BogoMips field to cpuinfo
* <Jeff_Tranter@Mitel.COM>
*
* Bruno Haible : remove 4K limit for the maps file
* <haible@ma2s2.mathematik.uni-karlsruhe.de>
*
* Yves Arrouye : remove removal of trailing spaces in get_array.
* <Yves.Arrouye@marin.fdn.fr>
*/
/*
* uClinux revisions for NO_MM
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>,
* The Silver Hammer Group, Ltd.
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/tty.h>
#include <linux/user.h>
#include <linux/a.out.h>
#include <linux/string.h>
#include <linux/mman.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
#include <asm/segment.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
#ifdef CONFIG_DEBUG_MALLOC
int get_malloc(char * buffer);
#endif
static int read_core(struct inode * inode, struct file * file,char * buf, int count)
{
unsigned long p = file->f_pos, memsize;
int read;
int count1;
char * pnt;
struct user dump;
#ifdef __i386__
# define FIRST_MAPPED PAGE_SIZE /* we don't have page 0 mapped on x86.. */
#else
# define FIRST_MAPPED 0
#endif
memset(&dump, 0, sizeof(struct user));
dump.magic = CMAGIC;
dump.u_dsize = MAP_NR(high_memory);
#ifdef __alpha__
dump.start_data = PAGE_OFFSET;
#endif
if (count < 0)
return -EINVAL;
memsize = MAP_NR(high_memory + PAGE_SIZE) << PAGE_SHIFT;
if (p >= memsize)
return 0;
if (count > memsize - p)
count = memsize - p;
read = 0;
if (p < sizeof(struct user) && count > 0) {
count1 = count;
if (p + count1 > sizeof(struct user))
count1 = sizeof(struct user)-p;
pnt = (char *) &dump + p;
memcpy_tofs(buf,(void *) pnt, count1);
buf += count1;
p += count1;
count -= count1;
read += count1;
}
while (count > 0 && p < PAGE_SIZE + FIRST_MAPPED) {
put_user(0,buf);
buf++;
p++;
count--;
read++;
}
memcpy_tofs(buf, (void *) (PAGE_OFFSET + p - PAGE_SIZE), count);
read += count;
file->f_pos += read;
return read;
}
static struct file_operations proc_kcore_operations = {
NULL, /* lseek */
read_core,
};
struct inode_operations proc_kcore_inode_operations = {
&proc_kcore_operations,
};
/*
* This function accesses profiling information. The returned data is
* binary: the sampling step and the actual contents of the profile
* buffer. Use of the program readprofile is recommended in order to
* get meaningful info out of these data.
*/
static int read_profile(struct inode *inode, struct file *file, char *buf, int count)
{
unsigned long p = file->f_pos;
int read;
char * pnt;
unsigned int sample_step = 1 << prof_shift;
if (count < 0)
return -EINVAL;
if (p >= (prof_len+1)*sizeof(unsigned int))
return 0;
if (count > (prof_len+1)*sizeof(unsigned int) - p)
count = (prof_len+1)*sizeof(unsigned int) - p;
read = 0;
while (p < sizeof(unsigned int) && count > 0) {
put_user(*((char *)(&sample_step)+p),buf);
buf++; p++; count--; read++;
}
pnt = (char *)prof_buffer + p - sizeof(unsigned int);
memcpy_tofs(buf,(void *)pnt,count);
read += count;
file->f_pos += read;
return read;
}
/* Writing to /proc/profile resets the counters */
static int write_profile(struct inode * inode, struct file * file, const char * buf, int count)
{
int i=prof_len;
while (i--)
prof_buffer[i]=0UL;
return count;
}
static struct file_operations proc_profile_operations = {
NULL, /* lseek */
read_profile,
write_profile,
};
struct inode_operations proc_profile_inode_operations = {
&proc_profile_operations,
};
static int get_loadavg(char * buffer)
{
int a, b, c;
a = avenrun[0] + (FIXED_1/200);
b = avenrun[1] + (FIXED_1/200);
c = avenrun[2] + (FIXED_1/200);
return sprintf(buffer,"%d.%02d %d.%02d %d.%02d %d/%d %d\n",
LOAD_INT(a), LOAD_FRAC(a),
LOAD_INT(b), LOAD_FRAC(b),
LOAD_INT(c), LOAD_FRAC(c),
nr_running, nr_tasks, last_pid);
}
static int get_kstat(char * buffer)
{
int i, len;
unsigned sum = 0;
extern unsigned long total_forks;
for (i = 0 ; i < NR_IRQS ; i++)
sum += kstat.interrupts[i];
len = sprintf(buffer,
"cpu %u %u %u %lu\n"
"disk %u %u %u %u\n"
"disk_rio %u %u %u %u\n"
"disk_wio %u %u %u %u\n"
"disk_rblk %u %u %u %u\n"
"disk_wblk %u %u %u %u\n"
"page %u %u\n"
"swap %u %u\n"
"intr %u",
kstat.cpu_user,
kstat.cpu_nice,
kstat.cpu_system,
jiffies - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system),
kstat.dk_drive[0], kstat.dk_drive[1],
kstat.dk_drive[2], kstat.dk_drive[3],
kstat.dk_drive_rio[0], kstat.dk_drive_rio[1],
kstat.dk_drive_rio[2], kstat.dk_drive_rio[3],
kstat.dk_drive_wio[0], kstat.dk_drive_wio[1],
kstat.dk_drive_wio[2], kstat.dk_drive_wio[3],
kstat.dk_drive_rblk[0], kstat.dk_drive_rblk[1],
kstat.dk_drive_rblk[2], kstat.dk_drive_rblk[3],
kstat.dk_drive_wblk[0], kstat.dk_drive_wblk[1],
kstat.dk_drive_wblk[2], kstat.dk_drive_wblk[3],
kstat.pgpgin,
kstat.pgpgout,
kstat.pswpin,
kstat.pswpout,
sum);
for (i = 0 ; i < NR_IRQS ; i++)
len += sprintf(buffer + len, " %u", kstat.interrupts[i]);
len += sprintf(buffer + len,
"\nctxt %u\n"
"btime %lu\n"
"processes %lu\n",
kstat.context_swtch,
xtime.tv_sec - jiffies / HZ,
total_forks);
return len;
}
static int get_uptime(char * buffer)
{
unsigned long uptime;
unsigned long idle;
uptime = jiffies;
idle = task[0]->utime + task[0]->stime;
/* The formula for the fraction parts really is ((t * 100) / HZ) % 100, but
that would overflow about every five days at HZ == 100.
Therefore the identity a = (a / b) * b + a % b is used so that it is
calculated as (((t / HZ) * 100) + ((t % HZ) * 100) / HZ) % 100.
The part in front of the '+' always evaluates as 0 (mod 100). All divisions
in the above formulas are truncating. For HZ being a power of 10, the
calculations simplify to the version in the #else part (if the printf
format is adapted to the same number of digits as zeroes in HZ.
*/
#if HZ!=100
return sprintf(buffer,"%lu.%02lu %lu.%02lu\n",
uptime / HZ,
(((uptime % HZ) * 100) / HZ) % 100,
idle / HZ,
(((idle % HZ) * 100) / HZ) % 100);
#else
return sprintf(buffer,"%lu.%02lu %lu.%02lu\n",
uptime / HZ,
uptime % HZ,
idle / HZ,
idle % HZ);
#endif
}
static int get_meminfo(char * buffer)
{
struct sysinfo i;
int len;
si_meminfo(&i);
si_swapinfo(&i);
len = sprintf(buffer, " total: used: free: shared: buffers: cached:\n"
"Mem: %8lu %8lu %8lu %8lu %8lu %8lu\n"
"Swap: %8lu %8lu %8lu\n",
i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, page_cache_size*PAGE_SIZE,
i.totalswap, i.totalswap-i.freeswap, i.freeswap);
/*
* Tagged format, for easy grepping and expansion. The above will go away
* eventually, once the tools have been updated.
*/
return len + sprintf(buffer+len,
"MemTotal: %8lu kB\n"
"MemFree: %8lu kB\n"
"MemShared: %8lu kB\n"
"Buffers: %8lu kB\n"
"Cached: %8lu kB\n"
"SwapTotal: %8lu kB\n"
"SwapFree: %8lu kB\n",
i.totalram >> 10,
i.freeram >> 10,
i.sharedram >> 10,
i.bufferram >> 10,
page_cache_size << (PAGE_SHIFT - 10),
i.totalswap >> 10,
i.freeswap >> 10);
}
static int get_version(char * buffer)
{
extern const char *linux_banner;
strcpy(buffer, linux_banner);
return strlen(buffer);
}
static int get_cmdline(char * buffer)
{
extern char saved_command_line[];
return sprintf(buffer, "%s\n", saved_command_line);
}
static struct task_struct ** get_task(pid_t pid)
{
struct task_struct ** p;
p = task;
while (++p < task+NR_TASKS) {
if (*p && (*p)->pid == pid)
return p;
}
return NULL;
}
static unsigned long get_phys_addr(struct task_struct * p, unsigned long ptr)
{
#ifndef NO_MM
pgd_t *page_dir;
pmd_t *page_middle;
pte_t pte;
if (!p || !p->mm || ptr >= TASK_SIZE)
return 0;
page_dir = pgd_offset(p->mm,ptr);
if (pgd_none(*page_dir))
return 0;
if (pgd_bad(*page_dir)) {
printk("bad page directory entry %08lx\n", pgd_val(*page_dir));
pgd_clear(page_dir);
return 0;
}
page_middle = pmd_offset(page_dir,ptr);
if (pmd_none(*page_middle))
return 0;
if (pmd_bad(*page_middle)) {
printk("bad page middle entry %08lx\n", pmd_val(*page_middle));
pmd_clear(page_middle);
return 0;
}
pte = *pte_offset(page_middle,ptr);
if (!pte_present(pte))
return 0;
return pte_page(pte) + (ptr & ~PAGE_MASK);
#else /* NO_MM */
return ptr;
#endif /* NO_MM */
}
static int get_array(struct task_struct ** p, unsigned long start, unsigned long end, char * buffer)
{
unsigned long addr;
int size = 0, result = 0;
char c;
if (start >= end)
return result;
for (;;) {
addr = get_phys_addr(*p, start);
if (!addr)
return result;
do {
c = *(char *) addr;
if (!c)
result = size;
if (size < PAGE_SIZE)
buffer[size++] = c;
else
return result;
addr++;
start++;
if (!c && start >= end)
return result;
} while (addr & ~PAGE_MASK);
}
return result;
}
static int get_env(int pid, char * buffer)
{
struct task_struct ** p = get_task(pid);
if (!p || !*p || !(*p)->mm)
return 0;
return get_array(p, (*p)->mm->env_start, (*p)->mm->env_end, buffer);
}
static int get_arg(int pid, char * buffer)
{
struct task_struct ** p = get_task(pid);
if (!p || !*p || !(*p)->mm)
return 0;
return get_array(p, (*p)->mm->arg_start, (*p)->mm->arg_end, buffer);
}
static unsigned long get_wchan(struct task_struct *p)
{
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
#if defined(__i386__)
{
unsigned long ebp, eip;
unsigned long stack_page;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -