• Linux 下获取进程所在文件的路径


    一、应用层

    1.1 /proc/pid/exe

    top进程为例:

    [root@localhost ~]# ps -ef | grep top
    root     31386 15859  0 14:58 pts/2    00:00:00 top
    
    • 1
    • 2

    top进程的pid为31386 ,可以通过查看 /proc/pid/exe:

    Linux系统中,每个进程都有一个/proc/pid/exe文件,它是一个符号链接文件,指向当前进程的可执行文件。

    更具体地说,/proc/pid/exe文件是一个符号链接文件,它的内容是一个指向当前进程可执行文件的绝对路径的符号链接。例如,如果当前进程的可执行文件是/usr/bin/myprogram,那么/proc/pid/exe文件的内容将是/usr/bin/myprogram的绝对路径。

    通过访问/proc/pid/exe文件,可以快速获取当前进程的可执行文件路径。

    需要注意的是,/proc/pid/exe文件是一个符号链接文件,它指向的路径可能会随着进程的可执行文件的变化而变化,例如在运行过程中升级可执行文件或链接库,因此在读取它的内容时,应该对返回值进行错误检查,以确保它确实指向当前进程的可执行文件。

    [root@localhost ~]# ls -l /proc/31386/exe
    lrwxrwxrwx. 1 root root 0 518 14:59 /proc/31386/exe -> /usr/bin/top
    
    • 1
    • 2

    其中/usr/bin/top为top进程可执行文件所在的绝对文件路径。

    由于/proc/31386/exe是符号链接,直接调用 readlink 命令获取该进程可执行文件所在的绝对文件路径:

    [root@localhost ~]# readlink /proc/31386/exe
    /usr/bin/top
    
    • 1
    • 2
    NAME
           readlink - print resolved symbolic links or canonical file names
           
            Print value of a symbolic link or canonical file name
    
    • 1
    • 2
    • 3
    • 4
    #include 
    #include 
    
    int main()
    {
        char task_absolute_path[PATH_MAX];
     
        int cnt = readlink( "/proc/self/exe", task_absolute_path, PATH_MAX);
        if (cnt < 0){
            printf("readlink is error\n");
            return -1;
        }
        task_absolute_path[cnt] = '\0';
        printf("task absolute path:%s\n", task_absolute_path);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    readlink()函数用于读取符号链接文件的内容,符号链接文件的内容就是一个路径,即解析符号链接。使用 readlink 读取符号链接,获取的就是符号链接的内容,符号链接的内容的就是目标文件的路径。

    当然对于top这种系统shell命令,可以用which和whereis查看:

    [root@localhost ~]# which top
    /usr/bin/top
    [root@localhost ~]# whereis top
    top: /usr/bin/top /usr/share/man/man1/top.1.gz
    
    • 1
    • 2
    • 3
    • 4

    对于普通程序通常用 /proc/pid/exe进行查看。

    1.2 /proc/pid/cwd

    在Linux系统中,每个进程都有一个/proc/pid/cwd文件,它是一个符号链接文件,指向当前进程的工作目录。
    如下所示,我在 /root/link/test1/ 目录下运行 top 命令。

    [root@localhost ~]# ls -l /proc/31386/cwd
    lrwxrwxrwx. 1 root root 0 518 15:02 /proc/31386/cwd -> /root/link/test1
    [root@localhost ~]# readlink /proc/31386/cwd
    /root/link/test1
    
    • 1
    • 2
    • 3
    • 4

    更具体地说,/proc/pid/cwd文件是一个符号链接文件,它的内容是一个指向进程号为 pid 工作目录的绝对路径的符号链接。例如,如果当前进程的工作目录是/home/user,那么/proc/pid/cwd文件的内容将是/home/user的绝对路径。

    通过访问/proc/pid/cwd文件,可以快速获取当前进程的工作目录路径,而无需在程序中调用getcwd()函数等获取当前工作目录的系统调用。

    需要注意的是,/proc/pid/cwd文件是一个符号链接文件,它指向的路径可能会随着进程的工作目录的变化而变化,因此在读取它的内容时,应该对返回值进行错误检查,以确保它确实指向当前进程的工作目录。

    比如函数 getcwd():

    
    NAME
           getcwd, getwd, get_current_dir_name - get current working directory
    
    SYNOPSIS
           #include 
    
           char *getcwd(char *buf, size_t size);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.3 代码示例

    接下来给出一段代码,根据输入的参数进程pid号,开获取该进程可执行程序的绝对路径和当前在哪个目录执行:

    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define PROC_PATH_LEN 64
    
    int main(int argc, char *argv[]) {
        if (argc != 2) {
            fprintf(stderr, "Usage: %s \n", argv[0]);
            exit(EXIT_FAILURE);
        }
    
        char proc_path[PROC_PATH_LEN] = {0};
        snprintf(proc_path, sizeof(proc_path), "/proc/%s/exe", argv[1]);
    
        char exe_path[PATH_MAX] = {0};
        ssize_t len = readlink(proc_path, exe_path, sizeof(exe_path));
        if (len == -1) {
            perror("readlink");
            exit(EXIT_FAILURE);
        }
        exe_path[len] = '\0';
        printf("Executable path: %s\n", exe_path);
    
        memset(proc_path, 0, PROC_PATH_LEN);
        snprintf(proc_path, sizeof(proc_path), "/proc/%s/cwd", argv[1]);
        char cwd_path[PATH_MAX] = {0};
        len = readlink(proc_path, cwd_path, sizeof(cwd_path));
        if (len == -1) {
            perror("readlink");
            exit(EXIT_FAILURE);
        }
        cwd_path[len] = '\0';
        printf("pid current path: %s\n", cwd_path);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    二、内核态获取

    2.1 相对应的函数与结构体

    struct fs_struct *fs 描述了文件系统和进程相关的信息:

    // linux-3.10/include/linux/sched.h
    
    struct task_struct {
    	......
    	/* filesystem information */
    	struct fs_struct *fs;
    	......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    // linux-3.10/include/linux/fs_struct.h
    
    struct fs_struct {
    	int users;
    	spinlock_t lock;
    	seqcount_t seq;
    	int umask;
    	int in_exec;
    	struct path root, pwd;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其中 struct path root 表示根目录路径,通常都是 / 目录,但是通过chroot系统调用后,对于进程来说会将 / 目录变成了某个子目录,那么相应的进程就是使用该子目录而不是全局的根目录,该进程会将该子目录当作其根目录。

    chroot - run command or interactive shell with special root directory
    
    Run COMMAND with root directory set to NEWROOT.
    
    • 1
    • 2
    • 3

    struct path pwd就是当前工作目录。

    // linux-3.10/include/linux/path.h
    
    struct path {
    	struct vfsmount *mnt;
    	struct dentry *dentry;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    // linux-3.10/include/linux/dcache.h
    
    /*
     * "quick string" -- eases parameter passing, but more importantly
     * saves "metadata" about the string (ie length and the hash).
     *
     * hash comes first so it snuggles against d_parent in the
     * dentry.
     */
    struct qstr {
    	......
    	const unsigned char *name;
    };
    
    struct dentry {
    	......
    	struct qstr d_name;
    	......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    // linux-3.10/include/linux/sched.h
    
    struct task_struct {
    	......
    	struct mm_struct *mm;
    	......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    从task_struct获取路径基本通过mm_struct这个结构,从中可以获取进程全路径。

    // 获取进程全路径
    task_struct->mm->exe_file->f_path
    
    • 1
    • 2

    将进程的所在的文件路径存储到 /proc//exe symlink中:

    // linux-3.10/include/linux/mm_types.h
    
    struct mm_struct {
    	......
    	/* store ref to file /proc//exe symlink points to */
    	struct file *exe_file;
    	......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    // linux-3.10/include/linux/fs.h
    
    struct file {
    	......
    	struct path		f_path;
    	......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    (1) 通过dentry_path_raw获取文件的全路径,低版本比如2.6.32没有该API

    // linux-3.10/fs/dcache.c
    
    static int prepend(char **buffer, int *buflen, const char *str, int namelen)
    {
    	*buflen -= namelen;
    	if (*buflen < 0)
    		return -ENAMETOOLONG;
    	*buffer -= namelen;
    	memcpy(*buffer, str, namelen);
    	return 0;
    }
    
    static int prepend_name(char **buffer, int *buflen, struct qstr *name)
    {
    	return prepend(buffer, buflen, name->name, name->len);
    }
    
    /*
     * Write full pathname from the root of the filesystem into the buffer.
     */
    static char *__dentry_path(struct dentry *dentry, char *buf, int buflen)
    {
    	char *end = buf + buflen;
    	char *retval;
    
    	prepend(&end, &buflen, "\0", 1);
    	if (buflen < 1)
    		goto Elong;
    	/* Get '/' right */
    	retval = end-1;
    	*retval = '/';
    
    	while (!IS_ROOT(dentry)) {
    		struct dentry *parent = dentry->d_parent;
    		int error;
    
    		prefetch(parent);
    		spin_lock(&dentry->d_lock);
    		error = prepend_name(&end, &buflen, &dentry->d_name);
    		spin_unlock(&dentry->d_lock);
    		if (error != 0 || prepend(&end, &buflen, "/", 1) != 0)
    			goto Elong;
    
    		retval = end;
    		dentry = parent;
    	}
    	return retval;
    Elong:
    	return ERR_PTR(-ENAMETOOLONG);
    }
    
    char *dentry_path_raw(struct dentry *dentry, char *buf, int buflen)
    {
    	char *retval;
    
    	write_seqlock(&rename_lock);
    	retval = __dentry_path(dentry, buf, buflen);
    	write_sequnlock(&rename_lock);
    
    	return retval;
    }
    EXPORT_SYMBOL(dentry_path_raw);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    struct file *filp;
    dentry_path_raw(filp->f_path.dentry,buf,buflen);
    
    • 1
    • 2

    (2)通过d_path获取文件的全路径

    // linux-3.10/fs/dcache.c
    
    /**
     * d_path - return the path of a dentry
     * @path: path to report
     * @buf: buffer to return value in
     * @buflen: buffer length
     *
     * Convert a dentry into an ASCII path name. If the entry has been deleted
     * the string " (deleted)" is appended. Note that this is ambiguous.
     *
     * Returns a pointer into the buffer or an error code if the path was
     * too long. Note: Callers should use the returned pointer, not the passed
     * in buffer, to use the name! The implementation often starts at an offset
     * into the buffer, and may leave 0 bytes at the start.
     *
     * "buflen" should be positive.
     */
    char *d_path(const struct path *path, char *buf, int buflen)
    {
    	char *res = buf + buflen;
    	struct path root;
    	int error;
    
    	/*
    	 * We have various synthetic filesystems that never get mounted.  On
    	 * these filesystems dentries are never used for lookup purposes, and
    	 * thus don't need to be hashed.  They also don't need a name until a
    	 * user wants to identify the object in /proc/pid/fd/.  The little hack
    	 * below allows us to generate a name for these objects on demand:
    	 */
    	if (path->dentry->d_op && path->dentry->d_op->d_dname)
    		return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
    
    	get_fs_root(current->fs, &root);
    	br_read_lock(&vfsmount_lock);
    	write_seqlock(&rename_lock);
    	error = path_with_deleted(path, &root, &res, &buflen);
    	write_sequnlock(&rename_lock);
    	br_read_unlock(&vfsmount_lock);
    	if (error < 0)
    		res = ERR_PTR(error);
    	path_put(&root);
    	return res;
    }
    EXPORT_SYMBOL(d_path);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    调用d_path函数文件的路径时,应该使用返回的指针而不是转递进去的参数 buf 。
    原因是该函数的实现通常从缓冲区的偏移量开始。

    内核中用到d_path的例子:

    // linux-3.10/include/linux/mm_types.h
    
    /*
     * This struct defines a memory VMM memory area. There is one of these
     * per VM-area/task.  A VM area is any part of the process virtual memory
     * space that has a special rule for the page-fault handlers (ie a shared
     * library, the executable area etc).
     */
    struct vm_area_struct {
    	......
    	struct file * vm_file;		/* File we map to (can be NULL). */
    	......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    // linux-3.10/include/linux/fs.h
    
    struct file {
    	......
    	struct path		f_path;
    	......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    // linux-3.10/mm/memory.c
    
    /*
     * Print the name of a VMA.
     */
    void print_vma_addr(char *prefix, unsigned long ip)
    {
    	struct mm_struct *mm = current->mm;
    	struct vm_area_struct *vma;
    
    	/*
    	 * Do not print if we are in atomic
    	 * contexts (in exception stacks, etc.):
    	 */
    	if (preempt_count())
    		return;
    
    	down_read(&mm->mmap_sem);
    	vma = find_vma(mm, ip);
    	if (vma && vma->vm_file) {
    		struct file *f = vma->vm_file;
    		//使用伙伴系统接口,分配一个物理页,返回一个内核虚拟地址
    		char *buf = (char *)__get_free_page(GFP_KERNEL);
    		if (buf) {
    			char *p;
    
    			p = d_path(&f->f_path, buf, PAGE_SIZE);
    			if (IS_ERR(p))
    				p = "?";
    			printk("%s%s[%lx+%lx]", prefix, kbasename(p),
    					vma->vm_start,
    					vma->vm_end - vma->vm_start);
    			free_page((unsigned long)buf);
    		}
    	}
    	up_read(&mm->mmap_sem);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    2.2 API演示

    在这里只是简单的给出怎么在内核态获取进程所在文件的路径,详细的话请参考内核源码,在第三节给出内核源码获取进程所在文件的路径的方法。

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define TASK_PATH_MAX_LENGTH 512
    
    //内核模块初始化函数
    static int __init lkm_init(void)
    {
        struct qstr root_task_path;
        struct qstr current_task_path;
    
        char buf_1[TASK_PATH_MAX_LENGTH] = {0};
        char *task_path_1 = NULL;
    
        char buf_2[TASK_PATH_MAX_LENGTH] = {0};
        char *task_path_2 = NULL;
    
    	//获取当前目录名
        current_task_path = current->fs->pwd.dentry->d_name;
        //获取根目录
        root_task_path = current->fs->root.dentry->d_name;
    
    	//内核线程的 mm 成员为空,这里没做判断
    	
        //2.6.32 没有dentry_path_raw API
        //获取文件全路径
        task_path_1 = dentry_path_raw(current->mm->exe_file->f_path.dentry, buf_1, TASK_PATH_MAX_LENGTH);
    
    	//获取文件全路径
    	//调用d_path函数文件的路径时,应该使用返回的指针:task_path_2 ,而不是转递进去的参数buf:buf_2
        task_path_2 = d_path(&current->mm->exe_file->f_path, buf_2, TASK_PATH_MAX_LENGTH);
        if (IS_ERR(task_path_2)) {
            printk("Get path failed\n");
            return -1;
        }
    
        printk("current path = %s\n", current_task_path.name);
        printk("root path = %s\n", root_task_path.name);
        printk("task_path_1 = %s\n", task_path_1);
        printk("task_path_2 = %s\n", task_path_2);
    
    	return -1;
    }
    
    module_init(lkm_init);
    
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    结果展示:

    [root@localhost task_path]# dmesg -c
    [415299.952165] current path = task_path
    [415299.952172] root path = /
    [415299.952176] task_path_1 = /usr/bin/kmod
    [415299.952179] task_path_2 = /usr/bin/kmod
    
    • 1
    • 2
    • 3
    • 4
    • 5

    三、内核源码实现

    // linux-3.10/fs/proc/base.c
    
    /* NOTE:
     *	Implementing inode permission operations in /proc is almost
     *	certainly an error.  Permission checks need to happen during
     *	each system call not at open time.  The reason is that most of
     *	what we wish to check for permissions in /proc varies at runtime.
     *
     *	The classic example of a problem is opening file descriptors
     *	in /proc for a task before it execs a suid executable.
     */
    
    struct pid_entry {
    	char *name;
    	int len;
    	umode_t mode;
    	const struct inode_operations *iop;
    	const struct file_operations *fop;
    	union proc_op op;
    };
    
    static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
    {
    	struct task_struct *task;
    	struct mm_struct *mm;
    	struct file *exe_file;
    
    	task = get_proc_task(dentry->d_inode);
    	if (!task)
    		return -ENOENT;
    	mm = get_task_mm(task);
    	put_task_struct(task);
    	if (!mm)
    		return -ENOENT;
    	exe_file = get_mm_exe_file(mm);
    	mmput(mm);
    	if (exe_file) {
    		*exe_path = exe_file->f_path;
    		path_get(&exe_file->f_path);
    		fput(exe_file);
    		return 0;
    	} else
    		return -ENOENT;
    }
    
    /*
     * Tasks
     */
    static const struct pid_entry tid_base_stuff[] = {
    	DIR("fd",        S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations),
    	......
    	REG("comm",      S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),
    	......
    	LNK("cwd",       proc_cwd_link),
    	LNK("root",      proc_root_link),
    	LNK("exe",       proc_exe_link),
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    // linux-3.10/fs/proc/base.c
    
    static int do_proc_readlink(struct path *path, char __user *buffer, int buflen)
    {
    	//由于这里申请的是一个页大小,便没有使用 kmalloc接口,调用伙伴系统接口分配一个物理页,返回内核虚拟地址
    	char *tmp = (char*)__get_free_page(GFP_TEMPORARY);
    	char *pathname;
    	int len;
    
    	if (!tmp)
    		return -ENOMEM;
    
    	//获取进程所在文件的路径
    	pathname = d_path(path, tmp, PAGE_SIZE);
    	len = PTR_ERR(pathname);
    	if (IS_ERR(pathname))
    		goto out;
    	len = tmp + PAGE_SIZE - 1 - pathname;
    
    	if (len > buflen)
    		len = buflen;
    		
    	//把进程所在文件的路径拷贝到用户空间:char __user *buffer
    	//用户空间调用 readlink /proc/pid/
    	if (copy_to_user(buffer, pathname, len))
    		len = -EFAULT;
     out:
    	free_page((unsigned long)tmp);
    	return len;
    }
    
    static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int buflen)
    {
    	int error = -EACCES;
    	struct inode *inode = dentry->d_inode;
    	struct path path;
    
    	/* Are we allowed to snoop on the tasks file descriptors? */
    	if (!proc_fd_access_allowed(inode))
    		goto out;
    
    	error = PROC_I(inode)->op.proc_get_link(dentry, &path);
    	if (error)
    		goto out;
    
    	error = do_proc_readlink(&path, buffer, buflen);
    	path_put(&path);
    out:
    	return error;
    }
    
    const struct inode_operations proc_pid_link_inode_operations = {
    	.readlink	= proc_pid_readlink,
    	.follow_link	= proc_pid_follow_link,
    	.setattr	= proc_setattr,
    };
    
    
    // linux-3.10/fs/proc/internal.h
    union proc_op {
    	int (*proc_get_link)(struct dentry *, struct path *);
    	int (*proc_read)(struct task_struct *task, char *page);
    	int (*proc_show)(struct seq_file *m,
    		struct pid_namespace *ns, struct pid *pid,
    		struct task_struct *task);
    };
    
    struct proc_inode {
    	struct pid *pid;
    	int fd;
    	union proc_op op;
    	struct proc_dir_entry *pde;
    	struct ctl_table_header *sysctl;
    	struct ctl_table *sysctl_entry;
    	struct proc_ns ns;
    	struct inode vfs_inode;
    };
    
    /*
     * General functions
     */
    static inline struct proc_inode *PROC_I(const struct inode *inode)
    {
    	return container_of(inode, struct proc_inode, vfs_inode);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    其中:

    proc_pid_readlink()
    	-->do_proc_readlink(){
    			char __user *buffer;
    			copy_to_user(buffer, pathname, len);
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    比如:当用户调用 readlink /proc/pid/exe,将该文件内容拷贝到用户空间:char __user *buffer中。

    参考资料

    Linux 3.10

    https://blog.csdn.net/qq_42931917/article/details/119803534
    https://blog.csdn.net/cenziboy/article/details/8761621
    https://blog.csdn.net/whatday/article/details/100638552

  • 相关阅读:
    【C++】bug之vector subscript out of range
    国芯方案——电子吊钩秤方案
    简单工厂、工厂方法、抽象工厂、抽象工厂加简单工厂
    C++项目:【负载均衡式在线OJ】
    SpringMVC简介
    【树莓派不吃灰】命令篇③ 学习Shell脚本
    字节跳动资深架构师整理2022年秋招最新面试题汇总:208页核心体系
    DT科幻建筑建模
    LeetCode_队列_中等_649.Dota2 参议院
    一幅长文细学Redis(一)——NoSQL数据库简介以及Redis安装
  • 原文地址:https://blog.csdn.net/weixin_45030965/article/details/127659145