<Android> Android与Kernel之间使用proc/sysfs结点通信

Android与Kernel通信机制

Android与Kernel使用结点进行通信.

Android与Kernel通信

Linux中一切皆文件, Android上层与Kernel通信可以使用结点.

public boolean writeProcCmd(int cmd, int data) {
        String new_proc_path = "/proc/mydebug";
        File procFile = new File(new_proc_path);
        try {
            FileOutputStream fw = new FileOutputStream(procFile);
            byte buf[] = new byte[2];
            buf[0] = (byte)cmd;
            buf[1] = (byte)data;
            fw.write(buf);
            fw.flush();
            fw.close();
            return true;
        } catch (IOException e) {
            Log.e(MYTAG, "open output or input stream error");
            return false;
        }
    }

     public int readProcCmd(byte[] buf) {
        String new_proc_path = "/proc/mydebug";
        File procFile = new File(new_proc_path);

        try {
            FileInputStream fr = new FileInputStream(procFile);
            int ret = fr.read(buf);
            fr.close();
            if (ret < 0) {
                Log.e(MYTAG, "proc read input stream error");
                return -1 ;
            }
            return ret;
        } catch (IOException e) {
            Log.e(MYTAG, "open output or input stream error");
            return -1;
        }
    }

Kernel模块与模块通信

同理kernel间模块之间通讯也可以采用结点通讯的方法进行通讯.

#define IDC_BOARD_PROC          "/proc/mydebug"
#define PROC_MYCMD_1                    30
#define PROC_MYCMD_2                    31

static int fts_send_sd_cmd(int present) 
{
    struct file *idcfile;
    mm_segment_t old_fs;
    char opbuf[2] = {0};
    int ret;

    idcfile = filp_open(IDC_BOARD_PROC, O_RDWR, 0);

    if (IS_ERR(idcfile)) {
        pr_err("Open %s failed!\n", IDC_BOARD_PROC);
        return -EINVAL;
    }
    old_fs = get_fs();
    set_fs(KERNEL_DS);

    if (present) {
        opbuf[0] = PROC_MYCMD_1;
    } else {
        opbuf[0] = PROC_MYCMD_2;
    }

    ret = idcfile->f_op->write(idcfile, opbuf, 1, &idcfile->f_pos);

    filp_close(idcfile, NULL);
    set_fs(old_fs);
    if (ret < 0) {
        pr_err("send cmd failed, ret: %d\n", ret);
        return -EINVAL;
    }
    pr_err("send cmd success\n");

    return 0;
}

Kernel端创建结点

 1. 创建proc结点(注意有linux版本区别)


struct kernel_ts_data {
    struct i2c_client *client;
    struct proc_dir_entry *proc;
};
#define PROC_NAME   "mydebug"
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
/************************************************************************
*   Name: kernel_debug_write
*  Brief:interface of write proc
* Input: file point, data buf, data len, no use
* Output: no
* Return: data len
***********************************************************************/
static ssize_t kernel_debug_write(struct file *filp, const char __user *buff, size_t count, loff_t *ppos)
{
    u8 writebuf[PROC_WRITE_BUF_SIZE] = { 0 };
    int buflen = count;
    int writelen = 0;
    int ret = 0;
    char tmp[25];
    struct kernel_ts_data *ts_data = kernel_data;
    struct i2c_client *client = ts_data->client;

    if ((count == 0) || (count > PROC_WRITE_BUF_SIZE)) {
        pr_err("proc wirte count(%d) fail", (int)count);
        return -EINVAL;
    }

    if (copy_from_user(&writebuf, buff, count)) {
        pr_err("copy from user error!!");
        return -EFAULT;
    }

    ts_data->proc_opmode = writebuf[0];

    switch (ts_data->proc_opmode) {
    case PROC_READ_DATA:
    case PROC_WRITE_DATA:
        writelen = buflen - 1;
        if (writelen > 0) {
            ret = kernel_i2c_write(client, writebuf + 1, writelen);
            if (ret < 0) {
                pr_err("write iic error!!");
            }
        }
        break;
    default:
        break;
    }

    if (ret < 0) {
        return ret;
    } else {
        return count;
    }
}

/************************************************************************
*   Name: kernel_debug_read
*  Brief:interface of read proc
* Input: point to the data, no use, no use, read len, no use, no use
* Output: page point to data
* Return: read char number
***********************************************************************/
static ssize_t kernel_debug_read(struct file *filp, char __user *buff, size_t count, loff_t *ppos)
{
    int ret = 0;
    int num_read_chars = 0;
    int readlen = 0;
    u8 buf[PROC_READ_BUF_SIZE] = { 0 };
    struct kernel_ts_data *ts_data = kernel_data;
    struct i2c_client *client = ts_data->client;

    if ((count == 0) || (count > PROC_READ_BUF_SIZE)) {
        pr_err("proc read count(%d) fail", (int)count);
        return -EINVAL;
    }

    switch (ts_data->proc_opmode) {
    case PROC_READ_DATA:
        readlen = count;
        ret = kernel_i2c_read(client, NULL, 0, buf, readlen);
        if (ret < 0) {
            pr_err("read iic error!!");
            return ret;
        }

        num_read_chars = readlen;
        break;
    case PROC_WRITE_DATA:
        break;
    default:
        break;
    }

    if (copy_to_user(buff, buf, num_read_chars)) {
        pr_err("copy to user error!!");
        return -EFAULT;
    }

    return num_read_chars;
}

static const struct file_operations kernel_proc_fops = {
    .owner  = THIS_MODULE,
    .read   = kernel_debug_read,
    .write  = kernel_debug_write,
};
#else
/* interface of write proc */
/************************************************************************
*   Name: kernel_debug_write
*  Brief:interface of write proc
* Input: file point, data buf, data len, no use
* Output: no
* Return: data len
***********************************************************************/
static int kernel_debug_write(struct file *filp,
                           const char __user *buff, unsigned long len, void *data)
{
    int ret = 0;
    u8 writebuf[PROC_WRITE_BUF_SIZE] = { 0 };
    int buflen = len;
    int writelen = 0;
    struct kernel_ts_data *ts_data = kernel_data;
    struct i2c_client *client = ts_data->client;

    if ((count == 0) || (count > PROC_WRITE_BUF_SIZE)) {
        pr_err("proc wirte count(%d) fail", (int)count);
        return -EINVAL;
    }

    if (copy_from_user(&writebuf, buff, buflen)) {
        pr_err("copy from user error!!");
        return -EFAULT;
    }

    ts_data->proc_opmode = writebuf[0];

    switch (ts_data->proc_opmode) {

    case PROC_READ_DATA:
    case PROC_WRITE_DATA:
        writelen = len - 1;
        if (writelen > 0) {
            ret = kernel_i2c_write(client, writebuf + 1, writelen);
            if (ret < 0) {
                pr_err(" write iic error!!");
            }
        }
        break;
    default:
        break;
    }

    if (ret < 0) {
        return ret;
    } else {
        return len;
    }
}

/* interface of read proc */
/************************************************************************
*   Name: kernel_debug_read
*  Brief:interface of read proc
* Input: point to the data, no use, no use, read len, no use, no use
* Output: page point to data
* Return: read char number
***********************************************************************/
static int kernel_debug_read( char *page, char **start,
                           off_t off, int count, int *eof, void *data )
{
    int ret = 0;
    u8 buf[PROC_READ_BUF_SIZE] = { 0 };
    int num_read_chars = 0;
    int readlen = 0;
    struct kernel_ts_data *ts_data = kernel_data;
    struct i2c_client *client = ts_data->client;

    if ((count == 0) || (count > PROC_READ_BUF_SIZE)) {
        pr_err("proc read count(%d) fail", (int)count);
        return -EINVAL;
    }

    switch (ts_data->proc_opmode) {
    case PROC_READ_DATA:
        readlen = count;
        ret = kernel_i2c_read(client, NULL, 0, buf, readlen);
        if (ret < 0) {
            pr_err("read iic error!!");
            return ret;
        }
        num_read_chars = readlen;
        break;
    case PROC_WRITE_DATA:
        break;
    default:
        break;
    }
    memcpy(page, buf, num_read_chars);
    return num_read_chars;
}
#endif

/************************************************************************
* Name: kernel_create_apk_debug_channel
* Brief:  create apk debug channel
* Input: i2c info
* Output:
* Return: return 0 if success
***********************************************************************/
int kernel_create_apk_debug_channel(struct kernel_ts_data *ts_data)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
    ts_data->proc = proc_create(PROC_NAME, 0777, NULL, &kernel_proc_fops);
#else
    ts_data->proc = create_proc_entry(PROC_NAME, 0777, NULL);
#endif
    if (NULL == ts_data->proc) {
        pr_err("create proc entry fail");
        return -ENOMEM;
    } else {
        pr_info("Create proc entry success!");

#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0))
        ts_data->proc->write_proc = kernel_debug_write;
        ts_data->proc->read_proc = kernel_debug_read;
#endif
    }

    return 0;
}

/************************************************************************
* Name: kernel_release_apk_debug_channel
* Brief:  release apk debug channel
* Input:
* Output:
* Return:
***********************************************************************/
void kernel_release_apk_debug_channel(struct kernel_ts_data *ts_data)
{

    if (ts_data->proc) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
        proc_remove(ts_data->proc);
#else
        remove_proc_entry(PROC_NAME, NULL);
#endif
    }
}


2. 创建sysfs结点, 以I2C设备为例

enum {
    RWREG_OP_READ = 0,
    RWREG_OP_WRITE = 1,
};
static struct rwreg_operation_t {
    int type;         /*  0: read, 1: write */
    int reg;        /*  register */
    int len;        /*  read/write length */
    int val;      /*  length = 1; read: return value, write: op return */
    int res;     /*  0: success, otherwise: fail */
    char *opbuf;        /*  length >= 1, read return value, write: op return */
} rw_op;
/************************************************************************
* Name: kernel_rwreg_show
* Brief:  no
* Input: device, device attribute, char buf
* Output: no
* Return: EPERM
***********************************************************************/
static ssize_t kernel_rwreg_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    int count;
    int i;
    struct input_dev *input_dev = kernel_data->input_dev;

    mutex_lock(&input_dev->mutex);

    if (rw_op.len < 0) {
        count = snprintf(buf, PAGE_SIZE, "Invalid cmd line\n");
    } else if (rw_op.len == 1) {
        if (RWREG_OP_READ == rw_op.type) {
            if (rw_op.res == 0) {
                count = snprintf(buf, PAGE_SIZE, "Read %02X: %02X\n", rw_op.reg, rw_op.val);
            } else {
                count = snprintf(buf, PAGE_SIZE, "Read %02X failed, ret: %d\n", rw_op.reg,  rw_op.res);
            }
        } else {
            if (rw_op.res == 0) {
                count = snprintf(buf, PAGE_SIZE, "Write %02X, %02X success\n", rw_op.reg,  rw_op.val);
            } else {
                count = snprintf(buf, PAGE_SIZE, "Write %02X failed, ret: %d\n", rw_op.reg,  rw_op.res);
            }
        }
    } else {
        if (RWREG_OP_READ == rw_op.type) {
            count = snprintf(buf, PAGE_SIZE, "Read Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len);
            count += snprintf(buf + count, PAGE_SIZE, "Result: ");
            if (rw_op.res) {
                count += snprintf(buf + count, PAGE_SIZE, "failed, ret: %d\n", rw_op.res);
            } else {
                if (rw_op.opbuf) {
                    for (i = 0; i < rw_op.len; i++) {
                        count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]);
                    }
                    count += snprintf(buf + count, PAGE_SIZE, "\n");
                }
            }
        } else {
            ;
            count = snprintf(buf, PAGE_SIZE, "Write Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len - 1);
            count += snprintf(buf + count, PAGE_SIZE, "Write Data: ");
            if (rw_op.opbuf) {
                for (i = 1; i < rw_op.len; i++) {
                    count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]);
                }
                count += snprintf(buf + count, PAGE_SIZE, "\n");
            }
            if (rw_op.res) {
                count += snprintf(buf + count, PAGE_SIZE, "Result: failed, ret: %d\n", rw_op.res);
            } else {
                count += snprintf(buf + count, PAGE_SIZE, "Result: success\n");
            }
        }
        /*if (rw_op.opbuf) {
            kernel_kfree(rw_op.opbuf);
            rw_op.opbuf = NULL;
        }*/
    }
    mutex_unlock(&input_dev->mutex);

    return count;
}

static int shex_to_int(const char *hex_buf, int size)
{
    int i;
    int base = 1;
    int value = 0;
    char single;

    for (i = size - 1; i >= 0; i--) {
        single = hex_buf[i];

        if ((single >= '0') && (single <= '9')) {
            value += (single - '0') * base;
        } else if ((single >= 'a') && (single <= 'z')) {
            value += (single - 'a' + 10) * base;
        } else if ((single >= 'A') && (single <= 'Z')) {
            value += (single - 'A' + 10) * base;
        } else {
            return -EINVAL;
        }

        base *= 16;
    }

    return value;
}


static u8 shex_to_u8(const char *hex_buf, int size)
{
    return (u8)shex_to_int(hex_buf, size);
}
/*
 * Format buf:
 * [0]: '0' write, '1' read(reserved)
 * [1-2]: addr, hex
 * [3-4]: length, hex
 * [5-6]...[n-(n+1)]: data, hex
 */
static int kernel_parse_buf(const char *buf, size_t cmd_len)
{
    int length;
    int i;
    char *tmpbuf;

    rw_op.reg = shex_to_u8(buf + 1, 2);
    length = shex_to_int(buf + 3, 2);

    if (buf[0] == '1') {
        rw_op.len = length;
        rw_op.type = RWREG_OP_READ;
        kernel_DEBUG("read %02X, %d bytes", rw_op.reg, rw_op.len);
    } else {
        if (cmd_len < (length * 2 + 5)) {
            pr_err("data invalided!\n");
            return -EINVAL;
        }
        kernel_DEBUG("write %02X, %d bytes", rw_op.reg, length);

        /* first byte is the register addr */
        rw_op.type = RWREG_OP_WRITE;
        rw_op.len = length + 1;
    }

    if (rw_op.len > 0) {
        tmpbuf = (char *)kzalloc(rw_op.len, GFP_KERNEL);
        if (!tmpbuf) {
            pr_err("allocate memory failed!\n");
            return -ENOMEM;
        }

        if (RWREG_OP_WRITE == rw_op.type) {
            tmpbuf[0] = rw_op.reg & 0xFF;
            kernel_DEBUG("write buffer: ");
            for (i = 1; i < rw_op.len; i++) {
                tmpbuf[i] = shex_to_u8(buf + 5 + i * 2 - 2, 2);
                kernel_DEBUG("buf[%d]: %02X", i, tmpbuf[i] & 0xFF);
            }
        }
        rw_op.opbuf = tmpbuf;
    }

    return rw_op.len;
}



/************************************************************************
* Name: kernel_rwreg_store
* Brief:  read/write register
* Input: device, device attribute, char buf, char count
* Output: print register value
* Return: char count
***********************************************************************/
static ssize_t kernel_rwreg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    struct input_dev *input_dev = kernel_data->input_dev;
    struct i2c_client *client = container_of(dev, struct i2c_client, dev);
    ssize_t cmd_length = 0;

    mutex_lock(&input_dev->mutex);
    cmd_length = count - 1;

    if (rw_op.opbuf) {
        kernel_kfree(rw_op.opbuf);
        rw_op.opbuf = NULL;
    }

    kernel_DEBUG("cmd len: %d, buf: %s", (int)cmd_length, buf);
    /* compatible old ops */
    if (2 == cmd_length) {
        rw_op.type = RWREG_OP_READ;
        rw_op.len = 1;

        rw_op.reg = shex_to_int(buf, 2);
    } else if (4 == cmd_length) {
        rw_op.type = RWREG_OP_WRITE;
        rw_op.len = 1;
        rw_op.reg = shex_to_int(buf, 2);
        rw_op.val = shex_to_int(buf + 2, 2);

    } else if (cmd_length < 5) {
        pr_err("Invalid cmd buffer");
        mutex_unlock(&input_dev->mutex);
        return -EINVAL;
    } else {
        rw_op.len = kernel_parse_buf(buf, cmd_length);
    }

    if (rw_op.len < 0) {
        pr_err("cmd buffer error!");

    } else {
        if (RWREG_OP_READ == rw_op.type) {
            if (rw_op.len == 1) {
                u8 reg, val;
                reg = rw_op.reg & 0xFF;
                rw_op.res = kernel_i2c_read_reg(client, reg, &val);
                rw_op.val = val;
            } else {
                char reg;
                reg = rw_op.reg & 0xFF;

                rw_op.res = kernel_i2c_read(client, &reg, 1, rw_op.opbuf, rw_op.len);
            }

            if (rw_op.res < 0) {
                pr_err("Could not read 0x%02x", rw_op.reg);
            } else {
                pr_info("read 0x%02x, %d bytes successful", rw_op.reg, rw_op.len);
                rw_op.res = 0;
            }

        } else {
            if (rw_op.len == 1) {
                u8 reg, val;
                reg = rw_op.reg & 0xFF;
                val = rw_op.val & 0xFF;
                rw_op.res = kernel_i2c_write_reg(client, reg, val);
            } else {
                rw_op.res = kernel_i2c_write(client, rw_op.opbuf, rw_op.len);
            }
            if (rw_op.res < 0) {
                pr_err("Could not write 0x%02x", rw_op.reg);

            } else {
                pr_info("Write 0x%02x, %d bytes successful", rw_op.val, rw_op.len);
                rw_op.res = 0;
            }
        }
    }

    mutex_unlock(&input_dev->mutex);

    return count;
}
/* add your attr in here*/
static struct attribute *fts_attributes[] = {
    &dev_attr_rw_reg.attr,
    NULL
};

static struct attribute_group fts_attribute_group = {
    .attrs = fts_attributes
};
/************************************************************************
* Name: kernel_create_sysfs
* Brief: create sysfs interface
* Input:
* Output:
* Return: return 0 if success
***********************************************************************/
int kernel_create_sysfs(struct i2c_client *client)
{
    int ret = 0;

    ret = sysfs_create_group(&client->dev.kobj, &kernel_attribute_group);
    if (ret) {
        pr_err("[EX]: sysfs_create_group() failed!!");
        sysfs_remove_group(&client->dev.kobj, &kernel_attribute_group);
        return -ENOMEM;
    } else {
        pr_info("[EX]: sysfs_create_group() succeeded!!");
    }

    return ret;
}
/************************************************************************
* Name: kernel_remove_sysfs
* Brief: remove sysfs interface
* Input:
* Output:
* Return:
***********************************************************************/
int kernel_remove_sysfs(struct i2c_client *client)
{
    sysfs_remove_group(&client->dev.kobj, &kernel_attribute_group);
    return 0;
}
<think>好的,我现在需要帮助用户解决如何配置Linux内核以使/proc/<PID>/io文件可见的问题。首先,我需要回忆一下之前关于pidstat和/proc文件系统的讨论。用户之前询问了pidstat的实现原理,我解释了/proc文件系统中各种文件和目录的作用,比如/proc/<PID>/io用于进程的I/O统计。现在用户的问题更具体,涉及到如何配置内核来启用这个文件。 首先,我需要确认/proc/<PID>/io的可见性是否默认启用。根据之前的引用资料和Linux内核文档,常大多数/proc下的文件是默认启用的,但可能某些配置选项或内核版本会影响它们的可见性。比如,如果内核编译时关闭了相关选项,这些文件可能不会生成。 接下来,我需要考虑用户可能遇到的问题。用户可能发现某个特定的Linux系统上没有/proc/<PID>/io文件,想知道如何配置内核来启用它。可能的原因包括内核配置选项未开启、权限问题,或者使用的内核版本不支持该功能。 我需要检查内核配置选项中I/O统计相关的部分。常,这些选项可能在General setup或Kernel hacking部分。例如,CONFIG_TASK_IO_ACCOUNTING是控制任务I/O统计的关键选项。如果这个选项被禁用,/proc/<PID>/io就不会被生成。 然后,我需要指导用户如何检查当前内核的配置,可以过查看/boot/config-$(uname -r)文件或使用zcat /proc/config.gz(如果启用了CONFIG_IKCONFIG_PROC)。如果发现CONFIG_TASK_IO_ACCOUNTING未启用,用户需要重新编译内核并启用该选项。 此外,还要考虑内核版本的问题。旧版本的内核可能不支持该功能,用户可能需要升级内核。另外,权限问题也可能导致文件不可见,需要检查文件权限和SELinux/apparmor等安全模块的设置。 在提供步骤时,应该详细说明如何配置内核选项,包括过make menuconfig或直接编辑.config文件,然后重新编译和安装内核。同时提醒用户备份数据,因为编译内核有风险。 最后,需要验证配置是否成功,检查/proc/<PID>/io是否存在,并测试其内容。可能还需要考虑其他相关选项,如CONFIG_PROC_FS是否启用,确保整个proc文件系统已启用。 总结起来,回答应包括检查当前配置、配置内核选项、重新编译内核、处理权限和可能的安全模块,以及验证步骤。同时,需要提醒用户注意内核编译的风险,并根据其具体情况调整建议。</think>### 如何配置Linux内核使`/proc/<PID>/io`文件可见 `/proc/<PID>/io`文件是Linux内核提供的进程级I/O统计接口,其可见性依赖于**内核编译时的配置选项**和**内核功能模块的启用**。以下是完整的配置步骤和原理分析: --- #### 一、核心配置选项 `/proc/<PID>/io`的生成由以下内核配置选项控制: 1. **`CONFIG_TASK_IO_ACCOUNTING`** - **作用**:启用进程级I/O统计功能(包括`rchar`、`wchar`、`read_bytes`等字段)[^1] - **依赖项**: - `CONFIG_TASKSTATS`(进程统计信息支持) - `CONFIG_PROC_FS`(proc文件系统支持) 2. **`CONFIG_PROC_PID_IO`**(部分内核版本中独立存在) - **作用**:显式控制`/proc/<PID>/io`文件的生成 --- #### 二、配置步骤 ##### 1. 检查当前内核配置 过以下命令验证当前内核是否支持该功能: ```bash # 查看当前内核配置 grep -E "CONFIG_TASK_IO_ACCOUNTING|CONFIG_PROC_PID_IO" /boot/config-$(uname -r) # 若输出结果为: # CONFIG_TASK_IO_ACCOUNTING=y # CONFIG_PROC_PID_IO=y # 则表示已启用;若为`=n`或未找到则需重新配置 ``` ##### 2. 修改内核配置 **方法一:过`make menuconfig`配置** ```bash cd /usr/src/linux # 进入内核源码目录 make menuconfig ``` 导航至: ``` General Setup -> Taskstats (CONFIG_TASKSTATS) [必须启用] -> Enable per-task storage I/O accounting (CONFIG_TASK_IO_ACCOUNTING) [启用] Processor type and features -> /proc/<PID>/io support (CONFIG_PROC_PID_IO) [若存在则启用] ``` **方法二:直接编辑`.config`文件** 在源码目录中手动修改: ```bash echo "CONFIG_TASK_IO_ACCOUNTING=y" >> .config echo "CONFIG_PROC_PID_IO=y" >> .config # 若存在此选项 ``` ##### 3. 编译并安装内核 ```bash make -j$(nproc) # 编译内核 make modules_install # 安装模块 make install # 安装内核 reboot # 重启系统 ``` ##### 4. 验证配置 重启后检查: ```bash # 检查新内核是否生效 uname -r # 验证/proc/<PID>/io是否存在 cat /proc/$$/io # 查看当前shell进程的I/O统计 ``` --- #### 三、特殊情况处理 1. **内核版本差异** - **Linux 5.3+**:`CONFIG_TASK_IO_ACCOUNTING`默认启用[^2] - **旧内核(如4.x)**:需显式启用上述选项 2. **权限问题** `/proc/<PID>/io`文件权限为`-r--r--r--`,普用户需过`sudo`访问: ```bash sudo cat /proc/1/io # 查看PID=1的进程I/O统计 ``` 3. **安全模块干扰** 若启用了SELinux或AppArmor,需调整策略允许访问: ```bash # SELinux临时允许访问 setenforce 0 # 或添加永久规则 audit2allow -a -M proc_io_access ``` --- #### 四、调试扩展 - **内核日志分析** 若`/proc/<PID>/io`仍不可见,检查内核日志: ```bash dmesg | grep -i "proc_io\|task_io_accounting" ``` - **动态模块支持** 若编译为模块(`=m`),需加载相关模块: ```bash modprobe taskstats ``` --- ### 关键配置选项总结 | 配置项 | 功能描述 | 默认状态(典型发行版) | |---------------------------|----------------------------|-----------------------| | `CONFIG_TASK_IO_ACCOUNTING` | 启用进程I/O统计 | 多数启用 | | `CONFIG_PROC_PID_IO` | 控制`/proc/<PID>/io`生成 | 依赖内核版本 | | `CONFIG_PROC_FS` | 启用整个proc文件系统 | 强制启用 | --- ### 参考资料 - **Linux内核文档**:`Documentation/filesystems/proc.txt` - **内核编译指南**:https://www.kernel.org/doc/html/latest/admin-guide/README.html - **sysstat工具依赖**:https://github.com/sysstat/sysstat/wiki/Proc-Interface --- --相关问题-- 1. 如何过内核模块动态启用进程I/O统计? 2. `/proc/<PID>/io`中各字段的具体含义是什么? 3. 如何在内核中扩展`/proc`接口以添加自定义统计信息?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值