<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel rdf:about="https://dwt.life/feed/rss/tag/kernel/">
<title>dwt&#039;s life - kernel</title>
<link>https://dwt.life/tag/kernel/</link>
<description></description>
<items>
<rdf:Seq>
<rdf:li resource="https://dwt.life/archives/29/"/>
<rdf:li resource="https://dwt.life/archives/28/"/>
<rdf:li resource="https://dwt.life/archives/25/"/>
<rdf:li resource="https://dwt.life/archives/23/"/>
<rdf:li resource="https://dwt.life/archives/22/"/>
<rdf:li resource="https://dwt.life/archives/19/"/>
</rdf:Seq>
</items>
</channel>
<item rdf:about="https://dwt.life/archives/29/">
<title>openat与open的区别及用法示例（dfd）</title>
<link>https://dwt.life/archives/29/</link>
<dc:date>2021-07-06T09:12:52+08:00</dc:date>
<description>从2.6.16版本开始，GNU/Linux引入opeant系统调用：#define _XOPEN_SOURCE 700 /* Or define _POSIX_C_SOURCE &gt;= 200809 */
#include &lt;fcntl.h&gt;
int openat(int  dirfd , const char * pathname , int  flags , ... /* mode_t  mode */);Returns file descriptor on success, or –1 on error同open相比，多了一个dirfd参数。关于它的用法，参考以下解释：If pathname specifies a relative pathname, then it is interpretedrelative to the directory referred to by the open file descriptordirfd, rather than relative to the process’s current workingdirectory.If pathname specifies a relative pathname, and dirfd contains thespecial value AT_FDCWD , then pathname is interpreted relative to theprocess’s current working directory (i.e., the same behavior asopen(2)).If pathname specifies an absolute pathname, then dirfd is ignored.总结起来，如果pathname是绝对路径，则dirfd参数没用。如果pathname是相对路径，并且dirfd的值不是AT_FDCWD，则pathname的参照物是相对于dirfd指向的目录，而不是进程的当前工作目录；反之，如果dirfd的值是AT_FDCWD，pathname则是相对于进程当前工作目录的相对路径，此时等同于open。参考kernel代码则一目了然：引入openat（及其它at结尾的函数）有以下两个原因：First, openat() allows an application to avoid race conditions thatcould occur when using open(2) to open files in directories other thanthe current working directory. These race conditions result from thefact that some component of the directory prefix given to open(2)could be changed in parallel with the call to open(2). Such races canbe avoided by opening a file descriptor for the target directory, andthen specifying that file descriptor as the dirfd argument ofopenat().Second, openat() allows the implementation of a per-thread “currentworking directory”, via file descriptor(s) maintained by theapplication. (This functionality can also be obtained by tricks basedon the use of /proc/self/fd/dirfd, but less efficiently.)引入openat是方便一个进程内的各线程可拥有不同的当前目录，传统的chdir会影响整个进程，而使用openat只需要每个线程在初始化时打开一个目录(调用open)，然后就可以以openat在“当前目录”操作文件了，如：int dirfd = open(&quot;/tmp&quot;); // 相当于chdir到“/tmp”
int filefd = openat(dirfd, &quot;myfile&quot;); // 在/tmp目录下打开“myfile”文件 用法示例：#include &lt;stdio.h&gt;  
#include &lt;sys/stat.h&gt;  
#include &lt;fcntl.h&gt;  
#include &lt;stdlib.h&gt;  
#include &lt;unistd.h&gt;  
  
void creat_at(char *dir_path, char *relative_path)  
{  
    int dir_fd;  
    int fd;  
    int flags;  
    mode_t mode;  
  
    dir_fd = open(dir_path, O_RDONLY);  //fd参数是通过打开相对路径名所在的目录来获取。
    if (dir_fd &lt; 0)   
    {  
        perror(&quot;open&quot;);  
        exit(EXIT_FAILURE);  
    }  
  
    flags = O_CREAT | O_TRUNC | O_RDWR;  
    mode = 0640;  //-rw-r-----
    fd = openat(dir_fd, relative_path, flags, mode);  
    if (fd &lt; 0)   
    {  
        perror(&quot;openat&quot;);  
        exit(EXIT_FAILURE);  
    }  
  
    write(fd, &quot;HELLO&quot;, 5);  
  
    close(fd);  
    close(dir_fd);  
}  
  
int main()  
{  
    creat_at(&quot;../03.文件IO&quot;, &quot;log.txt&quot;);  
    return 0;  
}借用dirfd，将DIR*转换成int类型的文件描述符#include &lt;sys/types.h&gt;  
#include &lt;sys/stat.h&gt;  
#include &lt;fcntl.h&gt;  
#include &lt;dirent.h&gt;  
#include &lt;stdio.h&gt;  
#include &lt;unistd.h&gt;  
  
int main()  
{  
    DIR *dir;  
    int dirfd2;  
    int fd;  
    int n;  
  
    dir = opendir(&quot;../03.文件IO&quot;);  
    if(NULL == dir)  
    {  
        perror(&quot;open dir error&quot;);  
        return -1;  
    }  
    dirfd2 = dirfd(dir);  
    if(-1 == dirfd2)  
    {  
        perror(&quot;dirfd error&quot;);  
        return -1;  
    }  
  
    fd = openat(dirfd2,&quot;output.log&quot;,O_CREAT|O_RDWR|O_TRUNC, \
　　　　　　　　　　　　　　　　　　　　　　S_IRWXU|S_IRWXG|S_IRWXO);  
    if(-1 == fd)  
    {  
        perror(&quot;opeat error&quot;);  
        return -1;  
    }  
    n = write(fd,&quot;Hello world!\n&quot;,15);  
      
    close(fd);  
    closedir(dir);  
  
    return 0;  
  
}</description>
</item>
<item rdf:about="https://dwt.life/archives/28/">
<title>郵件回復歸檔：關於Linux Kernel不同版本存在的模塊開發問題</title>
<link>https://dwt.life/archives/28/</link>
<dc:date>2021-07-05T14:14:02+08:00</dc:date>
<description>來自https://szlin.me/的博主的郵件解答，其中第一點有不小的作用。Hi Rick,一般而言, 遇到這種改版造成行為改變的議題, 我常用以下兩種方法.找出並定位改變的 kernel 版本 - 使用 git blame + bisect 來找出 function argument or content 的差異, 並詳細閱讀 commit log, 大概就會有手感.如果找到並閱讀完 commit log 還是沒有頭緒, 可試試透過 grep 關鍵字, 找出其他也有使用該 function 的檔案, 了解它們在新版的使用方法. 並可搭配 git blame 來看如何這些檔案如何從舊版移植到新版以上行為也可以搭配 kernel documentation 來閱讀, 雖然有時候文件會跟不上 code 進版速度.最後, 若公司允許, 建議把 driver 進行 upstream, 推入 Linux kernel mainline 中.這樣一來就不會有因為 kernel 進版而導致錯誤, 因為 Linux kernel 本身進板時, maintainer 就會處理了.以上, 希望對你有所幫助.SZ</description>
</item>
<item rdf:about="https://dwt.life/archives/25/">
<title>Centos对应内核版本</title>
<link>https://dwt.life/archives/25/</link>
<dc:date>2021-07-04T01:12:11+08:00</dc:date>
<description>https://en.wikipedia.org/wiki/CentOS#CentOS_version_7</description>
</item>
<item rdf:about="https://dwt.life/archives/23/">
<title>linux4.17后重写sys_call_table函数参数无法获得的解决方法</title>
<link>https://dwt.life/archives/23/</link>
<dc:date>2021-07-03T17:25:00+08:00</dc:date>
<description>从社区中得到了一个解决方案：System call hooking example arguments are incorrect然后搜索这个得到了这篇文章其中，参数以const struct pt_regs *regs的形式存储，第一个参数dfd在regs-&gt;di中第二个参数filename在regs-&gt;si中第三个参数flags在regs-&gt;dx中第四个参数mode在regs-&gt;r10中获取之后再对其进行类型转换即可。主要是kernel4.17后对参数的获取做了修改，但是即使定义的函数没有hook上也不会影响正常使用的意思。花了两天时间自己没解决，求助社区半天不到就解决了。</description>
</item>
<item rdf:about="https://dwt.life/archives/22/">
<title>linux 内核函数 filp_open、filp_read、IS_ERR、ERR_PTR、PTR_ERR 简介</title>
<link>https://dwt.life/archives/22/</link>
<dc:date>2021-07-02T11:41:00+08:00</dc:date>
<description>内核态文件操作在用户态，我们操作文件可以用C库函数：open()、read()、write()等，但是在内核态没有库函数可用，这时就需要用内核的一些函数：filp_open、filp_close、vfs_read、vfs_write、set_fs、get_fs等函数，在下列文件中声明：/usr/lib/modules/3.10.0-514.el7.x86_64/build/include/linux/fs.h

/usr/lib/modules/3.10.0-514.el7.x86_64/build/include/asm-generic/uaccess.h

/usr/src/kernels/3.10.0-514.el7.x86_64/include/linux/err.hfilp_openextern struct file *filp_open(const char *, int, umode_t);参数说明：第一个参数表明要打开或创建文件的名称(包括路径部分)。第二个参数文件的打开方式，其取值与标准库中的open相应参数类似，可以取O_CREAT,O_RDWR,O_RDONLY等。第三个参数创建文件时使用，设置创建文件的读写权限，其它情况可以设为0该函数返回strcut file*结构指针，供后继函数操作使用，该返回值用IS_ERR()来检验其有效性。filp_closeextern int filp_close(struct file *, fl_owner_t id);参数说明：第一个参数是filp_open返回的file结构体指针第二个参数基本上都是NULLvfs_read/writeextern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *);extern ssize_t vfs_write(struct file *, const char __user *, size_t, loff_t *);参数说明：第一个参数是filp_open返回的file结构体指针第二个参数是buf，注意，这个参数有用__user修饰，表明buf指向用户空间的地址，如果传入内核空间的地址，就会报错，并返回-EFAULT，但在kernel中，要使这两个读写函数使用kernel空间的buf指针也能正确工作，需要使用set_fs()set_fsstatic inline void set_fs(mm_segment_t fs)该函数的作用是改变kernel对内存地址检查的处理方式，其实该函数的参数fs只有两个取值：USER_DS，KERNEL_DS，分别代表用户空间和内核空间，默认情况下，kernel取值为USER_DS，即对用户空间地址检查并做变换。那么要在这种对内存地址做检查变换的函数中使用内核空间地址，就需要使用set_fs(KERNEL_DS)进行设置，它的作用是取得当前的设置，这两个函数的一般用法为：filp_open()
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
...... //与内存有关的操作
set_fs(old_fs);
filp_close第三个参数表明文件要读写的起始位置。几点说明：（从网上查找的资料）Linux Kernel组成员不赞成在kernel中独立的读写文件(这样做可能会影响到策略和安全问题)，对内核需要操作的文件内容，最好由应用层配合完成。这些函数的正确运行需要依赖于进程环境，因此,有些函数不能在中断的handle或Kernel中不属于任何进程的代码中执行，否则可能出现崩溃，要避免这种情况发生，可以在kernel中创建内核线程，将这些函数放在线程环境下执行。
#ifndef _LINUX_ERR_H
#define _LINUX_ERR_H
#include &lt;linux/compiler.h&gt;
#include &lt;asm/errno.h&gt;
/*
 * Kernel pointers have redundant information, so we can use a
 * scheme where we can return either an error code or a dentry
 * pointer with the same return value.
 *
 * This should be a per-architecture thing, to allow different
 * error and pointer decisions.
 */
#define MAX_ERRNO       4095
 
#ifndef __ASSEMBLY__
 
#define IS_ERR_VALUE(x) unlikely((x) &gt;= (unsigned long)-MAX_ERRNO)
 
static inline void * __must_check ERR_PTR(long error)
{
        return (void *) error;
}

static inline long __must_check PTR_ERR(const void *ptr)
{
        return (long) ptr;
}
 
static inline long __must_check IS_ERR(const void *ptr)
{
        return IS_ERR_VALUE((unsigned long)ptr);
}
 
static inline long __must_check IS_ERR_OR_NULL(const void *ptr)
{
        return !ptr || IS_ERR_VALUE((unsigned long)ptr);
}内核中的函数常常返回指针，问题是如果出错，也希望能够通过返回的指针体现出来。总体来说，如果内核返回一个指针，那么有三种情况：合法指针，NULL指针和非法指针。在linux中有很多错误，内核错误可以参考include/asm-generic/errno-base.h。MAX_ERRNO定义了最大的错误号4095，刚好是4k-1，所以内核地址保留了0xfffffffffffff000~0xffffffffffffffff（64位系统）用来记录错误号，也就是说这段地址和Linux的错误号是一一对应的，可以用上面的内联函数相互转化。比如说我们上面的filp_open函数返回值，用IS_ERR函数去检查，如果地址落在0xfffffffffffff000~0xffffffffffffffff范围，表示filp_open函数失败，IS_ERR为1，同时filp_open返回的错误地址对应一个linux的错误号，如果想知道是哪个错误号，就用PTR_ERR函数来转化。错误的返回地址和错误号是可以使用 ERR_PTR、PTR_ERR 相互转化的。</description>
</item>
<item rdf:about="https://dwt.life/archives/19/">
<title>yum 安装对应内核版本的kernel-devel</title>
<link>https://dwt.life/archives/19/</link>
<dc:date>2021-07-01T00:22:02+08:00</dc:date>
<description>uname -r 查看内核版本查看已安装kernel-develuname -a ; rpm -qa kernel\* | sort下载对应版本sudo yum install &quot;kernel-devel-uname-r == $(uname -r)&quot;完成后在/usr/src/kernels/下生成对应版本的源码</description>
</item>
</rdf:RDF>