linux 进程参数文件 /proc/pid/cmdline 简介

在proc根目录下,以数字命名的目录表示当前一个运行的进程,目录名为进程的pid。其内的目录和文件给出了一些关于进程的信息。
ywx@ywx:/proc/1500$ ls
ls: cannot read symbolic link cwd: Permission denied
ls: cannot read symbolic link root: Permission denied
ls: cannot read symbolic link exe: Permission denied
attr    cpuset  latency   mountstats   sched      status
auxv    cwd          limits    net          schedstat  syscall
cgroup    environ       loginuid  oom_adj      sessionid  task
clear_refs              exe       maps         oom_score  smaps      wchan
cmdline   fd            mem       pagemap      stack
comm      fdinfo        mountinfo personality  stat
coredump_filter         io        mounts       root       statm

我们可以看到该目录下有这么些文件。其中attr、fd、fdinfo、task为目录,cwd、root为指向目录的链接,exe为指向文件的链接,其余为一般文件。对于一些文件或目录的权限(查看或者修改的权限)是该进程的创建者才有,例如auxv等文件或目录只有创建该进程的用户才有查看或修改的权限,而其他一些文件则对所有用户可读权限。关于这些文件或目录的权限,我们可以在内核中找到(fs/proc/base.c, tid_base_stuff数组)。

在2.6.35源码fs/proc/base.c中;http://lxr.linux.no/linux+v2.6.35/fs/proc/base.c
static const struct    pid_entry    tid_base_stuff[] = {
    DIR("fd", S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations),
    DIR("fdinfo", S_IRUSR|S_IXUSR, proc_fdinfo_inode_operations, proc_fdinfo_operations),
    REG("environ", S_IRUSR, proc_environ_operations),
    INF("auxv", S_IRUSR, proc_pid_auxv),
    ONE("status", S_IRUGO, proc_pid_status),
    ONE("personality", S_IRUSR, proc_pid_personality),
    INF("limits", S_IRUSR, proc_pid_limits),
#ifdef    CONFIG_SCHED_DEBUG
    REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations),
#endif
    REG("comm", S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),
#ifdef    CONFIG_HAVE_ARCH_TRACEHOOK
    INF("syscall", S_IRUSR, proc_pid_syscall),
#endif
    INF("cmdline", S_IRUGO, proc_pid_cmdline),
    ONE("stat", S_IRUGO, proc_tid_stat),
    ONE("statm", S_IRUGO, proc_pid_statm),
    REG("maps", S_IRUGO, proc_maps_operations),
#ifdef    CONFIG_NUMA
    REG("numa_maps", S_IRUGO, proc_numa_maps_operations),
#endif
    REG("mem", S_IRUSR|S_IWUSR, proc_mem_operations),
    LNK("cwd", proc_cwd_link),
    LNK("root", proc_root_link),
    LNK("exe", proc_exe_link),
    REG("mounts", S_IRUGO, proc_mounts_operations),
    REG("mountinfo", S_IRUGO, proc_mountinfo_operations),
#ifdef    CONFIG_PROC_PAGE_MONITOR
    REG("clear_refs", S_IWUSR, proc_clear_refs_operations),
    REG("smaps", S_IRUGO, proc_smaps_operations),
    REG("pagemap", S_IRUSR, proc_pagemap_operations),
#endif
#ifdef    CONFIG_SECURITY
    DIR("attr", S_IRUGO|S_IXUGO, proc_attr_dir_inode_operations, proc_attr_dir_operations),
#endif
#ifdef    CONFIG_KALLSYMS
    INF("wchan", S_IRUGO, proc_pid_wchan),
#endif
#ifdef    CONFIG_STACKTRACE
    ONE("stack", S_IRUSR, proc_pid_stack),
#endif
#ifdef    CONFIG_SCHEDSTATS
    INF("schedstat", S_IRUGO, proc_pid_schedstat),
#endif
#ifdef    CONFIG_LATENCYTOP
    REG("latency", S_IRUGO, proc_lstats_operations),
#endif
#ifdef    CONFIG_PROC_PID_CPUSET
    REG("cpuset", S_IRUGO, proc_cpuset_operations),
#endif
#ifdef    CONFIG_CGROUPS
    REG("cgroup", S_IRUGO, proc_cgroup_operations),
#endif
    INF("oom_score", S_IRUGO, proc_oom_score),
    REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adjust_operations),
#ifdef    CONFIG_AUDITSYSCALL
    REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
    REG("sessionid", S_IRUSR, proc_sessionid_operations),
#endif
#ifdef    CONFIG_FAULT_INJECTION
    REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),
#endif
#ifdef    CONFIG_TASK_IO_ACCOUNTING
    INF("io", S_IRUGO, proc_tid_io_accounting),
#endif
};

下面来详细每一个文件和目录的作用。

1.cmdline文件
该文件包含的是该进程的命令行参数,包括进程的启动路径(argv[0])。也就是说例如你在命令行上运行一个hello程序:

打开一个终端:

ywx@ywx:~/desktop$ cat hello.c
#include <stdio.h>
#include <wait.h>

int main()
{
    int i=0;
    for(i=0;i<100;i++)
    {
        printf("hello world\n");
        sleep(2);
    }
    return 0;
}
ywx@ywx:~/desktop$ gcc hello.c -o hello
ywx@ywx:~/desktop$ ./hello one two
hello world
hello world
打开另一个终端,查看当前进程的进程号:得知进程号为
ywx@ywx:~/Desktop$ ps -A | grep hello
 2752 pts/0    00:00:00 hello

然后进入/proc/2752进程下面:

ywx@ywx:~/Desktop$ cd /proc/2752
ywx@ywx:/proc/2752$ cat cmdline
./helloonetwoywx@ywx:/proc/2752$

可以看到cmdline里的内容为 “./helloonetwo”,正是命令行的参数。可能你会疑问为什么参数没有分开??那是因为cat欺骗了你。我们可以将cmdline复制到desktop下,然后用vim查看发现是这样的:

./hello^@one^@two^@

也就是说,实际上每个参数之间是有东西隔开的,只不过cat将其忽略而已,而vim可以给你标识出东西,但vim本身不显示罢了。我们可以通过编程读取该文件。下面给出程序。

 
我们一个字符一个字符的读取文件内容直到文件结束,在读取每一个字符的时候,打印其字符和对应的数值。
// ywx@ywx:~/desktop$ cat readcmd.c
#include <stdio.h>// std io fopen() snprintf() feof() perrof()
int main(int argc,char *argv[])
{
    FILE *fp;
    char path[80];
    unsigned char ch;
    snprintf(path,80,"/home/ywx/desktop/cmdline");//将cmdline中内容写入path数组中,并在path的结尾添 加字符结束符"\0"
    if((fp=fopen(path,"r")) == NULL)
    {
        perror("fopen");
        return 0;
    }
    while(!feof(fp)) //判断当前操作位置是否为文件的末尾,如果是,返回一个非零值
    {
        ch=fgetc(fp);//从stream流中读取一个字符,操作位置向下移动一个
        printf("%c %d\n",ch,ch);
    }
    fclose(fp);
    return 0;
}
ywx@ywx:~/Desktop$ gcc readcmd.c -o readcmd
ywx@ywx:~/Desktop$ ./readcmd 
. 46
/ 47
h 104
e 101
l 108
l 108
o 111
 0
o 111
n 110
e 101
 0
t 116
w 119
o 111
 0
� 255

由此我们可以看到并非是每个参数之间没有间隔,而是以字符"\0"作为间隔。所以如果我们在某一个程序中想读取进程的命令行参数,我们只需要知道该进程的pid,然后进入proc文件系统的该pid对应的目录下,编程读写读取cmdline文件就可以了。

 

 

### /proc/pid/cmdline 使用 `fopen` 失败的原因 当尝试通过 `fopen` 打开 `/proc/[pid]/cmdline` 文件时,可能会遇到失败的情况。这通常是由以下几个原因引起的: #### 1. 权限不足 `/proc/[pid]/cmdline` 是一个特殊文件,位于 Linux 的虚拟文件系统中。如果当前用户没有足够的权限来访问目标进程的 `cmdline` 文件,则会触发权限错误 (errno=13, Permission denied)[^1]。 #### 2. 进程不存在或已终止 如果目标 PID 对应的进程已经退出或者根本不存在,那么对该路径的操作将会返回 `-ENOENT` 错误 (errno=2, No such file or directory),此时调用 `fopen` 将无法成功[^2]。 #### 3. 特殊字符处理不当 某些情况下,`/proc/[pid]/cmdline` 中的内容是以 `\0` 分隔符组成的连续字符串序列。如果程序未正确解析这些分隔符,可能导致逻辑上的误解甚至崩溃[^4]。 --- ### 解决方案 针对上述可能的原因,以下是具体的解决办法: #### 方法一:检查并提升权限 确保运行程序的用户具有读取其他用户进程信息的能力。可以通过以下方式验证和调整: - 验证是否有权访问目标进程的相关资源。 - 如果需要更高的权限,考虑以 root 用户身份执行操作或将适当能力赋予特定二进制文件(如设置 SUID bit)[^1]。 #### 方法二:确认目标进程状态 在打开之前先判断目标 PID 是否有效且处于活动状态。可以借助 `kill(0)` 或者直接查询 `/proc/[pid]/status` 文件是否存在来进行检测: ```c #include <sys/types.h> #include <unistd.h> int check_pid_alive(pid_t pid) { if (kill(pid, 0)) { // 发送信号 '0' 不实际发送任何信号仅用于测试有效性 return 0; // 返回 false 表示进程不可达 } return 1; // 否则表示进程存活 } ``` #### 方法三:适配 cmdline 数据结构特点 由于 `/proc/[pid]/cmdline` 存储的数据形式较为独特——各参数间由 ASCII NUL 字节 (`\0`) 分割而成单一缓冲区内的多部分数据串列,因此需特别注意其读取后的解释过程: 下面展示了一个简单的例子说明如何安全地加载此类型的资料而不会因为格式问题引发异常行为: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> void read_cmdline(int pid){ char filename[64]; snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid); FILE *fp=fopen(filename,"r"); if(!fp){ perror("Failed to open"); exit(EXIT_FAILURE); } size_t len; ssize_t read_len; char* line=NULL; /* getline will allocate memory automatically */ read_len=getdelim(&line,&len,'\0',fp); printf("%s\n",strtok(line,"\0")); free(line); fclose(fp); } // Example usage within main function... read_cmdline(getppid()); ``` 此外还需留意一点就是关于软链接的目标地址获取方面的问题,在使用诸如 `readlink()` API 获取 `/proc/[pid]/exe` 路径的时候记得预留额外空间给 NULL 终止符以免越界风险发生[^5]. --- ### 总结 综上所述,造成 `/proc/[pid]/cmdline` 上 `fopen` 操作失败的主要因素包括但不限于权限限制、目标对象生命周期管理失误以及对于该类伪文件内部布局理解偏差等方面的影响。采取相应措施之后应该能够顺利解决问题。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值