精华内容
下载资源
问答
  • 2022-04-11 14:12:51

    nginx目录结构和配置文件详解


    0x00 Nginx 目录结构


    Nginx 文件结构比较简洁,主要包括配置文件和二进制可执行程序,通过安装包形式安装的 nginx 文件结构跟各 Linux 发行版目录规则存放配置文件和二进制文件的位置

    目录结构及文件功能如下:

    [root@localhost ~]# tree /usr/local/nginx
    /usr/local/nginx
    ├── client_body_temp                 # POST 大文件暂存目录
    ├── conf                             # Nginx所有配置文件的目录
    │   ├── fastcgi.conf                 # fastcgi相关参数的配置文件
    │   ├── fastcgi.conf.default         # fastcgi.conf的原始备份文件
    │   ├── fastcgi_params               # fastcgi的参数文件
    │   ├── fastcgi_params.default      
    │   ├── koi-utf
    │   ├── koi-win
    │   ├── mime.types                   # 媒体类型
    │   ├── mime.types.default
    │   ├── nginx.conf                   #这是Nginx默认的主配置文件,日常使用和修改的文件
    │   ├── nginx.conf.default
    │   ├── scgi_params                 # scgi相关参数文件
    │   ├── scgi_params.default  
    │   ├── uwsgi_params                 # uwsgi相关参数文件
    │   ├── uwsgi_params.default
    │   └── win-utf
    ├── fastcgi_temp                     # fastcgi临时数据目录
    ├── html                             # Nginx默认站点目录
    │   ├── 50x.html                     # 错误页面优雅替代显示文件,例如出现502错误时会调用此页面
    │   └── index.html                   # 默认的首页文件
    ├── logs                             # Nginx日志目录
    │   ├── access.log                   # 访问日志文件
    │   ├── error.log                   # 错误日志文件
    │   └── nginx.pid                   # pid文件,Nginx进程启动后,会把所有进程的ID号写到此文件
    ├── proxy_temp                       # 临时目录
    ├── sbin                             # Nginx 可执行文件目录
    │   └── nginx                       # Nginx 二进制可执行程序
    ├── scgi_temp                       # 临时目录
    └── uwsgi_temp                       # 临时目录

    0x01 Nginx配置文件


    Nginx 主配置文件/usr/local/nginx/conf/nginx.conf 是一个纯文本类型的文件,整个配置文件是以区块的形式组织,通常每一个区块以一对大括号{}来表示开始与结束。提示:若编译安装则 nginx.conf 位于编译时所指定目录。

    • Main 位于 nginx.conf 配置文件的最高层;

    • Main 层下可以有 Event、HTTP 层;

    • Http 层下面允许有多个 Server 层,用于对不同的网站做不同的配置;

    • Server 层下面允许有多个 Location,用于对不同的路径进行不同模块的配置。

    全局配置部分用来配置对整个 server 都有效的参数。主要会设置一些影响 nginx 服务器整体运行的配置指令,主要包括配置运行 Nginx 服务器的用户(组)允许生成的 worker process 数进程 PID 存放路径日志存放路径和类型以 及配置文件的引入

    • 全局Main配置

    user nginx;
    worker_processes 1; #设置值和CPU核心数一致
    
    error_log /var/log/nginx/error.log warn; #日志位置和日志级别
    pid       /var/run/nginx.pid;

    events 块涉及的指令主要影响 Nginx 服务器与用户的网络连接,常用的设置包括是否开启对多 worker process 下的网络连接进行序列化,是否允许同时接收多个网络连接,选取哪种事件驱动模型来处理连接请求,每个 worker process 可以同时支持的最大连接数等。

    • Event配置

    events {
        worker_connections 1024;
    }

    http 全局块配置的指令包括文件引入、MIME-TYPE 定义、日志自定义、连接超时时间、单链接请求数上限等。

         http配置

    http {
      include       /etc/nginx/mime.types;
      default_type application/octet-stream;
      log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"';
      access_log /var/log/nginx/access.log main;
      sendfile       on;
      #tcp_nopush     on;
      keepalive_timeout 65;
      #gzip on;
      include /etc/nginx/conf.d/*.conf;
    }

    通常 Server 配置在独立的/etc/nginx/conf.d/*.conf中,通过引用的方式调用。

    Server 块也被叫做“虚拟主机”部分,它描述的是一组根据不同 server_name 指令逻辑分割的资源,这些虚拟服务器响应 HTTP 请求,因此都包含在 http 部分。最常见的配置是本虚拟机主机的监听配置和本虚拟主机的名称或 IP 配置。一个 server 块可以配置多个 location 块。

    • server配置

    server {
      listen       80;
      server_name localhost;
      location / {
          root   /usr/share/nginx/html;
          index index.html index.htm;
      }
      error_page   500 502 503 504 /50x.html;
      location = /50x.html {
          root   /usr/share/nginx/html;
      }
    }
    • Nginx全局配置

    user nginx;       #进程用户
    worker_processes 1;       #工作进程,配合和CPU个数保持一致
    error_log /var/log/nginx/error.log warn;       #错误日志路径及级别
    pid       /var/run/nginx.pid;#Nginx服务启动的pid
    • Nginx events事件配置

    events {
        worker_connections 1024;#每个worker进程支持的最大连接数
        use epoll;#内核模型,select、poll、epoll
    }
    • Nginx公共配置

    http {
      include       /etc/nginx/mime.types;#指定在当前文件中包含另一个文件的指令
      default_type application/octet-stream;#指定默认处理的文件类型可以是二进制
    
      log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"';#日志格式
    
      access_log /var/log/nginx/access.log main;#访问日志
    
      sendfile       on;#优化静态资源
      #tcp_nopush     on;#nginx不要缓存数据,而是一段一段发送
    
      keepalive_timeout 65;#给客户端分配连接超时时间,服务器会在这个时间过后关闭连接。
    
      #gzip on;#压缩
    • Nginx server配置

    Nginx必须使用虚拟机配置站点,每个虚拟主机使用一个server。

    server {
          listen       80;       #监听端口,默认80
          server_name localhost;#提供服务的域名或主机名
    
          #charset koi8-r;
    
          #access_log logs/host.access.log main;
    
          location / {#控制网站访问路径
              root   /usr/share/nginx/html;       #存放网站的路径
              index index.html index.htm;       #默认访问的首页
          }
          #error_page 404             /404.html;#错误页面
    
          # redirect server error pages to the static page /50x.html
          #
          error_page   500 502 503 504 /50x.html;#定义请求错误,指定错误代码
          location = /50x.html {#错误代码重定向到新的location
              root   html;
          }
      # another virtual host using mix of IP-, name-, and port-based configuration
      #
      #server {#server段配置
      #   listen       8000;
      #   listen       somename:8080;
      #   server_name somename alias another.alias;
    
      #   location / {
      #       root   html;
      #       index index.html index.htm;
      #   }
      #}
    
    
      # HTTPS server
      #
      #server {#server段配置
      #   listen       443 ssl;
      #   server_name localhost;
    
      #   ssl_certificate     cert.pem;
      #   ssl_certificate_key cert.key;#SSL证书配置
    
      #   ssl_session_cache   shared:SSL:1m;
      #   ssl_session_timeout 5m;
    
      #   ssl_ciphers HIGH:!aNULL:!MD5;
      #   ssl_prefer_server_ciphers on;
    
      #   location / {
      #       root   html;
      #       index index.html index.htm;
      #   }
      #}
    }
    • Nginx网站配置

    在默认虚拟机 default.conf 基础上新建虚拟机。

    [root@nginx ~]# vi /etc/nginx/conf.d/mysite.conf
    server {
      server_name www.cainiaojc.com;
      location / {
          root   /usr/share/nginx/base;
          index index.html;
      }
    }


    *本文章仅供技术交流分享,请勿做未授权违法攻击,雨笋教育不负任何责任。具体请参考《网络安全法》。

    更多相关内容
  • redis配置文件中常用配置详解

    万次阅读 多人点赞 2019-05-29 23:38:44
    这跟有利于文件的 容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗,所以如果你追求高性能,可以关闭该配置 rdbchecksum yes #指定本地数据库文件名,一般采用默认的 dump.rdb dbfilename dump.rdb #数据...

    redis6.0分布式缓存+高可用集群

    此次安装的版本为: 5.0.3

    [root@localhost local]# redis-server --version
    Redis server v=5.0.3 sha=00000000:0 malloc=jemalloc-5.1.0 bits=64 build=afabdecde61000c3
    

    打开redis.cof

    ###################################  NETWORK  ###################################

    ###################################  NETWORK ###################################
    
    # 指定 redis 只接收来自于该IP地址的请求,如果不进行设置,那么将处理所有请求
    bind 127.0.0.1
    
    #是否开启保护模式,默认开启。要是配置里没有指定bind和密码。开启该参数后,redis只会本地进行访问,
    拒绝外部访问。要是开启了密码和bind,可以开启。否则最好关闭,设置为no
    protected-mode yes
    
    #redis监听的端口号
    port 6379
    
    #此参数确定了TCP连接中已完成队列(完成三次握手之后)的长度, 当然此值必须不大于Linux系统定义
    的/proc/sys/net/core/somaxconn值,默认是511,而Linux的默认参数值是128。当系统并发量大并且客户端
    速度缓慢的时候,可以将这二个参数一起参考设定。该内核参数默认值一般是128,对于负载很大的服务程序来说
    大大的不够。一般会将它修改为2048或者更大。在/etc/sysctl.conf中添加:net.core.somaxconn = 2048,
    然后在终端中执行sysctl -p
    tcp-backlog 511
    
    #此参数为设置客户端空闲超过timeout,服务端会断开连接,为0则服务端不会主动断开连接,不能小于0
    timeout 0
    
    #tcp keepalive参数。如果设置不为0,就使用配置tcp的SO_KEEPALIVE值,使用keepalive有两个好处:检测挂
    掉的对端。降低中间设备出问题而导致网络看似连接却已经与对端端口的问题。在Linux内核中,设置了
    keepalive,redis会定时给对端发送ack。检测到对端关闭需要两倍的设置值
    tcp-keepalive 300
    
    #是否在后台执行,yes:后台运行;no:不是后台运行
    daemonize yes
    
    #redis的进程文件
    pidfile /var/run/redis/redis.pid
    
    #指定了服务端日志的级别。级别包括:debug(很多信息,方便开发、测试),verbose(许多有用的信息,
    但是没有debug级别信息多),notice(适当的日志级别,适合生产环境),warn(只有非常重要的信息)
    loglevel notice
    
    #指定了记录日志的文件。空字符串的话,日志会打印到标准输出设备。后台运行的redis标准输出是/dev/null
    logfile /usr/local/redis/var/redis.log
    
    
    #是否打开记录syslog功能
    # syslog-enabled no
    
    #syslog的标识符。
    # syslog-ident redis
    
    #日志的来源、设备
    # syslog-facility local0
    
    #数据库的数量,默认使用的数据库是0。可以通过”SELECT 【数据库序号】“命令选择一个数据库,序号从0开始
    databases 16

    #################################  SNAPSHOTTING  ################################# 

    ###################################  SNAPSHOTTING  ###################################
    
    #RDB核心规则配置 save <指定时间间隔> <执行指定次数更新操作>,满足条件就将内存中的数据同步到硬盘
    中。官方出厂配置默认是 900秒内有1个更改,300秒内有10个更改以及60秒内有10000个更改,则将内存中的
    数据快照写入磁盘。
    若不想用RDB方案,可以把 save "" 的注释打开,下面三个注释
    #   save ""
    save 900 1
    save 300 10
    save 60 10000
    
    #当RDB持久化出现错误后,是否依然进行继续进行工作,yes:不能进行工作,no:可以继续进行工作,可以通
    过info中的rdb_last_bgsave_status了解RDB持久化是否有错误
    stop-writes-on-bgsave-error yes
    
    #配置存储至本地数据库时是否压缩数据,默认为yes。Redis采用LZF压缩方式,但占用了一点CPU的时间。若关闭该选项,
    但会导致数据库文件变的巨大。建议开启。
    rdbcompression yes
    
    #是否校验rdb文件;从rdb格式的第五个版本开始,在rdb文件的末尾会带上CRC64的校验和。这跟有利于文件的
    容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗,所以如果你追求高性能,可以关闭该配置
    rdbchecksum yes
    
    #指定本地数据库文件名,一般采用默认的 dump.rdb
    dbfilename dump.rdb
    
    #数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
    dir /usr/local/redis/var
    

    #################################  REPLICATION  #################################

    ################################# REPLICATION #################################
    
    # 复制选项,slave复制对应的master。
    # replicaof <masterip> <masterport>
    
    #如果master设置了requirepass,那么slave要连上master,需要有master的密码才行。masterauth就是用来
    配置master的密码,这样可以在连上master后进行认证。
    # masterauth <master-password>
    
    #当从库同主机失去连接或者复制正在进行,从机库有两种运行方式:1) 如果slave-serve-stale-data设置为
    yes(默认设置),从库会继续响应客户端的请求。2) 如果slave-serve-stale-data设置为no,
    INFO,replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG,SUBSCRIBE, UNSUBSCRIBE,
    PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB,COMMAND, POST, HOST: and LATENCY命令之外的任何请求
    都会返回一个错误”SYNC with master in progress”。
    replica-serve-stale-data yes
    
    #作为从服务器,默认情况下是只读的(yes),可以修改成NO,用于写(不建议)
    #replica-read-only yes
    
    # 是否使用socket方式复制数据。目前redis复制提供两种方式,disk和socket。如果新的slave连上来或者
    重连的slave无法部分同步,就会执行全量同步,master会生成rdb文件。有2种方式:disk方式是master创建
    一个新的进程把rdb文件保存到磁盘,再把磁盘上的rdb文件传递给slave。socket是master创建一个新的进
    程,直接把rdb文件以socket的方式发给slave。disk方式的时候,当一个rdb保存的过程中,多个slave都能
    共享这个rdb文件。socket的方式就的一个个slave顺序复制。在磁盘速度缓慢,网速快的情况下推荐用socket方式。
    repl-diskless-sync no
    
    #diskless复制的延迟时间,防止设置为0。一旦复制开始,节点不会再接收新slave的复制请求直到下一个rdb传输。
    所以最好等待一段时间,等更多的slave连上来
    repl-diskless-sync-delay 5
    
    #slave根据指定的时间间隔向服务器发送ping请求。时间间隔可以通过 repl_ping_slave_period 来设置,默认10秒。
    # repl-ping-slave-period 10
    
    # 复制连接超时时间。master和slave都有超时时间的设置。master检测到slave上次发送的时间超过repl-timeout,即认为slave离线,清除该slave信息。slave检测到上次和master交互的时间超过repl-timeout,则认为master离线。需要注意的是repl-timeout需要设置一个比repl-ping-slave-period更大的值,不然会经常检测到超时
    # repl-timeout 60
    
    
    #是否禁止复制tcp链接的tcp nodelay参数,可传递yes或者no。默认是no,即使用tcp nodelay。如果
    master设置了yes来禁止tcp nodelay设置,在把数据复制给slave的时候,会减少包的数量和更小的网络带
    宽。但是这也可能带来数据的延迟。默认我们推荐更小的延迟,但是在数据量传输很大的场景下,建议选择yes
    repl-disable-tcp-nodelay no
    
    #复制缓冲区大小,这是一个环形复制缓冲区,用来保存最新复制的命令。这样在slave离线的时候,不需要完
    全复制master的数据,如果可以执行部分同步,只需要把缓冲区的部分数据复制给slave,就能恢复正常复制状
    态。缓冲区的大小越大,slave离线的时间可以更长,复制缓冲区只有在有slave连接的时候才分配内存。没有
    slave的一段时间,内存会被释放出来,默认1m
    # repl-backlog-size 1mb
    
    # master没有slave一段时间会释放复制缓冲区的内存,repl-backlog-ttl用来设置该时间长度。单位为秒。
    # repl-backlog-ttl 3600
    
    # 当master不可用,Sentinel会根据slave的优先级选举一个master。最低的优先级的slave,当选master。
    而配置成0,永远不会被选举
    replica-priority 100
    
    #redis提供了可以让master停止写入的方式,如果配置了min-replicas-to-write,健康的slave的个数小于N,mater就禁止写入。master最少得有多少个健康的slave存活才能执行写命令。这个配置虽然不能保证N个slave都一定能接收到master的写操作,但是能避免没有足够健康的slave的时候,master不能写入来避免数据丢失。设置为0是关闭该功能
    # min-replicas-to-write 3
    
    # 延迟小于min-replicas-max-lag秒的slave才认为是健康的slave
    # min-replicas-max-lag 10
    
    # 设置1或另一个设置为0禁用这个特性。
    # Setting one or the other to 0 disables the feature.
    # By default min-replicas-to-write is set to 0 (feature disabled) and
    # min-replicas-max-lag is set to 10.

    #################################  SECURITY #################################

    
    #requirepass配置可以让用户使用AUTH命令来认证密码,才能使用其他命令。这让redis可以使用在不受信任的
    网络中。为了保持向后的兼容性,可以注释该命令,因为大部分用户也不需要认证。使用requirepass的时候需要
    注意,因为redis太快了,每秒可以认证15w次密码,简单的密码很容易被攻破,所以最好使用一个更复杂的密码
    # requirepass foobared
    
    #把危险的命令给修改成其他名称。比如CONFIG命令可以重命名为一个很难被猜到的命令,这样用户不能使用,而
    内部工具还能接着使用
    # rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
    
    #设置成一个空的值,可以禁止一个命令
    # rename-command CONFIG ""

     #################################  CLIENTS #################################

    # 设置能连上redis的最大客户端连接数量。默认是10000个客户端连接。由于redis不区分连接是客户端连接还
    是内部打开文件或者和slave连接等,所以maxclients最小建议设置到32。如果超过了maxclients,redis会给
    新的连接发送’max number of clients reached’,并关闭连接
    # maxclients 10000
    
    

     #######################    MEMORY MANAGEMENT    ##########################

    redis配置的最大内存容量。当内存满了,需要配合maxmemory-policy策略进行处理。注意slave的输出缓冲区
    是不计算在maxmemory内的。所以为了防止主机内存使用完,建议设置的maxmemory需要更小一些
    maxmemory 122000000
    
    #内存容量超过maxmemory后的处理策略。
    #volatile-lru:利用LRU算法移除设置过过期时间的key。
    #volatile-random:随机移除设置过过期时间的key。
    #volatile-ttl:移除即将过期的key,根据最近过期时间来删除(辅以TTL)
    #allkeys-lru:利用LRU算法移除任何key。
    #allkeys-random:随机移除任何key。
    #noeviction:不移除任何key,只是返回一个写错误。
    #上面的这些驱逐策略,如果redis没有合适的key驱逐,对于写命令,还是会返回错误。redis将不再接收写请求,只接收get请求。写命令包括:set setnx setex append incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby getset mset msetnx exec sort。
    # maxmemory-policy noeviction
    
    # lru检测的样本数。使用lru或者ttl淘汰算法,从需要淘汰的列表中随机选择sample个key,选出闲置时间最长的key移除
    # maxmemory-samples 5
    
    # 是否开启salve的最大内存
    # replica-ignore-maxmemory yes

     ##########################    LAZY FREEING    #############################

    #以非阻塞方式释放内存
    #使用以下配置指令调用了
    lazyfree-lazy-eviction no
    lazyfree-lazy-expire no
    lazyfree-lazy-server-del no
    replica-lazy-flush no

     ########################   APPEND ONLY MODE    ###########################

    #Redis 默认不开启。它的出现是为了弥补RDB的不足(数据的不一致性),所以它采用日志的形式来记录每个写
    操作,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
    默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,会导致可
    能有几分钟的数据丢失,根据save来策略进行持久化,Append Only File是另一种持久化方式,可以提供更好的
    持久化特性。Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时Redis都会先把这
    个文件的数据读入内存里,先忽略RDB文件。若开启rdb则将no改为yes
    appendonly no
    
    指定本地数据库文件名,默认值为 appendonly.aof
    appendfilename "appendonly.aof"
    
    
    #aof持久化策略的配置
    #no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快
    #always表示每次写入都执行fsync,以保证数据同步到磁盘
    #everysec表示每秒执行一次fsync,可能会导致丢失这1s数据
    # appendfsync always
    appendfsync everysec
    # appendfsync no
    
    # 在aof重写或者写入rdb文件的时候,会执行大量IO,此时对于everysec和always的aof模式来说,执行
    fsync会造成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no。如果对延迟要求很高的
    应用,这个字段可以设置为yes,否则还是设置为no,这样对持久化特性来说这是更安全的选择。设置为yes表
    示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes。Linux的
    默认fsync策略是30秒。可能丢失30秒数据
    no-appendfsync-on-rewrite no
    
    #aof自动重写配置。当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件
    增长到一定大小的时候Redis能够调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志重写得
    到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程
    auto-aof-rewrite-percentage 100
    
    #设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写
    auto-aof-rewrite-min-size 64mb
    
    #aof文件可能在尾部是不完整的,当redis启动的时候,aof文件的数据被载入内存。重启可能发生在redis所
    在的主机操作系统宕机后,尤其在ext4文件系统没有加上data=ordered选项(redis宕机或者异常终止不会造
    成尾部不完整现象。)出现这种现象,可以选择让redis退出,或者导入尽可能多的数据。如果选择的是yes,
    当截断的aof文件被导入的时候,会自动发布一个log给客户端然后load。如果是no,用户必须手动redis-
    check-aof修复AOF文件才可以
    aof-load-truncated yes
    
    #加载redis时,可以识别AOF文件以“redis”开头。
    #字符串并加载带前缀的RDB文件,然后继续加载AOF尾巴
    aof-use-rdb-preamble yes

     #########################   LUA SCRIPTING     ############################

    # 如果达到最大时间限制(毫秒),redis会记个log,然后返回error。当一个脚本超过了最大时限。只有
    SCRIPT KILL和SHUTDOWN NOSAVE可以用。第一个可以杀没有调write命令的东西。要是已经调用了write,只能
    用第二个命令杀
    lua-time-limit 5000

     #########################   REDIS CLUSTER     ############################

    # 集群开关,默认是不开启集群模式
    # cluster-enabled yes
    
    #集群配置文件的名称,每个节点都有一个集群相关的配置文件,持久化保存集群的信息。这个文件并不需要手动
    配置,这个配置文件有Redis生成并更新,每个Redis集群节点需要一个单独的配置文件,请确保与实例运行的系
    统中配置文件名称不冲突
    # cluster-config-file nodes-6379.conf
    
    #节点互连超时的阀值。集群节点超时毫秒数
    # cluster-node-timeout 15000
    
    #在进行故障转移的时候,全部slave都会请求申请为master,但是有些slave可能与master断开连接一段时间
    了,导致数据过于陈旧,这样的slave不应该被提升为master。该参数就是用来判断slave节点与master断线的时
    间是否过长。判断方法是:
    #比较slave断开连接的时间和(node-timeout * slave-validity-factor) + repl-ping-slave-period
    #如果节点超时时间为三十秒, 并且slave-validity-factor为10,假设默认的repl-ping-slave-period是10
    秒,即如果超过310秒slave将不会尝试进行故障转移
    # cluster-replica-validity-factor 10
    
    # master的slave数量大于该值,slave才能迁移到其他孤立master上,如这个参数若被设为2,那么只有当一
    个主节点拥有2 个可工作的从节点时,它的一个从节点会尝试迁移
    # cluster-migration-barrier 1
    
    #默认情况下,集群全部的slot有节点负责,集群状态才为ok,才能提供服务。设置为no,可以在slot没有全
    部分配的时候提供服务。不建议打开该配置,这样会造成分区的时候,小分区的master一直在接受写请求,而
    造成很长时间数据不一致
    # cluster-require-full-coverage yes
    

     ####################  CLUSTER DOCKER/NAT support  #######################

    #*群集公告IP
    #*群集公告端口
    #*群集公告总线端口
    # Example:
    #
    # cluster-announce-ip 10.1.1.5
    # cluster-announce-port 6379
    # cluster-announce-bus-port 6380

     #############################  SLOW LOG #################################

    # slog log是用来记录redis运行中执行比较慢的命令耗时。当命令的执行超过了指定时间,就记录在slow log
    中,slog log保存在内存中,所以没有IO操作。
    #执行时间比slowlog-log-slower-than大的请求记录到slowlog里面,单位是微秒,所以1000000就是1秒。注
    意,负数时间会禁用慢查询日志,而0则会强制记录所有命令。
    slowlog-log-slower-than 10000
    
    #慢查询日志长度。当一个新的命令被写进日志的时候,最老的那个记录会被删掉。这个长度没有限制。只要有足
    够的内存就行。你可以通过 SLOWLOG RESET 来释放内存
    slowlog-max-len 128

      ########################  LATENCY MONITOR ############################

    #延迟监控功能是用来监控redis中执行比较缓慢的一些操作,用LATENCY打印redis实例在跑命令时的耗时图表。
    只记录大于等于下边设置的值的操作。0的话,就是关闭监视。默认延迟监控功能是关闭的,如果你需要打开,也
    可以通过CONFIG SET命令动态设置
    latency-monitor-threshold 0

      #######################  EVENT NOTIFICATION ###########################

    #键空间通知使得客户端可以通过订阅频道或模式,来接收那些以某种方式改动了 Redis 数据集的事件。因为开启键空间通知功能需要消耗一些 CPU ,所以在默认配置下,该功能处于关闭状态。
    #notify-keyspace-events 的参数可以是以下字符的任意组合,它指定了服务器该发送哪些类型的通知:
    ##K 键空间通知,所有通知以 __keyspace@__ 为前缀
    ##E 键事件通知,所有通知以 __keyevent@__ 为前缀
    ##g DEL 、 EXPIRE 、 RENAME 等类型无关的通用命令的通知
    ##$ 字符串命令的通知
    ##l 列表命令的通知
    ##s 集合命令的通知
    ##h 哈希命令的通知
    ##z 有序集合命令的通知
    ##x 过期事件:每当有过期键被删除时发送
    ##e 驱逐(evict)事件:每当有键因为 maxmemory 政策而被删除时发送
    ##A 参数 g$lshzxe 的别名
    #输入的参数中至少要有一个 K 或者 E,否则的话,不管其余的参数是什么,都不会有任何 通知被分发。详细使用可以参考http://redis.io/topics/notifications
    
    notify-keyspace-events ""

      #######################  ADVANCED CONFIG  ###########################

    # 数据量小于等于hash-max-ziplist-entries的用ziplist,大于hash-max-ziplist-entries用hash
    hash-max-ziplist-entries 512
    
    # value大小小于等于hash-max-ziplist-value的用ziplist,大于hash-max-ziplist-value用hash
    hash-max-ziplist-value 64
    
    #-5:最大大小:64 KB<--不建议用于正常工作负载
    #-4:最大大小:32 KB<--不推荐
    #-3:最大大小:16 KB<--可能不推荐
    #-2:最大大小:8kb<--良好
    #-1:最大大小:4kb<--良好
    list-max-ziplist-size -2
    
    #0:禁用所有列表压缩
    #1:深度1表示“在列表中的1个节点之后才开始压缩,
    #从头部或尾部
    #所以:【head】->node->node->…->node->【tail】
    #[头部],[尾部]将始终未压缩;内部节点将压缩。
    #2:[头部]->[下一步]->节点->节点->…->节点->[上一步]->[尾部]
    #2这里的意思是:不要压缩头部或头部->下一个或尾部->上一个或尾部,
    #但是压缩它们之间的所有节点。
    #3:[头部]->[下一步]->[下一步]->节点->节点->…->节点->[上一步]->[上一步]->[尾部]
    list-compress-depth 0
    
    # 数据量小于等于set-max-intset-entries用iniset,大于set-max-intset-entries用set
    set-max-intset-entries 512
    
    #数据量小于等于zset-max-ziplist-entries用ziplist,大于zset-max-ziplist-entries用zset
    zset-max-ziplist-entries 128
    
    #value大小小于等于zset-max-ziplist-value用ziplist,大于zset-max-ziplist-value用zset
    zset-max-ziplist-value 64
    
    #value大小小于等于hll-sparse-max-bytes使用稀疏数据结构(sparse),大于hll-sparse-max-bytes使
    用稠密的数据结构(dense)。一个比16000大的value是几乎没用的,建议的value大概为3000。如果对CPU要
    求不高,对空间要求较高的,建议设置到10000左右
    hll-sparse-max-bytes 3000
    
    #宏观节点的最大流/项目的大小。在流数据结构是一个基数
    #树节点编码在这项大的多。利用这个配置它是如何可能#大节点配置是单字节和
    #最大项目数,这可能包含了在切换到新节点的时候
    # appending新的流条目。如果任何以下设置来设置
    # ignored极限是零,例如,操作系统,它有可能只是一集
    通过设置限制最大#纪录到最大字节0和最大输入到所需的值
    stream-node-max-bytes 4096
    stream-node-max-entries 100
    
    #Redis将在每100毫秒时使用1毫秒的CPU时间来对redis的hash表进行重新hash,可以降低内存的使用。当你
    的使用场景中,有非常严格的实时性需要,不能够接受Redis时不时的对请求有2毫秒的延迟的话,把这项配置
    为no。如果没有这么严格的实时性要求,可以设置为yes,以便能够尽可能快的释放内存
    activerehashing yes
    
    ##对客户端输出缓冲进行限制可以强迫那些不从服务器读取数据的客户端断开连接,用来强制关闭传输缓慢的客户端。
    #对于normal client,第一个0表示取消hard limit,第二个0和第三个0表示取消soft limit,normal 
    client默认取消限制,因为如果没有寻问,他们是不会接收数据的
    client-output-buffer-limit normal 0 0 0
    
    #对于slave client和MONITER client,如果client-output-buffer一旦超过256mb,又或者超过64mb持续
    60秒,那么服务器就会立即断开客户端连接
    client-output-buffer-limit replica 256mb 64mb 60
    
    #对于pubsub client,如果client-output-buffer一旦超过32mb,又或者超过8mb持续60秒,那么服务器就
    会立即断开客户端连接
    client-output-buffer-limit pubsub 32mb 8mb 60
    
    # 这是客户端查询的缓存极限值大小
    # client-query-buffer-limit 1gb
    
    #在redis协议中,批量请求,即表示单个字符串,通常限制为512 MB。但是您可以更改此限制。
    # proto-max-bulk-len 512mb
    
    #redis执行任务的频率为1s除以hz
    hz 10
    
    #当启用动态赫兹时,实际配置的赫兹将用作作为基线,但实际配置的赫兹值的倍数
    #在连接更多客户端后根据需要使用。这样一个闲置的实例将占用很少的CPU时间,而繁忙的实例将反应更灵敏
    dynamic-hz yes
    
    #在aof重写的时候,如果打开了aof-rewrite-incremental-fsync开关,系统会每32MB执行一次fsync。这
    对于把文件写入磁盘是有帮助的,可以避免过大的延迟峰值
    aof-rewrite-incremental-fsync yes
    
    #在rdb保存的时候,如果打开了rdb-save-incremental-fsync开关,系统会每32MB执行一次fsync。这
    对于把文件写入磁盘是有帮助的,可以避免过大的延迟峰值
    rdb-save-incremental-fsync yes
    
    

     ###################### ACTIVE DEFRAGMENTATION  ##########################

    # 已启用活动碎片整理
    # activedefrag yes
    
    # 启动活动碎片整理的最小碎片浪费量
    # active-defrag-ignore-bytes 100mb
    
    # 启动活动碎片整理的最小碎片百分比
    # active-defrag-threshold-lower 10
    
    # 我们使用最大努力的最大碎片百分比
    # active-defrag-threshold-upper 100
    
    # 以CPU百分比表示的碎片整理的最小工作量
    # active-defrag-cycle-min 5
    
    # 在CPU的百分比最大的努力和碎片整理
    # active-defrag-cycle-max 75
    
    #将从中处理的set/hash/zset/list字段的最大数目
    #主词典扫描
    # active-defrag-max-scan-fields 1000
    

    展开全文
  • Linux 内核配置(一)

    千次阅读 2021-11-18 11:20:31
    内核配置使用menuconfig,界面如下: 我们将针对每个选项,解释选项的含义,以及说明是否推荐使用(在什么情况下使用 General setup(通用设置 Cross-compiler tool prefix – 交叉编译工具前缀,如果你要使用...

    最近有个项目需要做内核裁剪,我之前也没做过,为了更了解内核和硬件,把相应的知识记录一下
    内核的配置使用menuconfig,界面如下:
    内核配置首页
    我们将针对每个选项,解释选项的含义,以及说明是否推荐使用(在什么情况下使用

    General setup(通用设置

    通用设置

    • Cross-compiler tool prefix – 交叉编译工具前缀,如果你要使用交叉编译工具的话输入相关前缀。默认不使用。嵌入式linux不需要。
    • Compile also drivers which will not load – 在其他平台编译以便测试驱动程序编译流程,通常不需要
    • Local version - append to kernel release – 在内核版本后面加上自定义的版本字符串,默认不选择
    • Automatically append version information to the version string – 自动增加版本信息到版本字符串,默认不选择
    • Support for paging of anonymous memory – 内核支持虚拟内存,即是交换分区(swap),嵌入式Linux不需要
    • System V IPC – 一种进程间通信规范,定义了消息对流,信号量,共享内存,需要勾选
    • Enable process_vm_readv/writev syscalls – 使能这两个系统调用,这些调用允许具有正确权限的进程直接读取或写入另一个进程的地址空间。现在它们只用于openMPI快速进程通信,也可以用于调试程序.未来也许还会有其他用途。看情况勾选。
    • open by fhandle syscalls – 是否允许使用文件handle替代文件名来进行文件操作,默认勾选。
    • uselib syscall – 启用老旧的uselib()系统接口支持,仅在你需要使用基于libc5的古董级程序时才需要,推荐不选择。
    • IRQ subsystem —> 中断请求子系统
      - Expose hardware/virtual IRQ mapping via debugfs – 通过debugfs中的irq_domain_mapping文件向用户显示硬件IRQ号/Linux IRQ号之间的对应关系.仅用于开发调试.推荐不选择。
      - Expose irq internals in debugfs – 通过debugfs公开内部状态信息。 主要用于开发人员和调试难以诊断的中断问题。推荐不选择。
    • Timers subsystem —> 时钟子系统
      - Timer tick handling (Idle dynticks system (tickless idle)) —> 内核时钟滴答处理程序,更多信息可以参考内核源码树下的"Documentation/timers/NO_HZ.txt"文件
      1. Periodic timer ticks (constant rate, no dynticks)无论CPU是否需要,都强制按照固定频率不断触发时钟中断.这是最耗电的方式,不推荐使用
      2. Idle dynticks system (tickless idle) CPU在空闲状态时不产生不必要的时钟中断,以使处理器能够在较低能耗状态下运行以节约电力,适合于大多数场合
      3. Full dynticks system (tickless) 即使CPU在忙碌状态也尽可能关闭所有时钟中断,适用于CPU在同一时间仅运行一个任务,或者用户空间程序极少与内核交互的场合.即使开启此选项,也需要额外设置"nohz_full=?"内核命令行参数才能真正生效.
      - Old Idle dynticks config 为了兼容以前的配置文件而存在的,不需要选中。
      - High Resolution Timer Support 支持高精度时钟,需要选中
    • CPU/Task time and stats accounting —> CPU/进程的时间及状态统计
      - Cputime accounting —>
      1. Simple tick based cputime accountin 简单的基于滴答的统计,适用于大多数场合
      2. Full dynticks CPU time accounting 利用上下文跟踪子系统,通过观察每一个内核与用户空间的边界进行统计.该选项对性能有显著的不良影响,目前仅用于完全无滴答子系统(CONFIG_NO_HZ_FULL)的调试
      - Fine granularity task level IRQ time accounting – 通过读取TSC时间戳进行统计,这是统计进程IRQ时间的更细粒度的统计方式,但对性能有些不良影响(特别是在RDTSC指令速度较慢的CPU上).不需要选中。
      - BSD Process Accounting – 用户空间程序可以要求内核将进程的统计信息写入一个指定的文件,主要包括进程的创建时间/创建者/内存占用等信息. 不需要选中。
      - BSD Process Accounting version 3 file format – 使用新的v3版文件格式,可以包含每个进程的PID和其父进程的PID,但是不兼容老版本的文件格式.不需要选中。
    • RCU Subsystem —> 非对称读写锁系统 是一种新的kernel锁机制,适用于读多写少环境。不做专家级别调整。
    • Kernel .config support – 这个选项允许.config文件(即编译LINUX时的配置文件)保存在内核当中。它提供正在运行中的或者还在硬盘中的内核的相关配置选项。可以通过内核 镜像文件 kernel image file 用命令 script scripts/extract-ikconfig 来提取出来,作为当前内核重编译或者另一个内核编译的参考。如果你的内核在运行中,可以通过/proc/config.gz文件来读取。下一个选项提供这 项支持。这个功能在pc中比较实用,在嵌入式上不需要。不选。
    • Memory placement aware NUMA scheduler – 允许自动根据NUMA系统的节点分布状况进行进程/内存均衡(方法很原始,就是简单的内存移动).这个选项对UMA系统无效。简单的嵌入式Linux不需要勾选。
    • Control Group support —> 默认设置即可
    • Checkpoint/restore support 在内核中添加"检查点/恢复"支持.也就是添加一些辅助的代码用于设置进程的 text, data, heap 段,并且在 /proc 文件系统中添加一些额外的条目.主要用于调试目的。不需要勾选。
    • Namespaces support —> 命名空间支持,每个进程有自己的命名空间,防止名称重复。需要勾选。
    • Automatic process group scheduling – 自动进程组的调度。需要勾选。
    • Enable deprecated sysfs features to support old userspace tools – 使能废弃的sysfs特性来支持老的用户空间工具。不选。
    • Kernel->user space relay support (formerly relayfs) – 在某些文件系统上(比如debugfs)提供从内核空间向用户空间传递大量数据的接口 。保持默认状态。
    • Initial RAM filesystem and RAM disk (initramfs/initrd) support – 初始 RAM 文件系统是一个 ramfs,它由引导加载程序(loadlin 或 lilo)加载,并在正常引导过程之前以 root 身份安装。 它通常用于加载挂载“真实”根文件系统等所需的模块。有关详细信息,请参见 <file:Documentation/admin-guide/initrd.rst>。 如果还包括 RAM 磁盘支持 (BLK_DEV_RAM),这也会启用初始 RAM 磁盘 (initrd) 支持并将 15 KB(在某些其他体系结构上更多)添加到内核大小。不确定就勾选
    • Initramfs source file(s) – 这可以是带有 .cpio 后缀的单个 cpio 存档,也可以是用于构建 initramfs 映像的以空格分隔的目录和文件列表。 cpio 存档应包含用作 initramfs 映像的文件系统存档。 目录应包含要包含在 initramfs 映像中的文件系统布局。 文件应包含符合内核树中“usr/gen_init_cpio”程序描述的格式的条目。 当指定多个目录和文件时,initramfs 映像将是所有目录和文件的集合。
    • ***Configure standard kernel features (expert users) —>***保持默认
    • Enable bpf() system call – 开启内核的bpf()系统调用支持(从3.15版本开始引入),以支持eBPF功能.可用于内核调试与网络包过滤。勾选。
    • Use full shmem filesystem – 启用shmem支持.shmem是基于共享内存的文件系统(可能用到swap),在启用TMPFS后可以挂载为tmpfs供用户空间使用,它比简单的ramfs先进许多。
    • Enable AIO support – 支持AIO(Asynchronous I/O 异步事件非阻塞I/O),(包含aio.h, aio_read,向内核发出读命令,aio_write向内核写命令,详细见‘AIO介绍‘文档),AIO机制为服务器端高并发应用程序提供了一种性能优化的手段。加大了系统吞吐量,所以一般用于大型服务器,一般不用选。
    • Enable madvise/fadvise syscalls – 开启内核的madvise()/fadvise()系统调用支持(2.6.16版本开始引入).以允许应用程序预先提示内核,它将如何使用特定的内存与文件.这种措施有助于提升应用程序的性能.建议选”Y”.
    • Enable userfaultfd() system call – 开启内核的userfaultfd()系统调用支持(从4.3版本开始引入).Userfaults 允许从用户态实现按需分页,更一般地说,它们允许用户态控制各种内存页面错误,否则只有内核代码才能做到。
    • Enable PCI quirk workarounds – 这为各种 PCI 芯片组错误/ 怪癖提供了解决方法。 仅当您的目标机器不受 PCI 怪癖影响时才禁用此功能。
    • Enable membarrier() system call – 开启内核的membarrier()系统调用支持(与Memory Barrier相关).有助于提升多CPU场景下的并行计算性能.建议选"Y".
    • Embedded system – 嵌入式系统,一些专业的选项会出现。
    • PC/104 support – 公开 PC/104 外形设备驱动程序和可供选择和配置的选项。 如果您的目标机器有 PC/104 总线,请启用此选项。
    • Kernel Performance Events And Counters —> – 不确定就勾选。
    • Enable VM event counters for /proc/vmstat – 允许在/proc/vmstat中包含虚拟内存事件记数器。
    • Enable SLUB debugging support – SLUB 具有广泛的调试支持功能。 禁用这些可以显着节省代码大小。 这也会禁用 SLUB sysfs 支持。 /sys/slab 将不存在,也不支持缓存验证等。
    • Enable memcg SLUB sysfs support by default – SLUB 在 /sys/kernel/slab 下为每个分配缓存创建一个目录来托管信息和调试文件。 如果启用了 memory cgroup,则每个缓存可以具有每个内存 cgroup 缓存。 SLUB 可以在 /sys/kernel/slab/CACHE/cgroup 下为这些缓存创建相同的 sysfs 目录,但这会导致创建大量调试文件。 这由 slub_memcg_sysfs 引导参数控制,此配置选项确定参数的默认值。
    • Disable heap randomization – 禁用随机heap,不勾选
    • Allow slab caches to be merged – 为了减少内核内存碎片,slab 缓存可以在拥有相同大小和其他特性时合并。 这带来了内核堆溢出能够覆盖合并缓存中对象的风险(并且更容易控制缓存布局),这使得攻击者更容易利用此类堆攻击。 通过保持缓存未合并,这些类型的漏洞通常只能损坏同一缓存中的对象。 要在运行时禁用合并,可以在内核命令行上传递“slab_nomerge”。
    • SLAB freelist randomization – 随机化创建新页面时使用的空闲列表顺序。 此安全功能降低了内核slab分配器对堆溢出的可预测性。
    • Harden slab freelist metadata – 许多内核堆攻击试图针对slab缓存元数据和其他基础设施。 此选项会牺牲较小的性能以加强内核slab分配器以抵抗常见的空闲列表利用方法。
    • SLUB per cpu partial cache – 每个 cpu 部分缓存加速了处理器本地的对象分配和释放,但代价是空闲延迟的不确定性更大。 溢出时,这些缓存将被清除,这需要获取可能导致延迟峰值的锁。 通常,对于实时系统,人们会选择“否”。
    • Profiling support – 扩展的分析支持机制
    • Kprobes – kprobes调试技术是内核开发者们专门为了便于跟踪内核函数执行状态所设计的一种轻量级内核调试技术。利用kprobes技术,内核开发人员可以在内核的绝大多数指定函数中动态的插入探测点来收集所需的调试状态信息而基本不影响内核原有的执行流程。
    • Optimize very unlikely/likely branches – 对极不可能、非常可能分支的优化
    • Stack Protector buffer overflow detection – 此选项打开“堆栈保护器”GCC 功能。 此功能在函数的开头将 Canary 值放在堆栈中返回地址之前的值,并在实际返回之前验证该值。 基于堆栈的缓冲区溢出(需要覆盖此返回地址)现在也会覆盖金丝雀,它会被检测到,然后通过内核恐慌消除攻击。
    • Use a virtually-mapped stack – 如果您希望使用带有保护页的虚拟映射内核堆栈,请启用此选项。 这会导致内核堆栈溢出立即被捕获,而不是导致难以诊断的损坏。 这目前与 KASAN 不兼容,因为如果堆栈在 vmalloc 空间中,则 KASAN 期望堆栈使用不正确的公式直接映射到 KASAN 阴影贴图。
    • Perform full reference count validation at the expense of speed – 启用此功能会将引用计数基础结构从快速的未经检查的 atomic_t 实现切换到完全经过状态检查的实现,这可能会(稍微)慢一些,但提供了针对可用于安全漏洞利用的各种释放后使用条件的保护。
    • GCOV-based kernel profiling – 不确定就不勾选
    展开全文
  • Linux perf 1.1、perf_event内核框架

    万次阅读 多人点赞 2018-07-25 12:18:53
    通过perf_event_open系统调用分配到perf_event以后,会返回一个文件句柄fd,这样这个perf_event结构可以通过read/write/ioctl/mmap通用文件接口来操作。 perf_event提供两种类型的trace数据: count 和 sample 。...

    为什么有了ftrace又出来一个perf?因为ftrace只管抓trace数据并没有分析,perf在trace数据分析方面做出了很多成果。

    在trace数据采集方面,perf复用了ftrace的所有插桩点,并且加入了采样法(硬件PMU)。PMU是一种非常重要的数据采集方法,因为它大部分是硬件的,所以可以做到一些软件做不到的事情,获取到一些底层硬件的信息。

    perf的基本包装模型是这样的,对每一个event分配一个对应的perf_event结构。所有对event的操作都是围绕perf_event来展开的:

    • 通过perf_event_open系统调用分配到perf_event以后,会返回一个文件句柄fd,这样这个perf_event结构可以通过read/write/ioctl/mmap通用文件接口来操作。
    • perf_event提供两种类型的trace数据:countsample。count只是记录了event的发生次数,sample记录了大量信息(比如:IP、ADDR、TID、TIME、CPU、BT)。如果需要使用sample功能,需要给perf_event分配ringbuffer空间,并且把这部分空间通过mmap映射到用户空间。这和定位问题时从粗到细的思路是相符的,首先从counter的比例上找出问题热点在哪个模块,再使用详细记录抓取更多信息来进一步定位。具体分别对应“perf stat”和“perf record/report”命令。
    • perf的开销应该是比ftrace要大的,因为它给每个event都独立一套数据结构perf_event,对应独立的attr和pmu。在数据记录时的开销肯定大于ftrace,但是每个event的ringbuffer是独立的所以也不需要ftrace复杂的ringbuffer操作。perf也有比ftrace开销小的地方,它的sample数据存储的ringbuffer空间会通过mmap映射到到用户态,这样在读取数据的时候就会少一次拷贝。不过perf的设计初衷也不是让成百上千的event同时使用,只会挑出一些event重点debug。

    0、perf_event的组织

    从上面的描述看per就是一个个perf_event并不复杂,那么复杂在哪里呢?真正的难点在于对event的组织,怎么把全局的event的资源,按照用户的需要分割成cpu维度/task维度。

    我们在分析问题的时候,并不是一下子深入到底层event直接来看数据(如果不加区别event记录的是整系统的数据),我们会遵从系统->cpu->进程来分析问题。针对实际的需求,perf使用cpu维度/task维度来组织perf_event。

    我们具体来看看perf_event的组织方法:

    • 1、cpu维度

      使用perf_event_context类型的链表来连接本cpu的相关perf_event。这样的链表共有两条(perf_hw_context = 0, perf_sw_context = 1),链表指针存放在per_cpu变量pmu->pmu_cpu_context.ctx中由所有同类型的pmu共享。

      这里写图片描述

    • 2、task维度

      使用perf_event_context类型的链表来连接本task的相关perf_event。这样的链表共有两条(perf_hw_context = 0, perf_sw_context = 1),链表指针存放在task->perf_event_ctxp[ctxn]变量中。
      这里写图片描述

    • 3、perf_event_open()系统调用使用cpu、pid两个参数来指定perf_event的cpu、task维度。

      pid == 0: event绑定到当前进程;
      pid > 0: event绑定到指定进程;
      pid == -1: event绑定到当前cpu的所有进程。
      cpu >= 0: event绑定到指定cpu;
      cpu == -1: event绑定到所有cpu;

      在同时指定的情况下task维度优先于cpu维度,所以pid、cpu组合起来有以下几种情况:
      组合1:pid >= 0, cpu >= 0。perf_event绑定到task维度的context。task在得到cpu调度运行的时候,context上挂载的本task相关的perf_event也开始运行。但是如果event指定的cpu不等于当前运行的cpu,event不会得到执行,这样就符合了这个组合的含义;
      组合2:pid >= 0, cpu == -1。perf_event绑定到task维度的context。只要task得到调度,该perf_event就会得到执行;
      组合3:pid == -1, cpu >= 0。perf_event绑定到cpu维度的context。只要该cpu运行,该perf_event就会得到执行。目前只有在cpu online的情况下才能绑定perf_event,cpu hotplug支持可能会有问题;
      组合4:pid == -1, cpu == -1。这种组合目前是非法的,相当于整系统所有cpu、所有进程。

    • 4、group leader

      cpu/task context使用->event_list链表来连接所有的perf_event。这些perf_event还允许以group的形式来组织,context使用->pinned_groups/flexible_groups链表来连接group leader的perf_event;group leader使用->sibling_list链表来连接所有的group member perf_event。组织形式参考上图。

      group的作用是在read count的时候可以一次性读出group中所有perf_event的count。

      perf_event_open()系统调用使用group_fd参数来指定perf_event的group_leader:>=0指定对于的perf_event为当前group_leader,== -1创建新的group_leader。

      pinned:可以看到group leader被放到两个链表中(->pinned_groups/flexible_groups),attr.pinned=1指定放到高优先级链表->pinned_groups中。

      (具体参见后面perf_install_in_context()的代码解析)

    • 5、perf_task_sched

      对于cpu维度的perf_event来说只要cpu online会一直运行,而对于task维度的perf_event来说只有在task得到调度运行的时候event才能运行。所以在每个cpu上同时只能有一个task维度的perf_evnt得到执行,cpu维度的context使用了pmu->pmu_cpu_context->task_ctx指针来保存当前运行的task context。

      这里写图片描述

      perf驱动层的精髓就在于此:在合适的时间合适的开关对应的perf_event。(具体参见后面perf_event_task_sched_in()、perf_event_task_sched_out()的代码解析)

      在单个cpu上,多个任务调度时context/perf_event的开关情况:

      这里写图片描述

      单个任务,在多个cpu上调度时context/perf_event的开关情况:

      这里写图片描述

    • 6、inherit

      inherit属性指定如果perf_event绑定的task创建子进程,event自动的对子进程也进行追踪。这在实际使用时是非常有用的,我们追踪一个app,随后它创建出的子进程/线程都能自动的被追踪。

      父进程中所有attr.inherit=1的event被子进程所继承和复制,在使用PERF_FORMAT_GROUP读event的count值时,会把inherit所有子进程的值累加进来。(具体参见后面perf_event_init_task()、perf_read_group()的代码解析)

      这里写图片描述

    • 7、exclusive

      如果pmu有PERF_PMU_CAP_EXCLUSIVE属性,表明它要么只能被绑定为cpu维度、要么只能被绑定为task维度,不能混合绑定。(具体参见后面exclusive_event_init()的代码解析)

    • 8、pmu的数据供给:

      每个pmu拥有一个per_cpu的链表,perf_event需要在哪个cpu上获取数据就加入到哪个cpu的链表上。如果event被触发,它会根据当前的运行cpu给对应链表上的所有perf_event推送数据。

      cpu维度的context:this_cpu_ptr(pmu->pmu_cpu_context->ctx)上链接的所有perf_event会根据绑定的pmu,链接到pmu对应的per_cpu的->perf_events链表上。
      task维度的context:this_cpu_ptr(pmu->pmu_cpu_context->task_ctx)上链接的所有perf_event会根据绑定的pmu,链接到pmu对应的per_cpu的->perf_events链表上。perf_event还需要做cpu匹配,符合(event->cpu == -1 || event->cpu == smp_processor_id())条件的event才能链接到pmu上。

      这里写图片描述

    • 9、enable_on_exec

      perf_event的状态(event->state)典型值有以下3种:
      disable:PERF_EVENT_STATE_OFF = -1, // 如果attr.disabled = 1,event->state的初始值
      enable/inactive:PERF_EVENT_STATE_INACTIVE = 0, // 如果attr.disabled = 0,event->state的初始值
      active:PERF_EVENT_STATE_ACTIVE = 1,

      attr.disabled属性决定了perf_event的初始化状态(disable/enable)。只有perf_event为enable以后才能参与schedule,在schedule过程中perf_event被使能时为active,关闭后恢复成enbale/inactive状态。

      perf_event变成enable状态有3种方法:
      1、attr.disabled = 0;
      2、attr.disabled = 1,创建后使用ioctl的PERF_EVENT_IOC_ENABLE命令来使能;
      3、attr.disabled = 1,attr.enable_on_exec = 1。这样使用execl执行新程序时使能event,这是一种非常巧妙的同步手段;

    • 10、ringbuffer:

      如果需要读取perf_event的sample类型的数据,需要先给perf_event分配一个对应的ringbuffer,为了减少开销这个ringbuffer会被mmap映射成用户态地址。

      如上图所示整个ringbuffer空间分成3部分:
      head:size = 1 page。主要用来存储控制数据,指针等等
      data:size = 2^n pages。主要用来存储perf_event的sample数据
      aux data:size = 2^n pages。作用暂时没有看明白

      如果perf_event支持inherit属性,那么它所有的子进程上继承的perf_event的sample数据,都会保存到父perf_event的ringbuffer中。perf_event可以inherit,但是ringbuffer不会重新分配,会共用父event的ringbuffer。

      这里写图片描述

    • 11、sample_period/sample_freq:

    1、perf_event初始化

    perf_event初始化的时候将各种pmu注册到pmus链表。

    start_kernel() -> perf_event_init():

    void __init perf_event_init(void)
    {
        int ret;
    
        /* (1) 初始化idr,给动态type的pmu来分配 */
        idr_init(&pmu_idr);
    
        /* (2) 初始化per_cpu变量swevent_htable */
        perf_event_init_all_cpus();
        init_srcu_struct(&pmus_srcu);
    
        /* (3) 注册"software" pmu */
        perf_pmu_register(&perf_swevent, "software", PERF_TYPE_SOFTWARE);
        perf_pmu_register(&perf_cpu_clock, NULL, -1);
        perf_pmu_register(&perf_task_clock, NULL, -1);
    
        /* (4) 注册"tracepoint" pmu */
        perf_tp_register();
        perf_cpu_notifier(perf_cpu_notify);
        idle_notifier_register(&perf_event_idle_nb);
        register_reboot_notifier(&perf_reboot_notifier);
    
        /* (5) 注册"breakpoint" pmu */
        ret = init_hw_breakpoint();
        WARN(ret, "hw_breakpoint initialization failed with: %d", ret);
    
        /* do not patch jump label more than once per second */
        jump_label_rate_limit(&perf_sched_events, HZ);
    
        /*
         * Build time assertion that we keep the data_head at the intended
         * location.  IOW, validation we got the __reserved[] size right.
         */
        BUILD_BUG_ON((offsetof(struct perf_event_mmap_page, data_head))
                 != 1024);
    }
    
    ↓
    
    int perf_pmu_register(struct pmu *pmu, const char *name, int type)
    {
        int cpu, ret;
    
        mutex_lock(&pmus_lock);
        ret = -ENOMEM;
        pmu->pmu_disable_count = alloc_percpu(int);
        if (!pmu->pmu_disable_count)
            goto unlock;
    
        /* (3.1) 如果name = null,则pmu->name=null、pmu->type=-1 */
        pmu->type = -1;
        if (!name)
            goto skip_type;
    
        /* (3.1.1) pmu->name = name */
        pmu->name = name;
    
        /* (3.1.2) 如果type < 0,在idr中动态分配值给pmu->type */
        if (type < 0) {
            type = idr_alloc(&pmu_idr, pmu, PERF_TYPE_MAX, 0, GFP_KERNEL);
            if (type < 0) {
                ret = type;
                goto free_pdc;
            }
        }
        pmu->type = type;
    
        if (pmu_bus_running) {
            ret = pmu_dev_alloc(pmu);
            if (ret)
                goto free_idr;
        }
    
        /* (3.2) 初始化cpu维度的perf_cpu_context,
           perf_cpu_context的作用是用来把某一维度的perf_event链接到一起 
         */
    skip_type:
        /* (3.2.1) 如果有相同task_ctx_nr类型的pmu已经创建perf_cpu_context结构, 
            直接引用
         */
        pmu->pmu_cpu_context = find_pmu_context(pmu->task_ctx_nr);
        if (pmu->pmu_cpu_context)
            goto got_cpu_context;
    
        ret = -ENOMEM;
        /* (3.2.2) 如果没有人创建本pmu task_ctx_nr类型的perf_cpu_context结构, 
            重新创建
         */
        pmu->pmu_cpu_context = alloc_percpu(struct perf_cpu_context);
        if (!pmu->pmu_cpu_context)
            goto free_dev;
    
        /* (3.2.3) 初始化per_cpu的perf_cpu_context结构 */
        for_each_possible_cpu(cpu) {
            struct perf_cpu_context *cpuctx;
    
            cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu);
            __perf_event_init_context(&cpuctx->ctx);
            lockdep_set_class(&cpuctx->ctx.mutex, &cpuctx_mutex);
            lockdep_set_class(&cpuctx->ctx.lock, &cpuctx_lock);
            cpuctx->ctx.pmu = pmu;
    
            __perf_mux_hrtimer_init(cpuctx, cpu);
    
            cpuctx->unique_pmu = pmu;
        }
    
    got_cpu_context:
        /* (3.3) 给pmu赋值一些默认的操作函数 */
        if (!pmu->start_txn) {
            if (pmu->pmu_enable) {
                /*
                 * If we have pmu_enable/pmu_disable calls, install
                 * transaction stubs that use that to try and batch
                 * hardware accesses.
                 */
                pmu->start_txn  = perf_pmu_start_txn;
                pmu->commit_txn = perf_pmu_commit_txn;
                pmu->cancel_txn = perf_pmu_cancel_txn;
            } else {
                pmu->start_txn  = perf_pmu_nop_txn;
                pmu->commit_txn = perf_pmu_nop_int;
                pmu->cancel_txn = perf_pmu_nop_void;
            }
        }
    
        if (!pmu->pmu_enable) {
            pmu->pmu_enable  = perf_pmu_nop_void;
            pmu->pmu_disable = perf_pmu_nop_void;
        }
    
        if (!pmu->event_idx)
            pmu->event_idx = perf_event_idx_default;
    
        /* (3.4) 最重要的一步:将新的pmu加入到pmus链表中 */
        list_add_rcu(&pmu->entry, &pmus);
        atomic_set(&pmu->exclusive_cnt, 0);
        ret = 0;
    unlock:
        mutex_unlock(&pmus_lock);
    
        return ret;
    
    free_dev:
        device_del(pmu->dev);
        put_device(pmu->dev);
    
    free_idr:
        if (pmu->type >= PERF_TYPE_MAX)
            idr_remove(&pmu_idr, pmu->type);
    
    free_pdc:
        free_percpu(pmu->pmu_disable_count);
        goto unlock;
    }
    

    另外一个函数perf_event_sysfs_init()会在稍后的device_initcall中,为所有“pmu->name != null”的pmu创建对应的device:

    static int __init perf_event_sysfs_init(void)
    {
        struct pmu *pmu;
        int ret;
    
        mutex_lock(&pmus_lock);
    
        /* (1) 注册pmu_bus */
        ret = bus_register(&pmu_bus);
        if (ret)
            goto unlock;
    
        /* (2) 遍历pmus链表,创建pmu对应的device */
        list_for_each_entry(pmu, &pmus, entry) {
            /* 如果pmu->name = null或者pmu->type < 0,不创建 */
            if (!pmu->name || pmu->type < 0)
                continue;
    
            ret = pmu_dev_alloc(pmu);
            WARN(ret, "Failed to register pmu: %s, reason %d\n", pmu->name, ret);
        }
    
        /* (3) 设置pmu_bus_running */
        pmu_bus_running = 1;
        ret = 0;
    
    unlock:
        mutex_unlock(&pmus_lock);
    
        return ret;
    }
    device_initcall(perf_event_sysfs_init);

    可以在/sys路径下看到对应的device:

     # ls /sys/bus/event_source/devices/
    armv8_pmuv3 software tracepoint

    2、perf_event_open系统调用

    perf_event_open会创建event对应的perf_event结构,按照perf_event_attr参数把perf_event和对应的pmu以及perf_cpu_context(cpu维度/task维度)绑定,最后再把perf_event和perf_fops以及fd绑定,返回fd给系统进行文件操作。

    理解perf_event_open系统调用先理解它的5个参数:

    perf_event_open(struct perf_event_attr attr, pid_t pid, int cpu, int group_fd, unsigned long flags)
    • 参数1、struct perf_event_attr attr。该参数是最复杂也是最重要的参数:
    struct perf_event_attr {
    
        /*
         * Major type: hardware/software/tracepoint/etc.
         */
        /* (1) 指定pmu的type:
            enum perf_type_id {
                PERF_TYPE_HARDWARE          = 0,
                PERF_TYPE_SOFTWARE          = 1,
                PERF_TYPE_TRACEPOINT            = 2,
                PERF_TYPE_HW_CACHE          = 3,
                PERF_TYPE_RAW               = 4,
                PERF_TYPE_BREAKPOINT            = 5,
    
                PERF_TYPE_MAX,              
            };
         */
        __u32           type;
    
        /*
         * Size of the attr structure, for fwd/bwd compat.
         */
        /* (2) 整个perf_event_attr结构体的size */
        __u32           size;
    
        /*
         * Type specific configuration information.
         */
        /* (3) 不同type的pmu,config的含义也不同:
            1、type = PERF_TYPE_HARDWARE:
                enum perf_hw_id {
                    PERF_COUNT_HW_CPU_CYCLES        = 0,
                    PERF_COUNT_HW_INSTRUCTIONS      = 1,
                    PERF_COUNT_HW_CACHE_REFERENCES      = 2,
                    PERF_COUNT_HW_CACHE_MISSES      = 3,
                    PERF_COUNT_HW_BRANCH_INSTRUCTIONS   = 4,
                    PERF_COUNT_HW_BRANCH_MISSES     = 5,
                    PERF_COUNT_HW_BUS_CYCLES        = 6,
                    PERF_COUNT_HW_STALLED_CYCLES_FRONTEND   = 7,
                    PERF_COUNT_HW_STALLED_CYCLES_BACKEND    = 8,
                    PERF_COUNT_HW_REF_CPU_CYCLES        = 9,
    
                    PERF_COUNT_HW_MAX,          
                };
            2、type = PERF_TYPE_SOFTWARE:
                enum perf_sw_ids {
                    PERF_COUNT_SW_CPU_CLOCK         = 0,
                    PERF_COUNT_SW_TASK_CLOCK        = 1,
                    PERF_COUNT_SW_PAGE_FAULTS       = 2,
                    PERF_COUNT_SW_CONTEXT_SWITCHES      = 3,
                    PERF_COUNT_SW_CPU_MIGRATIONS        = 4,
                    PERF_COUNT_SW_PAGE_FAULTS_MIN       = 5,
                    PERF_COUNT_SW_PAGE_FAULTS_MAJ       = 6,
                    PERF_COUNT_SW_ALIGNMENT_FAULTS      = 7,
                    PERF_COUNT_SW_EMULATION_FAULTS      = 8,
                    PERF_COUNT_SW_DUMMY         = 9,
                    PERF_COUNT_SW_BPF_OUTPUT        = 10,
    
                    PERF_COUNT_SW_MAX,          
                };
            3、type = PERF_TYPE_TRACEPOINT:
                trace_point对应trace_event的id:“/sys/kernel/debug/tracing/events/x/x/id”
            4、type = PERF_TYPE_HW_CACHE:
                enum perf_hw_cache_id {
                    PERF_COUNT_HW_CACHE_L1D         = 0,
                    PERF_COUNT_HW_CACHE_L1I         = 1,
                    PERF_COUNT_HW_CACHE_LL          = 2,
                    PERF_COUNT_HW_CACHE_DTLB        = 3,
                    PERF_COUNT_HW_CACHE_ITLB        = 4,
                    PERF_COUNT_HW_CACHE_BPU         = 5,
                    PERF_COUNT_HW_CACHE_NODE        = 6,
    
                    PERF_COUNT_HW_CACHE_MAX,        
                };
         */
        __u64           config;
    
        /* (4) period/freq sample模式的具体数值 */
        union {
            __u64       sample_period;
            __u64       sample_freq;
        };
    
        /* (5) 在sample数据时,需要保存哪些数据:
            enum perf_event_sample_format {
                PERF_SAMPLE_IP              = 1U << 0,
                PERF_SAMPLE_TID             = 1U << 1,
                PERF_SAMPLE_TIME            = 1U << 2,
                PERF_SAMPLE_ADDR            = 1U << 3,
                PERF_SAMPLE_READ            = 1U << 4,
                PERF_SAMPLE_CALLCHAIN           = 1U << 5,
                PERF_SAMPLE_ID              = 1U << 6,
                PERF_SAMPLE_CPU             = 1U << 7,
                PERF_SAMPLE_PERIOD          = 1U << 8,
                PERF_SAMPLE_STREAM_ID           = 1U << 9,
                PERF_SAMPLE_RAW             = 1U << 10,
                PERF_SAMPLE_BRANCH_STACK        = 1U << 11,
                PERF_SAMPLE_REGS_USER           = 1U << 12,
                PERF_SAMPLE_STACK_USER          = 1U << 13,
                PERF_SAMPLE_WEIGHT          = 1U << 14,
                PERF_SAMPLE_DATA_SRC            = 1U << 15,
                PERF_SAMPLE_IDENTIFIER          = 1U << 16,
                PERF_SAMPLE_TRANSACTION         = 1U << 17,
                PERF_SAMPLE_REGS_INTR           = 1U << 18,
    
                PERF_SAMPLE_MAX = 1U << 19, 
            };
         */
        __u64           sample_type;
    
        /* (6) 在read counter数据时,读取的格式:
             *
             * The format of the data returned by read() on a perf event fd,
             * as specified by attr.read_format:
             *
             * struct read_format {
             *  { u64       value;
             *    { u64     time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
             *    { u64     time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
             *    { u64     id;           } && PERF_FORMAT_ID
             *  } && !PERF_FORMAT_GROUP
             *
             *  { u64       nr;
             *    { u64     time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
             *    { u64     time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
             *    { u64     value;
             *      { u64   id;           } && PERF_FORMAT_ID
             *    }     cntr[nr];
             *  } && PERF_FORMAT_GROUP
             * };
             *
            enum perf_event_read_format {
                PERF_FORMAT_TOTAL_TIME_ENABLED      = 1U << 0,
                PERF_FORMAT_TOTAL_TIME_RUNNING      = 1U << 1,
                PERF_FORMAT_ID              = 1U << 2,
                PERF_FORMAT_GROUP           = 1U << 3,
    
                PERF_FORMAT_MAX = 1U << 4,      
            };
         */
        __u64           read_format;
    
        /* (7) bit标志 */
                    /* (7.1) 定义event的初始状态为disable/enable。
                        如果初始被disable,后续可以通过ioctl/prctl来enable。 
                     */
        __u64           disabled       :  1, /* off by default        */
    
                    /* (7.2) 如果该标志被设置,event进程对应的子孙后代的子进程都会计入counter */
                    inherit        :  1, /* children inherit it   */
    
                    /* (7.3) 如果该标志被设置,event和cpu绑定。(只适用于硬件counter只适用于group leaders) */
                    pinned         :  1, /* must always be on PMU */
    
                    /* (7.4) 如果该标志被设置,指定当这个group在CPU上时,它应该是唯一使用CPU计数器的group */
                    exclusive      :  1, /* only group on PMU     */
    
                    /* (7.5) exclude_user/exclude_kernel/exclude_hv/exclude_idle这几个标志用来标识,
                        不要记录对应场景的数据
                     */
                    exclude_user   :  1, /* don't count user      */
                    exclude_kernel :  1, /* ditto kernel          */
                    exclude_hv     :  1, /* ditto hypervisor      */
                    exclude_idle   :  1, /* don't count when idle */
    
                    /* (7.6) 允许记录PROT_EXEC mmap操作 */
                    mmap           :  1, /* include mmap data     */
    
                    /* (7.7) 允许记录进程创建时的comm数据 */
                    comm           :  1, /* include comm data     */
    
                    /* (7.8) 确定sample模式 = freq/period */
                    freq           :  1, /* use freq, not period  */
    
    
                    inherit_stat   :  1, /* per task counts       */
                    enable_on_exec :  1, /* next exec enables     */
                    task           :  1, /* trace fork/exit       */
                    watermark      :  1, /* wakeup_watermark      */
                    /*
                     * precise_ip:
                     *
                     *  0 - SAMPLE_IP can have arbitrary skid
                     *  1 - SAMPLE_IP must have constant skid
                     *  2 - SAMPLE_IP requested to have 0 skid
                     *  3 - SAMPLE_IP must have 0 skid
                     *
                     *  See also PERF_RECORD_MISC_EXACT_IP
                     */
                    precise_ip     :  2, /* skid constraint       */
                    mmap_data      :  1, /* non-exec mmap data    */
                    sample_id_all  :  1, /* sample_type all events */
    
                    exclude_host   :  1, /* don't count in host   */
                    exclude_guest  :  1, /* don't count in guest  */
    
                    exclude_callchain_kernel : 1, /* exclude kernel callchains */
                    exclude_callchain_user   : 1, /* exclude user callchains */
                    mmap2          :  1, /* include mmap with inode data     */
                    comm_exec      :  1, /* flag comm events that are due to an exec */
                    use_clockid    :  1, /* use @clockid for time fields */
                    context_switch :  1, /* context switch data */
                    constraint_duplicate : 1,
    
                    __reserved_1   : 36;
    
        union {
            __u32       wakeup_events;    /* wakeup every n events */
            __u32       wakeup_watermark; /* bytes before wakeup   */
        };
    
        __u32           bp_type;
        union {
            __u64       bp_addr;
            __u64       config1; /* extension of config */
        };
        union {
            __u64       bp_len;
            __u64       config2; /* extension of config1 */
        };
        __u64   branch_sample_type; /* enum perf_branch_sample_type */
    
        /*
         * Defines set of user regs to dump on samples.
         * See asm/perf_regs.h for details.
         */
        __u64   sample_regs_user;
    
        /*
         * Defines size of the user stack to dump on samples.
         */
        __u32   sample_stack_user;
    
        __s32   clockid;
        /*
         * Defines set of regs to dump for each sample
         * state captured on:
         *  - precise = 0: PMU interrupt
         *  - precise > 0: sampled instruction
         *
         * See asm/perf_regs.h for details.
         */
        __u64   sample_regs_intr;
    
        /*
         * Wakeup watermark for AUX area
         */
        __u32   aux_watermark;
        __u32   __reserved_2;   /* align to __u64 */
    }
    • 参数2、pid_t pid:

      pid == 0: event绑定到当前进程;
      pid > 0: event绑定到指定进程;
      pid < 0: event绑定到当前cpu的所有进程。

    • 参数3、int cpu:

      cpu >= 0: event绑定到指定cpu;
      cpu == -1: event绑定到所有cpu;
      (注意’pid == -1’、’cpu == -1’同时存在是非法的)

    • 参数4、int group_fd:

      group_fd = -1:创建一个新的group leader;
      group_fd > 0:加入到之前创建的group leader中。

    • 参数5、unsigned long flags:

    #define PERF_FLAG_FD_NO_GROUP       (1UL << 0)
    #define PERF_FLAG_FD_OUTPUT     (1UL << 1)
    #define PERF_FLAG_PID_CGROUP        (1UL << 2) /* pid=cgroup id, per-cpu mode only */
    #define PERF_FLAG_FD_CLOEXEC        (1UL << 3) /* O_CLOEXEC */

    perf_event_open()函数的完全解析如下:

    SYSCALL_DEFINE5(perf_event_open,
            struct perf_event_attr __user *, attr_uptr,
            pid_t, pid, int, cpu, int, group_fd, unsigned long, flags)
    {
        struct perf_event *group_leader = NULL, *output_event = NULL;
        struct perf_event *event, *sibling;
        struct perf_event_attr attr;
        struct perf_event_context *ctx, *uninitialized_var(gctx);
        struct file *event_file = NULL;
        struct fd group = {NULL, 0};
        struct task_struct *task = NULL;
        struct pmu *pmu;
        int event_fd;
        int move_group = 0;
        int err;
        int f_flags = O_RDWR;
        int cgroup_fd = -1;
    
        /* (1) 一系列的合法性判断和准备工作 */
        /* for future expandability... */
        if (flags & ~PERF_FLAG_ALL)
            return -EINVAL;
    
        /* (1.1) 权限判断 */
        if (perf_paranoid_any() && !capable(CAP_SYS_ADMIN))
            return -EACCES;
    
        /* (1.2) 拷贝用户态的perf_event_attr到内核态 */
        err = perf_copy_attr(attr_uptr, &attr);
        if (err)
            return err;
    
        if (attr.constraint_duplicate || attr.__reserved_1)
            return -EINVAL;
    
        if (!attr.exclude_kernel) {
            if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN))
                return -EACCES;
        }
    
        /* (1.3) 如果sample是freq mode,sample_freq的合法性判断 */
        if (attr.freq) {
            if (attr.sample_freq > sysctl_perf_event_sample_rate)
                return -EINVAL;
        /* (1.4) 如果sample是period mode,sample_period的合法性判断 */
        } else {
            if (attr.sample_period & (1ULL << 63))
                return -EINVAL;
        }
    
        /*
         * In cgroup mode, the pid argument is used to pass the fd
         * opened to the cgroup directory in cgroupfs. The cpu argument
         * designates the cpu on which to monitor threads from that
         * cgroup.
         */
        if ((flags & PERF_FLAG_PID_CGROUP) && (pid == -1 || cpu == -1))
            return -EINVAL;
    
        if (flags & PERF_FLAG_FD_CLOEXEC)
            f_flags |= O_CLOEXEC;
    
        /* (1.5) 当前进程获取一个新的fd编号  */
        event_fd = get_unused_fd_flags(f_flags);
        if (event_fd < 0)
            return event_fd;
    
        /* (1.6) 如果当前event需要加入到指定的group leader中,获取到: 
            group_fd对应的fd结构 和 perf_event结构
         */
        if (group_fd != -1) {
            err = perf_fget_light(group_fd, &group);
            if (err)
                goto err_fd;
            group_leader = group.file->private_data;
            if (flags & PERF_FLAG_FD_OUTPUT)
                output_event = group_leader;
            if (flags & PERF_FLAG_FD_NO_GROUP)
                group_leader = NULL;
        }
    
        /*
         * Take the group_leader's group_leader_mutex before observing
         * anything in the group leader that leads to changes in ctx,
         * many of which may be changing on another thread.
         * In particular, we want to take this lock before deciding
         * whether we need to move_group.
         */
        if (group_leader)
            mutex_lock(&group_leader->group_leader_mutex);
    
        /* (1.7) 找到pid对应的task_struct结构 */
        if (pid != -1 && !(flags & PERF_FLAG_PID_CGROUP)) {
            task = find_lively_task_by_vpid(pid);
            if (IS_ERR(task)) {
                err = PTR_ERR(task);
                goto err_group_fd;
            }
        }
    
        /* (1.8) 如果是加入到group leader,需要两者的attr.inherit属性一致 */
        if (task && group_leader &&
            group_leader->attr.inherit != attr.inherit) {
            err = -EINVAL;
            goto err_task;
        }
    
        get_online_cpus();
    
        if (task) {
            err = mutex_lock_interruptible(&task->signal->cred_guard_mutex);
            if (err)
                goto err_cpus;
    
            /*
             * Reuse ptrace permission checks for now.
             *
             * We must hold cred_guard_mutex across this and any potential
             * perf_install_in_context() call for this new event to
             * serialize against exec() altering our credentials (and the
             * perf_event_exit_task() that could imply).
             */
            err = -EACCES;
            if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS))
                goto err_cred;
        }
    
        /* (1.9) 创建cgroup fd,这和之前的group leader不一样 */
        if (flags & PERF_FLAG_PID_CGROUP)
            cgroup_fd = pid;
    
        /* (2) 重头戏:根据传入的参数,分配perf_event结构并初始化 */
        event = perf_event_alloc(&attr, cpu, task, group_leader, NULL,
                     NULL, NULL, cgroup_fd);
        if (IS_ERR(event)) {
            err = PTR_ERR(event);
            goto err_cred;
        }
    
        /* (3.1) 如果attr指定需要sample数据,但是pmu没有中断能力,返回出错(主要是针对硬件pmu) */
        if (is_sampling_event(event)) {
            if (event->pmu->capabilities & PERF_PMU_CAP_NO_INTERRUPT) {
                err = -ENOTSUPP;
                goto err_alloc;
            }
        }
    
        /*
         * Special case software events and allow them to be part of
         * any hardware group.
         */
        pmu = event->pmu;
    
        /* (3.2) 如果用户指定时钟源,把event->clock设置为用户指定值 */
        if (attr.use_clockid) {
            err = perf_event_set_clock(event, attr.clockid);
            if (err)
                goto err_alloc;
        }
    
        /* (3.3) 如果event和group_leader的pmu type不一致的处理 */
        if (group_leader &&
            (is_software_event(event) != is_software_event(group_leader))) {
            /* (3.3.1) pmu type: event == software, group_leader != software 
                把event加入到group_leader中
             */
            if (is_software_event(event)) {
                /*
                 * If event and group_leader are not both a software
                 * event, and event is, then group leader is not.
                 *
                 * Allow the addition of software events to !software
                 * groups, this is safe because software events never
                 * fail to schedule.
                 */
                pmu = group_leader->pmu;
    
            /* (3.3.2) pmu type: event != software, group_leader == software 
                尝试把整个group移入到hardware context中
             */
            } else if (is_software_event(group_leader) &&
                   (group_leader->group_flags & PERF_GROUP_SOFTWARE)) {
                /*
                 * In case the group is a pure software group, and we
                 * try to add a hardware event, move the whole group to
                 * the hardware context.
                 */
                move_group = 1;
            }
        }
    
        /*
         * Get the target context (task or percpu):
         */
        /* (4) get到perf_event_context,根据perf_event类型得到cpu维度/task维度的context:
            如果pid=-1即task=NULL,获得cpu维度的context,即pmu注册时根据pmu->task_ctx_nr分配的pmu->pmu_cpu_context->ctx
            如果pid>=0即task!=NULL,获得task维度的context,即task->perf_event_ctxp[ctxn],如果为空则重新创建
         */
        ctx = find_get_context(pmu, task, event);
        if (IS_ERR(ctx)) {
            err = PTR_ERR(ctx);
            goto err_alloc;
        }
    
        /* (5.1) event需要加入到group_leader,如果(pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE),出错返回  */
        if ((pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE) && group_leader) {
            err = -EBUSY;
            goto err_context;
        }
    
        /*
         * Look up the group leader (we will attach this event to it):
         */
        /* (5.2) event需要加入到group_leader,对一些条件进行合法性判断  */
        if (group_leader) {
            err = -EINVAL;
    
            /*
             * Do not allow a recursive hierarchy (this new sibling
             * becoming part of another group-sibling):
             */
            /* (5.2.1) 不允许递归的->group_leader */
            if (group_leader->group_leader != group_leader)
                goto err_context;
    
            /* All events in a group should have the same clock */
            /* (5.2.2) event加入gruop,需要时钟源一致 */
            if (group_leader->clock != event->clock)
                goto err_context;
    
            /*
             * Do not allow to attach to a group in a different
             * task or CPU context:
             */
            /* (5.2.3) event加入gruop,需要task/cpu的context一致 */
            if (move_group) {
                /*
                 * Make sure we're both on the same task, or both
                 * per-cpu events.
                 */
                if (group_leader->ctx->task != ctx->task)
                    goto err_context;
    
                /*
                 * Make sure we're both events for the same CPU;
                 * grouping events for different CPUs is broken; since
                 * you can never concurrently schedule them anyhow.
                 */
                if (group_leader->cpu != event->cpu)
                    goto err_context;
            } else {
                if (group_leader->ctx != ctx)
                    goto err_context;
            }
    
            /*
             * Only a group leader can be exclusive or pinned
             */
            /* (5.2.4) 只有group才能设置exclusive/pinned属性 */
            if (attr.exclusive || attr.pinned)
                goto err_context;
        }
    
        /* (5.3) 设置output_event */
        if (output_event) {
            err = perf_event_set_output(event, output_event);
            if (err)
                goto err_context;
        }
    
        /* (6) 分配perf_event对应的file结构: 
            file->private_data = event; // file和event结构链接在一起
            file->f_op = perf_fops;    // file的文件操作函数集
            后续会把fd和file链接到一起:fd_install(event_fd, event_file);
         */
        event_file = anon_inode_getfile("[perf_event]", &perf_fops, event,
                        f_flags);
        if (IS_ERR(event_file)) {
            err = PTR_ERR(event_file);
            event_file = NULL;
            goto err_context;
        }
    
        if (move_group) {
            gctx = group_leader->ctx;
            mutex_lock_double(&gctx->mutex, &ctx->mutex);
        } else {
            mutex_lock(&ctx->mutex);
        }
    
        /* (7.1) 根据attr计算:event->read_size、event->header_size、event->id_header_size 
            并判断是否有超长
         */
        if (!perf_event_validate_size(event)) {
            err = -E2BIG;
            goto err_locked;
        }
    
        /*
         * Must be under the same ctx::mutex as perf_install_in_context(),
         * because we need to serialize with concurrent event creation.
         */
        /* (7.2) 如果是排他性event:(pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE) 
            检查context链表中现有的event是否允许新的event插入
         */
        if (!exclusive_event_installable(event, ctx)) {
            /* exclusive and group stuff are assumed mutually exclusive */
            WARN_ON_ONCE(move_group);
    
            err = -EBUSY;
            goto err_locked;
        }
    
        WARN_ON_ONCE(ctx->parent_ctx);
    
        /*
         * This is the point on no return; we cannot fail hereafter. This is
         * where we start modifying current state.
         */
    
        /* (8) 如果group和当前event的pmu type不一致,
            尝试更改context到当前event
         */
        if (move_group) {
            /*
             * See perf_event_ctx_lock() for comments on the details
             * of swizzling perf_event::ctx.
             */
            /* (8.1) 把group_leader从原有的context中remove */
            perf_remove_from_context(group_leader, false);
    
            /* (8.2) 把所有group_leader的子event从原有的context中remove */
            list_for_each_entry(sibling, &group_leader->sibling_list,
                        group_entry) {
                perf_remove_from_context(sibling, false);
                put_ctx(gctx);
            }
    
            /*
             * Wait for everybody to stop referencing the events through
             * the old lists, before installing it on new lists.
             */
            synchronize_rcu();
    
            /*
             * Install the group siblings before the group leader.
             *
             * Because a group leader will try and install the entire group
             * (through the sibling list, which is still in-tact), we can
             * end up with siblings installed in the wrong context.
             *
             * By installing siblings first we NO-OP because they're not
             * reachable through the group lists.
             */
            /* (8.3) 把所有group_leader的子event安装到新的context中 */
            list_for_each_entry(sibling, &group_leader->sibling_list,
                        group_entry) {
                perf_event__state_init(sibling);
                perf_install_in_context(ctx, sibling, sibling->cpu);
                get_ctx(ctx);
            }
    
            /*
             * Removing from the context ends up with disabled
             * event. What we want here is event in the initial
             * startup state, ready to be add into new context.
             */
            /* (8.4) 把group_leader安装到新的context中 */
            perf_event__state_init(group_leader);
            perf_install_in_context(ctx, group_leader, group_leader->cpu);
            get_ctx(ctx);
    
            /*
             * Now that all events are installed in @ctx, nothing
             * references @gctx anymore, so drop the last reference we have
             * on it.
             */
            put_ctx(gctx);
        }
    
        /*
         * Precalculate sample_data sizes; do while holding ctx::mutex such
         * that we're serialized against further additions and before
         * perf_install_in_context() which is the point the event is active and
         * can use these values.
         */
        /* (9.1) 重新计算:event->read_size、event->header_size、event->id_header_size */
        perf_event__header_size(event);
        perf_event__id_header_size(event);
    
        /* (10) 把event安装到context当中 */
        perf_install_in_context(ctx, event, event->cpu);
        perf_unpin_context(ctx);
    
        if (move_group)
            mutex_unlock(&gctx->mutex);
        mutex_unlock(&ctx->mutex);
        if (group_leader)
            mutex_unlock(&group_leader->group_leader_mutex);
    
        if (task) {
            mutex_unlock(&task->signal->cred_guard_mutex);
            put_task_struct(task);
        }
    
        put_online_cpus();
    
        event->owner = current;
    
        /* (9.2) 把当前进程创建的所有event,加入到current->perf_event_list链表中 */
        mutex_lock(&current->perf_event_mutex);
        list_add_tail(&event->owner_entry, &current->perf_event_list);
        mutex_unlock(&current->perf_event_mutex);
    
        /*
         * Drop the reference on the group_event after placing the
         * new event on the sibling_list. This ensures destruction
         * of the group leader will find the pointer to itself in
         * perf_group_detach().
         */
        fdput(group);
        /* (9.3) 把fd和file进行链接 */
        fd_install(event_fd, event_file);
        return event_fd;
    
    err_locked:
        if (move_group)
            mutex_unlock(&gctx->mutex);
        mutex_unlock(&ctx->mutex);
    /* err_file: */
        fput(event_file);
    err_context:
        perf_unpin_context(ctx);
        put_ctx(ctx);
    err_alloc:
        /*
         * If event_file is set, the fput() above will have called ->release()
         * and that will take care of freeing the event.
         */
        if (!event_file)
            free_event(event);
    err_cred:
        if (task)
            mutex_unlock(&task->signal->cred_guard_mutex);
    err_cpus:
        put_online_cpus();
    err_task:
        if (task)
            put_task_struct(task);
    err_group_fd:
        if (group_leader)
            mutex_unlock(&group_leader->group_leader_mutex);
        fdput(group);
    err_fd:
        put_unused_fd(event_fd);
        return err;
    }
    
    |→
    
    static struct perf_event *
    perf_event_alloc(struct perf_event_attr *attr, int cpu,
             struct task_struct *task,
             struct perf_event *group_leader,
             struct perf_event *parent_event,
             perf_overflow_handler_t overflow_handler,
             void *context, int cgroup_fd)
    {
        struct pmu *pmu;
        struct perf_event *event;
        struct hw_perf_event *hwc;
        long err = -EINVAL;
    
        if ((unsigned)cpu >= nr_cpu_ids) {
            if (!task || cpu != -1)
                return ERR_PTR(-EINVAL);
        }
    
        /* (2.1) 分配perf_event空间 */
        event = kzalloc(sizeof(*event), GFP_KERNEL);
        if (!event)
            return ERR_PTR(-ENOMEM);
    
        /*
         * Single events are their own group leaders, with an
         * empty sibling list:
         *
        /* (2.2) 如果group_fd == -1,那么group_leader = self */
        if (!group_leader)
            group_leader = event;
    
        mutex_init(&event->group_leader_mutex);
        mutex_init(&event->child_mutex);
        INIT_LIST_HEAD(&event->child_list);
    
        INIT_LIST_HEAD(&event->group_entry);
        INIT_LIST_HEAD(&event->event_entry);
        INIT_LIST_HEAD(&event->sibling_list);
        INIT_LIST_HEAD(&event->rb_entry);
        INIT_LIST_HEAD(&event->active_entry);
        INIT_LIST_HEAD(&event->drv_configs);
        INIT_HLIST_NODE(&event->hlist_entry);
    
    
        init_waitqueue_head(&event->waitq);
        init_irq_work(&event->pending, perf_pending_event);
    
        mutex_init(&event->mmap_mutex);
    
        atomic_long_set(&event->refcount, 1);
        event->cpu      = cpu;
        event->attr     = *attr;
        event->group_leader = group_leader;
        event->pmu      = NULL;
        event->oncpu        = -1;
    
        event->parent       = parent_event;
    
        event->ns       = get_pid_ns(task_active_pid_ns(current));
        event->id       = atomic64_inc_return(&perf_event_id);
    
        event->state        = PERF_EVENT_STATE_INACTIVE;
    
        /* (2.3) 如果task != null */
        if (task) {
            event->attach_state = PERF_ATTACH_TASK;
            /*
             * XXX pmu::event_init needs to know what task to account to
             * and we cannot use the ctx information because we need the
             * pmu before we get a ctx.
             */
            event->hw.target = task;
        }
    
        event->clock = &local_clock;
        if (parent_event)
            event->clock = parent_event->clock;
    
        if (!overflow_handler && parent_event) {
            overflow_handler = parent_event->overflow_handler;
            context = parent_event->overflow_handler_context;
        }
    
        event->overflow_handler = overflow_handler;
        event->overflow_handler_context = context;
    
        /* (2.4) 根据attr.disabled的值来设置event的初始化值:
            event->state =  PERF_EVENT_STATE_OFF/PERF_EVENT_STATE_INACTIVE
         */
        perf_event__state_init(event);
    
        pmu = NULL;
    
        /* (2.5) 根据event的attr->freq和attr->sample_period/sample_freq来初始化event->hw:
            hwc->sample_period
            hwc->last_period
            hwc->period_left
         */
        hwc = &event->hw;
        hwc->sample_period = attr->sample_period;
        if (attr->freq && attr->sample_freq)
            hwc->sample_period = 1;
        hwc->last_period = hwc->sample_period;
    
        local64_set(&hwc->period_left, hwc->sample_period);
    
        /*
         * we currently do not support PERF_FORMAT_GROUP on inherited events
         */
        /* (2.6) inherit时不支持PERF_FORMAT_GROUP */
        if (attr->inherit && (attr->read_format & PERF_FORMAT_GROUP))
            goto err_ns;
    
        /* (2.7) 如果!(attr.sample_type & PERF_SAMPLE_BRANCH_STACK):
            则attr.branch_sample_type = 0
         */
        if (!has_branch_stack(event))
            event->attr.branch_sample_type = 0;
    
        /* (2.8) 如果(flags & PERF_FLAG_PID_CGROUP):
            将当前event加入cgroup 
         */
        if (cgroup_fd != -1) {
            err = perf_cgroup_connect(cgroup_fd, event, attr, group_leader);
            if (err)
                goto err_ns;
        }
    
        /* (2.9) 至关重要的一步:
            根据attr.type在pmus链表中找到对应的pmu,
            并且调用pmu->event_init(event)来初始化event
         */
        pmu = perf_init_event(event);
        if (!pmu)
            goto err_ns;
        else if (IS_ERR(pmu)) {
            err = PTR_ERR(pmu);
            goto err_ns;
        }
    
        /* (2.10) exclusive类型pmu的event的处理 */
        err = exclusive_event_init(event);
        if (err)
            goto err_pmu;
    
        if (!event->parent) {
            if (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN) {
                err = get_callchain_buffers();
                if (err)
                    goto err_per_task;
            }
        }
    
        /* symmetric to unaccount_event() in _free_event() */
        /* (2.11) 关于event操作的一些统计 */
        account_event(event);
    
        return event;
    
    err_per_task:
        exclusive_event_destroy(event);
    
    err_pmu:
        if (event->destroy)
            event->destroy(event);
        module_put(pmu->module);
    err_ns:
        if (is_cgroup_event(event))
            perf_detach_cgroup(event);
        if (event->ns)
            put_pid_ns(event->ns);
        kfree(event);
    
        return ERR_PTR(err);
    }
    
    |→
    
    static struct perf_event_context *
    find_get_context(struct pmu *pmu, struct task_struct *task,
            struct perf_event *event)
    {
        struct perf_event_context *ctx, *clone_ctx = NULL;
        struct perf_cpu_context *cpuctx;
        void *task_ctx_data = NULL;
        unsigned long flags;
        int ctxn, err;
        int cpu = event->cpu;
    
        /* (4.1) 如果task=null即pid=-1,获取cpu维度的context */
        if (!task) {
            /* Must be root to operate on a CPU event: */
            /* (4.1.1) 权限判断 */
            if (event->owner != EVENT_OWNER_KERNEL && perf_paranoid_cpu() &&
                !capable(CAP_SYS_ADMIN))
                return ERR_PTR(-EACCES);
    
            /*
             * We could be clever and allow to attach a event to an
             * offline CPU and activate it when the CPU comes up, but
             * that's for later.
             */
            /* (4.1.2) attr指定的cpu是否online */
            if (!cpu_online(cpu))
                return ERR_PTR(-ENODEV);
    
            /* (4.1.3) 根据cpu获取到对应pmu的cpu维度context:per_cpu_ptr(pmu->pmu_cpu_context, cpu)->ctx */
            cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu);
            ctx = &cpuctx->ctx;
            get_ctx(ctx);
            ++ctx->pin_count;
    
            return ctx;
        }
    
        /* (4.2) 如果task!=null即pid>=0,获取task维度的context */
    
        err = -EINVAL;
        ctxn = pmu->task_ctx_nr;
        if (ctxn < 0)
            goto errout;
    
        /* (4.2.1) 部分架构context需要分配ctx->task_ctx_data */
        if (event->attach_state & PERF_ATTACH_TASK_DATA) {
            task_ctx_data = kzalloc(pmu->task_ctx_size, GFP_KERNEL);
            if (!task_ctx_data) {
                err = -ENOMEM;
                goto errout;
            }
        }
    
    retry:
        /* (4.2.2) 获取task维度的context:task->perf_event_ctxp[pmu->task_ctx_nr]
            如果此前无人创建过此context,则分配空间创建
         */
        ctx = perf_lock_task_context(task, ctxn, &flags);
        /* (4.2.2.1) task此context已经创建,则使用现成的 */
        if (ctx) {
            clone_ctx = unclone_ctx(ctx);
            ++ctx->pin_count;
    
            if (task_ctx_data && !ctx->task_ctx_data) {
                ctx->task_ctx_data = task_ctx_data;
                task_ctx_data = NULL;
            }
            raw_spin_unlock_irqrestore(&ctx->lock, flags);
    
            if (clone_ctx)
                put_ctx(clone_ctx);
        /* (4.2.2.2) 否则,重新创建task->perf_event_ctxp[pmu->task_ctx_nr] */
        } else {
            ctx = alloc_perf_context(pmu, task);
            err = -ENOMEM;
            if (!ctx)
                goto errout;
    
            if (task_ctx_data) {
                ctx->task_ctx_data = task_ctx_data;
                task_ctx_data = NULL;
            }
    
            err = 0;
            mutex_lock(&task->perf_event_mutex);
            /*
             * If it has already passed perf_event_exit_task().
             * we must see PF_EXITING, it takes this mutex too.
             */
            if (task->flags & PF_EXITING)
                err = -ESRCH;
            else if (task->perf_event_ctxp[ctxn])
                err = -EAGAIN;
            else {
                get_ctx(ctx);
                ++ctx->pin_count;
                rcu_assign_pointer(task->perf_event_ctxp[ctxn], ctx);
            }
            mutex_unlock(&task->perf_event_mutex);
    
            if (unlikely(err)) {
                put_ctx(ctx);
    
                if (err == -EAGAIN)
                    goto retry;
                goto errout;
            }
        }
    
        kfree(task_ctx_data);
        return ctx;
    
    errout:
        kfree(task_ctx_data);
        return ERR_PTR(err);
    }
    
    |→
    
    static void
    perf_install_in_context(struct perf_event_context *ctx,
                struct perf_event *event,
                int cpu)
    {
        struct task_struct *task = ctx->task;
    
        lockdep_assert_held(&ctx->mutex);
    
        /* (10.1) context赋值给event->ctx */
        event->ctx = ctx;
        if (event->cpu != -1)
            event->cpu = cpu;
    
        /* (10.2) 如果是cpu维度的context,
            使用cpu同步机制来调用指定的cpu上运行__perf_install_in_context() 
            绑定context、event
         */
        if (!task) {
            /*
             * Per cpu events are installed via an smp call and
             * the install is always successful.
             */
            cpu_function_call(cpu, __perf_install_in_context, event);
            return;
        }
    
    retry:
        /* (10.3) 如果是task维度的context,且task当前正在running
            使用cpu同步机制调用指定的task的运行cpu(即task_cpu(p))上运行__perf_install_in_context()
            绑定context、event
         */
        if (!task_function_call(task, __perf_install_in_context, event))
            return;
    
        raw_spin_lock_irq(&ctx->lock);
        /*
         * If we failed to find a running task, but find the context active now
         * that we've acquired the ctx->lock, retry.
         */
        if (ctx->is_active) {
            raw_spin_unlock_irq(&ctx->lock);
            /*
             * Reload the task pointer, it might have been changed by
             * a concurrent perf_event_context_sched_out().
             */
            task = ctx->task;
            goto retry;
        }
    
        /*
         * Since the task isn't running, its safe to add the event, us holding
         * the ctx->lock ensures the task won't get scheduled in.
         */
        /* (10.4) 如果是task维度的context,但是task当前不在runnning
            可以安全的绑定event和context
         */
        add_event_to_ctx(event, ctx);
        raw_spin_unlock_irq(&ctx->lock);
    }
    
    ||→
    
    static void add_event_to_ctx(struct perf_event *event,
                       struct perf_event_context *ctx)
    {
        u64 tstamp = perf_event_time(event);
    
        /* (10.4.1) 将event加入到context的相关链表 */
        list_add_event(event, ctx);
    
        /* (10.4.2) 将event加入到group_leader的链表 */
        perf_group_attach(event);
        event->tstamp_enabled = tstamp;
        event->tstamp_running = tstamp;
        event->tstamp_stopped = tstamp;
    }
    
    |||→
    
    static void
    list_add_event(struct perf_event *event, struct perf_event_context *ctx)
    {
        WARN_ON_ONCE(event->attach_state & PERF_ATTACH_CONTEXT);
    
        /* (10.4.1.1) 设置event->attach_state的PERF_ATTACH_CONTEXT */
        event->attach_state |= PERF_ATTACH_CONTEXT;
    
        /*
         * If we're a stand alone event or group leader, we go to the context
         * list, group events are kept attached to the group so that
         * perf_group_detach can, at all times, locate all siblings.
         */
        /* (10.4.1.2) 如果event是group_leader 
            则将其event->group_entry加入到顶级group链表:ctx->flexible_groups/pinned_groups
            ctx->flexible_groups/pinned_groups链表只链接group_leader的event
         */
        if (event->group_leader == event) {
            struct list_head *list;
    
            if (is_software_event(event))
                event->group_flags |= PERF_GROUP_SOFTWARE;
    
            list = ctx_group_list(event, ctx);
            list_add_tail(&event->group_entry, list);
        }
    
        if (is_cgroup_event(event))
            ctx->nr_cgroups++;
    
        /* (10.4.1.3) 将event->event_entry加入到链表:ctx->event_list 
            ctx->event_list链表链接context下所有的event
         */
        list_add_rcu(&event->event_entry, &ctx->event_list);
        ctx->nr_events++;
        if (event->attr.inherit_stat)
            ctx->nr_stat++;
    
        ctx->generation++;
    }
    
    |||→
    
    static void perf_group_attach(struct perf_event *event)
    {
        struct perf_event *group_leader = event->group_leader, *pos;
    
        /*
         * We can have double attach due to group movement in perf_event_open.
         */
        /* (10.4.2.1) 设置event->attach_state的PERF_ATTACH_GROUP */
        if (event->attach_state & PERF_ATTACH_GROUP)
            return;
    
        event->attach_state |= PERF_ATTACH_GROUP;
    
        /* (10.4.2.2) 如果event本身就是group_leader,不需要继续操作 */
        if (group_leader == event)
            return;
    
        WARN_ON_ONCE(group_leader->ctx != event->ctx);
    
        /* (10.4.2.3) move_group的处理? */
        if (group_leader->group_flags & PERF_GROUP_SOFTWARE &&
                !is_software_event(event))
            group_leader->group_flags &= ~PERF_GROUP_SOFTWARE;
    
        /* (10.4.2.4) 把event->group_entry加入到group_leader->sibling_list链表 */
        list_add_tail(&event->group_entry, &group_leader->sibling_list);
        group_leader->nr_siblings++;
    
        /* (10.4.2.5) 重新计算group_leader的event->read_size、event->header_size */
        perf_event__header_size(group_leader);
    
        /* (10.4.2.6) 重新计算group_leader所有子event的event->read_size、event->header_size */
        list_for_each_entry(pos, &group_leader->sibling_list, group_entry)
            perf_event__header_size(pos);
    }
    

    经过perf_event_open()调用以后返回perf_event对应的fd,后续的文件操作对应perf_fops:

    static const struct file_operations perf_fops = {
        .llseek         = no_llseek,
        .release        = perf_release,
        .read           = perf_read,
        .poll           = perf_poll,
        .unlocked_ioctl     = perf_ioctl,
        .compat_ioctl       = perf_compat_ioctl,
        .mmap           = perf_mmap,
        .fasync         = perf_fasync,
    };

    后续会对其重点的函数perf_read()、perf_ioctl()、perf_mmap()一一进行解析。

    2.1、inherit

    进程通过task contex(task->perf_event_ctxp[ctxn])挂载了很多面向task的perf_event,如果event支持inherit属性,当进程创建子进程时需要给子进程创建task context和继承的event。

    copy_process() -> perf_event_init_task() -> perf_event_init_context() -> inherit_task_group() -> inherit_group() -> inherit_event():

    int perf_event_init_task(struct task_struct *child)
    {
        int ctxn, ret;
    
        /* (1) 初始化child task的perf_event相关结构 */
        memset(child->perf_event_ctxp, 0, sizeof(child->perf_event_ctxp));
        mutex_init(&child->perf_event_mutex);
        INIT_LIST_HEAD(&child->perf_event_list);
    
        /* (2) 进程通过task contex(task->perf_event_ctxp[ctxn])挂载了很多面向task的perf_event,
            如果event支持inherit属性,当进程创建子进程时需要给子进程创建task context和继承的event
         */
        for_each_task_context_nr(ctxn) {
            ret = perf_event_init_context(child, ctxn);
            if (ret) {
                perf_event_free_task(child);
                return ret;
            }
        }
    
        return 0;
    }
    
    |→
    
    static int perf_event_init_context(struct task_struct *child, int ctxn)
    {
        struct perf_event_context *child_ctx, *parent_ctx;
        struct perf_event_context *cloned_ctx;
        struct perf_event *event;
        struct task_struct *parent = current;
        int inherited_all = 1;
        unsigned long flags;
        int ret = 0;
    
        /* (2.1) 父进程即为当前进程(current) */
        if (likely(!parent->perf_event_ctxp[ctxn]))
            return 0;
    
        /*
         * If the parent's context is a clone, pin it so it won't get
         * swapped under us.
         */
        parent_ctx = perf_pin_task_context(parent, ctxn);
        if (!parent_ctx)
            return 0;
    
        /*
         * No need to check if parent_ctx != NULL here; since we saw
         * it non-NULL earlier, the only reason for it to become NULL
         * is if we exit, and since we're currently in the middle of
         * a fork we can't be exiting at the same time.
         */
    
        /*
         * Lock the parent list. No need to lock the child - not PID
         * hashed yet and not running, so nobody can access it.
         */
        mutex_lock(&parent_ctx->mutex);
    
        /*
         * We dont have to disable NMIs - we are only looking at
         * the list, not manipulating it:
         */
        /* (2.2) 遍历父进程context上的高优先级group leader event链表:parent_ctx->pinned_groups,
            在子进程上复制需要inherit的event
         */
        list_for_each_entry(event, &parent_ctx->pinned_groups, group_entry) {
            ret = inherit_task_group(event, parent, parent_ctx,
                         child, ctxn, &inherited_all);
            if (ret)
                break;
        }
    
        /*
         * We can't hold ctx->lock when iterating the ->flexible_group list due
         * to allocations, but we need to prevent rotation because
         * rotate_ctx() will change the list from interrupt context.
         */
        raw_spin_lock_irqsave(&parent_ctx->lock, flags);
        parent_ctx->rotate_disable = 1;
        raw_spin_unlock_irqrestore(&parent_ctx->lock, flags);
    
        /* (2.2) 遍历父进程context上的低优先级group leader event链表:parent_ctx->flexible_groups,
            在子进程上复制需要inherit的event
         */
        list_for_each_entry(event, &parent_ctx->flexible_groups, group_entry) {
            ret = inherit_task_group(event, parent, parent_ctx,
                         child, ctxn, &inherited_all);
            if (ret)
                break;
        }
    
        raw_spin_lock_irqsave(&parent_ctx->lock, flags);
        parent_ctx->rotate_disable = 0;
    
        child_ctx = child->perf_event_ctxp[ctxn];
    
        /* (2.3) 如果inherited_all>0,表示父进程挂载的所有event都是inherit属性,都被子进程复制继承, 
            那么给子进程的task context->parent_ctx赋值为父进程的context
         */
        if (child_ctx && inherited_all) {
            /*
             * Mark the child context as a clone of the parent
             * context, or of whatever the parent is a clone of.
             *
             * Note that if the parent is a clone, the holding of
             * parent_ctx->lock avoids it from being uncloned.
             */
            cloned_ctx = parent_ctx->parent_ctx;
            if (cloned_ctx) {
                child_ctx->parent_ctx = cloned_ctx;
                child_ctx->parent_gen = parent_ctx->parent_gen;
            } else {
                child_ctx->parent_ctx = parent_ctx;
                child_ctx->parent_gen = parent_ctx->generation;
            }
            get_ctx(child_ctx->parent_ctx);
        }
    
        raw_spin_unlock_irqrestore(&parent_ctx->lock, flags);
        mutex_unlock(&parent_ctx->mutex);
    
        perf_unpin_context(parent_ctx);
        put_ctx(parent_ctx);
    
        return ret;
    }
    
    ||→
    
    static int
    inherit_task_group(struct perf_event *event, struct task_struct *parent,
               struct perf_event_context *parent_ctx,
               struct task_struct *child, int ctxn,
               int *inherited_all)
    {
        int ret;
        struct perf_event_context *child_ctx;
    
        /* (2.1.1) 如果event不支持inherit,直接返回 */
        if (!event->attr.inherit) {
            *inherited_all = 0;
            return 0;
        }
    
        /* (2.1.2) 如果子进程的context为空,分配创建 */
        child_ctx = child->perf_event_ctxp[ctxn];
        if (!child_ctx) {
            /*
             * This is executed from the parent task context, so
             * inherit events that have been marked for cloning.
             * First allocate and initialize a context for the
             * child.
             */
    
            child_ctx = alloc_perf_context(parent_ctx->pmu, child);
            if (!child_ctx)
                return -ENOMEM;
    
            child->perf_event_ctxp[ctxn] = child_ctx;
        }
    
        /* (2.1.3) 子进程对父进程的event进行复制继承 */
        ret = inherit_group(event, parent, parent_ctx,
                    child, child_ctx);
    
        if (ret)
            *inherited_all = 0;
    
        return ret;
    }
    
    |||→
    
    static int inherit_group(struct perf_event *parent_event,
              struct task_struct *parent,
              struct perf_event_context *parent_ctx,
              struct task_struct *child,
              struct perf_event_context *child_ctx)
    {
        struct perf_event *leader;
        struct perf_event *sub;
        struct perf_event *child_ctr;
    
        /* (2.1.3.1) 对group_leader event进行复制继承, 
            新建group leader的继承者是新的group leader
         */
        leader = inherit_event(parent_event, parent, parent_ctx,
                     child, NULL, child_ctx);
        if (IS_ERR(leader))
            return PTR_ERR(leader);
    
        /* (2.1.3.2) 对group_leader下面的子event进行复制继承: 
            如果父进程group leader下面有树成员,他会把这棵树给子进程也复制一份,
            ---oops:这里有个疑问,如果父进程group成员不是group leader所在的父进程,但是inherit会给子进程复制一个event,这样合理吗?
         */
        list_for_each_entry(sub, &parent_event->sibling_list, group_entry) {
            child_ctr = inherit_event(sub, parent, parent_ctx,
                            child, leader, child_ctx);
            if (IS_ERR(child_ctr))
                return PTR_ERR(child_ctr);
        }
        return 0;
    }
    
    ||||→
    
    static struct perf_event *
    inherit_event(struct perf_event *parent_event,
              struct task_struct *parent,
              struct perf_event_context *parent_ctx,
              struct task_struct *child,
              struct perf_event *group_leader,
              struct perf_event_context *child_ctx)
    {
        enum perf_event_active_state parent_state = parent_event->state;
        struct perf_event *child_event;
        unsigned long flags;
    
        /*
         * Instead of creating recursive hierarchies of events,
         * we link inherited events back to the original parent,
         * which has a filp for sure, which we use as the reference
         * count:
         */
        /* (2.1.3.1.1) 获得子进程新建event的parent event,
            如果是多级子进程,把所有子进程的event都挂在原始parent下面,而不是一级一级的挂载
         */
        if (parent_event->parent)
            parent_event = parent_event->parent;
    
        /* (2.1.3.2) 对group_leader event进行复制,重新分配初始化: 
            和父event创建不同,这指定了子event的parent:event->parent = parent_event;
            随后子event也会被加入到父event的parent_event->child_list链表中
         */
        child_event = perf_event_alloc(&parent_event->attr,
                           parent_event->cpu,
                           child,
                           group_leader, parent_event,
                           NULL, NULL, -1);
        if (IS_ERR(child_event))
            return child_event;
    
        if (is_orphaned_event(parent_event) ||
            !atomic_long_inc_not_zero(&parent_event->refcount)) {
            free_event(child_event);
            return NULL;
        }
    
        get_ctx(child_ctx);
    
        /*
         * Make the child state follow the state of the parent event,
         * not its attr.disabled bit.  We hold the parent's mutex,
         * so we won't race with perf_event_{en, dis}able_family.
         */
        /* (2.1.3.3) 如果父event的状态已经enable,子event的状态也为enable */
        if (parent_state >= PERF_EVENT_STATE_INACTIVE)
            child_event->state = PERF_EVENT_STATE_INACTIVE;
        else
            child_event->state = PERF_EVENT_STATE_OFF;
    
        if (parent_event->attr.freq) {
            u64 sample_period = parent_event->hw.sample_period;
            struct hw_perf_event *hwc = &child_event->hw;
    
            hwc->sample_period = sample_period;
            hwc->last_period   = sample_period;
    
            local64_set(&hwc->period_left, sample_period);
        }
    
        child_event->ctx = child_ctx;
        child_event->overflow_handler = parent_event->overflow_handler;
        child_event->overflow_handler_context
            = parent_event->overflow_handler_context;
    
        /*
         * Precalculate sample_data sizes
         */
        perf_event__header_size(child_event);
        perf_event__id_header_size(child_event);
    
        /*
         * Link it up in the child's context:
         */
        /* (2.1.3.4) 将新的event加入到子进程的context中 */
        raw_spin_lock_irqsave(&child_ctx->lock, flags);
        add_event_to_ctx(child_event, child_ctx);
        raw_spin_unlock_irqrestore(&child_ctx->lock, flags);
    
        /*
         * Link this into the parent event's child list
         */
        /* (2.1.3.5) 将子event加入到父event的->child_list链表中 */
        WARN_ON_ONCE(parent_event->ctx->parent_ctx);
        mutex_lock(&parent_event->child_mutex);
        list_add_tail(&child_event->child_list, &parent_event->child_list);
        mutex_unlock(&parent_event->child_mutex);
    
        return child_event;
    }
    

    2.2、perf task sched

    task context上链接的perf_event需要跟随task进程调度一起动态启动停止,在task得到调度时相关perf_event开始工作,在task被其他任务调度出去时相关perf_event停止工作。

    为了支持这种行为,在task switch时调用perf的回调函数:perf_event_task_sched_out()/perf_event_task_sched_in()

    context_switch() -> prepare_task_switch() -> perf_event_task_sched_out():

    static inline void perf_event_task_sched_out(struct task_struct *prev,
                             struct task_struct *next)
    {
        /* (1) "software" pmu 子类型为“context-switches”的数据采集点 */
        perf_sw_event_sched(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 0);
    
        /* (2) 随着taskswitch,和task关联的相关event需要停止工作:即sched_out 
            perf_sched_events.key:是进行perf_sched_in/out的开关,在event分配(perf_event_alloc() -> account_event())和释放(_free_event() -> unaccount_event())时操作
         */
        if (static_key_false(&perf_sched_events.key))
            __perf_event_task_sched_out(prev, next);
    }
    
    |→
    
    void __perf_event_task_sched_out(struct task_struct *task,
                     struct task_struct *next)
    {
        int ctxn;
    
        /* (2.1) 回调函数pmu->sched_task的调用点 */
        if (__this_cpu_read(perf_sched_cb_usages))
            perf_pmu_sched_task(task, next, false);
    
        /* (2.2) */
        if (atomic_read(&nr_switch_events))
            perf_event_switch(task, next, false);
    
        /* (2.3) 遍历task->[perf_event_ctxp]中的context, 
            逐个关闭context链接中的每个perf_event
         */
        for_each_task_context_nr(ctxn)
            perf_event_context_sched_out(task, ctxn, next);
    
        /*
         * if cgroup events exist on this CPU, then we need
         * to check if we have to switch out PMU state.
         * cgroup event are system-wide mode only
         */
        /* (2.4) task cgroup相关sched_out */
        if (atomic_read(this_cpu_ptr(&perf_cgroup_events)))
            perf_cgroup_sched_out(task, next);
    }
    
    ||→
    
    static void perf_event_context_sched_out(struct task_struct *task, int ctxn,
                         struct task_struct *next)
    {
        struct perf_event_context *ctx = task->perf_event_ctxp[ctxn];
        struct perf_event_context *next_ctx;
        struct perf_event_context *parent, *next_parent;
        struct perf_cpu_context *cpuctx;
        int do_switch = 1;
    
        if (likely(!ctx))
            return;
    
        cpuctx = __get_cpu_context(ctx);
        if (!cpuctx->task_ctx)
            return;
    
        rcu_read_lock();
        next_ctx = next->perf_event_ctxp[ctxn];
        if (!next_ctx)
            goto unlock;
    
        parent = rcu_dereference(ctx->parent_ctx);
        next_parent = rcu_dereference(next_ctx->parent_ctx);
    
        /* If neither context have a parent context; they cannot be clones. */
        if (!parent && !next_parent)
            goto unlock;
    
        /* (2.3.1) 如果curr task和next task的context刚好是parent关系,我们使用快捷路径来切换任务, 
            context的parent关系,只有在创建子进程,且所有的父进程event都有inherit属性被子进程全部复制继承,
         */
        if (next_parent == ctx || next_ctx == parent || next_parent == parent) {
            /*
             * Looks like the two contexts are clones, so we might be
             * able to optimize the context switch.  We lock both
             * contexts and check that they are clones under the
             * lock (including re-checking that neither has been
             * uncloned in the meantime).  It doesn't matter which
             * order we take the locks because no other cpu could
             * be trying to lock both of these tasks.
             */
            raw_spin_lock(&ctx->lock);
            raw_spin_lock_nested(&next_ctx->lock, SINGLE_DEPTH_NESTING);
            if (context_equiv(ctx, next_ctx)) {
                /*
                 * XXX do we need a memory barrier of sorts
                 * wrt to rcu_dereference() of perf_event_ctxp
                 */
                task->perf_event_ctxp[ctxn] = next_ctx;
                next->perf_event_ctxp[ctxn] = ctx;
                ctx->task = next;
                next_ctx->task = task;
    
                swap(ctx->task_ctx_data, next_ctx->task_ctx_data);
    
                do_switch = 0;
    
                perf_event_sync_stat(ctx, next_ctx);
            }
            raw_spin_unlock(&next_ctx->lock);
            raw_spin_unlock(&ctx->lock);
        }
    unlock:
        rcu_read_unlock();
    
        /* (2.3.2) 慢速路径的task context切换:
         */
        if (do_switch) {
            raw_spin_lock(&ctx->lock);
            ctx_sched_out(ctx, cpuctx, EVENT_ALL);
            cpuctx->task_ctx = NULL;
            raw_spin_unlock(&ctx->lock);
        }
    }
    
    |||→
    
    static void ctx_sched_out(struct perf_event_context *ctx,
                  struct perf_cpu_context *cpuctx,
                  enum event_type_t event_type)
    {
        struct perf_event *event;
        int is_active = ctx->is_active;
    
        ctx->is_active &= ~event_type;
        if (likely(!ctx->nr_events))
            return;
    
        update_context_time(ctx);
        update_cgrp_time_from_cpuctx(cpuctx);
        if (!ctx->nr_active)
            return;
    
        perf_pmu_disable(ctx->pmu);
        /* (2.3.2.1) 遍历context的高优先级event链表->pinned_groups:
            任务已经被切出,关联的所有event需要停工
         */
        if ((is_active & EVENT_PINNED) && (event_type & EVENT_PINNED)) {
            list_for_each_entry(event, &ctx->pinned_groups, group_entry)
                group_sched_out(event, cpuctx, ctx);
        }
    
        /* (2.3.2.2) 遍历context的低优先级event链表->flexible_groups:
            任务已经被切出,关联的所有event需要停工
         */
        if ((is_active & EVENT_FLEXIBLE) && (event_type & EVENT_FLEXIBLE)) {
            list_for_each_entry(event, &ctx->flexible_groups, group_entry)
                group_sched_out(event, cpuctx, ctx);
        }
        perf_pmu_enable(ctx->pmu);
    }
    
    ||||→
    
    static void
    group_sched_out(struct perf_event *group_event,
            struct perf_cpu_context *cpuctx,
            struct perf_event_context *ctx)
    {
        struct perf_event *event;
        int state = group_event->state;
    
        /* (2.3.2.2.1) 对group leader event进行停工 */
        event_sched_out(group_event, cpuctx, ctx);
    
        /*
         * Schedule out siblings (if any):
         */
        /* (2.3.2.2.2) 对group leader下的子event进行停工 */
        list_for_each_entry(event, &group_event->sibling_list, group_entry)
            event_sched_out(event, cpuctx, ctx);
    
        if (state == PERF_EVENT_STATE_ACTIVE && group_event->attr.exclusive)
            cpuctx->exclusive = 0;
    }
    
    |||||→
    
    static void
    event_sched_out(struct perf_event *event,
              struct perf_cpu_context *cpuctx,
              struct perf_event_context *ctx)
    {
        u64 tstamp = perf_event_time(event);
        u64 delta;
    
        WARN_ON_ONCE(event->ctx != ctx);
        lockdep_assert_held(&ctx->lock);
    
        /*
         * An event which could not be activated because of
         * filter mismatch still needs to have its timings
         * maintained, otherwise bogus information is return
         * via read() for time_enabled, time_running:
         */
        if (event->state == PERF_EVENT_STATE_INACTIVE
            && !event_filter_match(event)) {
            delta = tstamp - event->tstamp_stopped;
            event->tstamp_running += delta;
            event->tstamp_stopped = tstamp;
        }
    
        /* (2.3.2.2.1.1) 如果当前event不是开工状态(PERF_EVENT_STATE_ACTIVE)直接返回 */
        if (event->state != PERF_EVENT_STATE_ACTIVE)
            return;
    
        perf_pmu_disable(event->pmu);
    
        event->tstamp_stopped = tstamp;
    
        /* (2.3.2.2.1.2) 调用event的停工函数:pmu->del() */
        event->pmu->del(event, 0);
        event->oncpu = -1;
    
        /* (2.3.2.2.1.3) 状态设置为停工:PERF_EVENT_STATE_INACTIVE */
        event->state = PERF_EVENT_STATE_INACTIVE;
        if (event->pending_disable) {
            event->pending_disable = 0;
            event->state = PERF_EVENT_STATE_OFF;
        }
    
        if (!is_software_event(event))
            cpuctx->active_oncpu--;
        if (!--ctx->nr_active)
            perf_event_ctx_deactivate(ctx);
        if (event->attr.freq && event->attr.sample_freq)
            ctx->nr_freq--;
        if (event->attr.exclusive || !cpuctx->active_oncpu)
            cpuctx->exclusive = 0;
    
        if (is_orphaned_child(event))
            schedule_orphans_remove(ctx);
    
        perf_pmu_enable(event->pmu);
    }

    context_switch() -> finish_task_switch() -> perf_event_task_sched_in():

    static inline void perf_event_task_sched_in(struct task_struct *prev,
                            struct task_struct *task)
    {
        /* (1) 新进程上相关的event需要开工 */
        if (static_key_false(&perf_sched_events.key))
            __perf_event_task_sched_in(prev, task);
    
        if (perf_sw_migrate_enabled() && task->sched_migrated) {
            struct pt_regs *regs = this_cpu_ptr(&__perf_regs[0]);
    
            perf_fetch_caller_regs(regs);
            ___perf_sw_event(PERF_COUNT_SW_CPU_MIGRATIONS, 1, regs, 0);
            task->sched_migrated = 0;
        }
    }
    
    |→
    
    void __perf_event_task_sched_in(struct task_struct *prev,
                    struct task_struct *task)
    {
        struct perf_event_context *ctx;
        int ctxn;
    
        /* (1.1) 遍历task->[perf_event_ctxp]中的context, 
            逐个打开context链接中的每个perf_event
         */
        for_each_task_context_nr(ctxn) {
            ctx = task->perf_event_ctxp[ctxn];
            if (likely(!ctx))
                continue;
    
            perf_event_context_sched_in(ctx, task);
        }
        /*
         * if cgroup events exist on this CPU, then we need
         * to check if we have to switch in PMU state.
         * cgroup event are system-wide mode only
         */
        if (atomic_read(this_cpu_ptr(&perf_cgroup_events)))
            perf_cgroup_sched_in(prev, task);
    
        if (atomic_read(&nr_switch_events))
            perf_event_switch(task, prev, true);
    
        if (__this_cpu_read(perf_sched_cb_usages))
            perf_pmu_sched_task(prev, task, true);
    }
    
    ||→
    
    static void perf_event_context_sched_in(struct perf_event_context *ctx,
                        struct task_struct *task)
    {
        struct perf_cpu_context *cpuctx;
    
        cpuctx = __get_cpu_context(ctx);
        if (cpuctx->task_ctx == ctx)
            return;
    
        perf_ctx_lock(cpuctx, ctx);
        perf_pmu_disable(ctx->pmu);
        /*
         * We want to keep the following priority order:
         * cpu pinned (that don't need to move), task pinned,
         * cpu flexible, task flexible.
         */
        /* (1.1.1) 将新task context对应的本cpu维度的ctx->flexible_groups停工
            ctx->pinned_groups这时不会停工,就体现出优先级了?
         */
        cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
    
        /* (1.1.2) 切换本cpu的当前task context指针:cpuctx->task_ctx */
        if (ctx->nr_events)
            cpuctx->task_ctx = ctx;
    
        /* (1.1.3) 将新对应的本cpu维度的ctx->flexible_groups开工 
            将task context对应的ctx->flexible_groups、ctx->pinned_groups开工
         */
        perf_event_sched_in(cpuctx, cpuctx->task_ctx, task);
    
        perf_pmu_enable(ctx->pmu);
        perf_ctx_unlock(cpuctx, ctx);
    }
    
    |||→
    
    static void perf_event_sched_in(struct perf_cpu_context *cpuctx,
                    struct perf_event_context *ctx,
                    struct task_struct *task)
    {
        cpu_ctx_sched_in(cpuctx, EVENT_PINNED, task);
        if (ctx)
            ctx_sched_in(ctx, cpuctx, EVENT_PINNED, task);
        cpu_ctx_sched_in(cpuctx, EVENT_FLEXIBLE, task);
        if (ctx)
            ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE, task);
    }
    
    ||||→
    
    static void cpu_ctx_sched_in(struct perf_cpu_context *cpuctx,
                     enum event_type_t event_type,
                     struct task_struct *task)
    {
        struct perf_event_context *ctx = &cpuctx->ctx;
    
        ctx_sched_in(ctx, cpuctx, event_type, task);
    }
    
    |||||→
    
    static void
    ctx_sched_in(struct perf_event_context *ctx,
             struct perf_cpu_context *cpuctx,
             enum event_type_t event_type,
             struct task_struct *task)
    {
        u64 now;
        int is_active = ctx->is_active;
    
        ctx->is_active |= event_type;
        if (likely(!ctx->nr_events))
            return;
    
        now = perf_clock();
        ctx->timestamp = now;
        perf_cgroup_set_timestamp(task, ctx);
        /*
         * First go through the list and put on any pinned groups
         * in order to give them the best chance of going on.
         */
        if (!(is_active & EVENT_PINNED) && (event_type & EVENT_PINNED))
            ctx_pinned_sched_in(ctx, cpuctx);
    
        /* Then walk through the lower prio flexible groups */
        if (!(is_active & EVENT_FLEXIBLE) && (event_type & EVENT_FLEXIBLE))
            ctx_flexible_sched_in(ctx, cpuctx);
    }
    
    ||||||→
    
    static void
    ctx_pinned_sched_in(struct perf_event_context *ctx,
                struct perf_cpu_context *cpuctx)
    {
        struct perf_event *event;
    
        list_for_each_entry(event, &ctx->pinned_groups, group_entry) {
            if (event->state <= PERF_EVENT_STATE_OFF)
                continue;
            if (!event_filter_match(event))
                continue;
    
            /* may need to reset tstamp_enabled */
            if (is_cgroup_event(event))
                perf_cgroup_mark_enabled(event, ctx);
    
            if (group_can_go_on(event, cpuctx, 1))
                group_sched_in(event, cpuctx, ctx);
    
            /*
             * If this pinned group hasn't been scheduled,
             * put it in error state.
             */
            if (event->state == PERF_EVENT_STATE_INACTIVE) {
                update_group_times(event);
                event->state = PERF_EVENT_STATE_ERROR;
            }
        }
    }
    
    |||||||→
    
    static int
    group_sched_in(struct perf_event *group_event,
               struct perf_cpu_context *cpuctx,
               struct perf_event_context *ctx)
    {
        struct perf_event *event, *partial_group = NULL;
        struct pmu *pmu = ctx->pmu;
        u64 now = ctx->time;
        bool simulate = false;
    
        if (group_event->state == PERF_EVENT_STATE_OFF)
            return 0;
    
        pmu->start_txn(pmu, PERF_PMU_TXN_ADD);
    
        if (event_sched_in(group_event, cpuctx, ctx)) {
            pmu->cancel_txn(pmu);
            perf_mux_hrtimer_restart(cpuctx);
            return -EAGAIN;
        }
    
        /*
         * Schedule in siblings as one group (if any):
         */
        list_for_each_entry(event, &group_event->sibling_list, group_entry) {
            if (event_sched_in(event, cpuctx, ctx)) {
                partial_group = event;
                goto group_error;
            }
        }
    
        if (!pmu->commit_txn(pmu))
            return 0;
    
    group_error:
        /*
         * Groups can be scheduled in as one unit only, so undo any
         * partial group before returning:
         * The events up to the failed event are scheduled out normally,
         * tstamp_stopped will be updated.
         *
         * The failed events and the remaining siblings need to have
         * their timings updated as if they had gone thru event_sched_in()
         * and event_sched_out(). This is required to get consistent timings
         * across the group. This also takes care of the case where the group
         * could never be scheduled by ensuring tstamp_stopped is set to mark
         * the time the event was actually stopped, such that time delta
         * calculation in update_event_times() is correct.
         */
        list_for_each_entry(event, &group_event->sibling_list, group_entry) {
            if (event == partial_group)
                simulate = true;
    
            if (simulate) {
                event->tstamp_running += now - event->tstamp_stopped;
                event->tstamp_stopped = now;
            } else {
                event_sched_out(event, cpuctx, ctx);
            }
        }
        event_sched_out(group_event, cpuctx, ctx);
    
        pmu->cancel_txn(pmu);
    
        perf_mux_hrtimer_restart(cpuctx);
    
        return -EAGAIN;
    }
    
    ||||||||→
    
    static int
    event_sched_in(struct perf_event *event,
             struct perf_cpu_context *cpuctx,
             struct perf_event_context *ctx)
    {
        u64 tstamp = perf_event_time(event);
        int ret = 0;
    
        lockdep_assert_held(&ctx->lock);
    
        if (event->state <= PERF_EVENT_STATE_OFF)
            return 0;
    
        WRITE_ONCE(event->oncpu, smp_processor_id());
        /*
         * Order event::oncpu write to happen before the ACTIVE state
         * is visible.
         */
        /* 设置event为开工状态:PERF_EVENT_STATE_ACTIVE */
        smp_wmb();
        WRITE_ONCE(event->state, PERF_EVENT_STATE_ACTIVE);
    
        /*
         * Unthrottle events, since we scheduled we might have missed several
         * ticks already, also for a heavily scheduling task there is little
         * guarantee it'll get a tick in a timely manner.
         */
        if (unlikely(event->hw.interrupts == MAX_INTERRUPTS)) {
            perf_log_throttle(event, 1);
            event->hw.interrupts = 0;
        }
    
        /*
         * The new state must be visible before we turn it on in the hardware:
         */
        smp_wmb();
    
        perf_pmu_disable(event->pmu);
    
        perf_set_shadow_time(event, ctx, tstamp);
    
        perf_log_itrace_start(event);
    
        /* 调用event的开工函数:pmu->add() */
        if (event->pmu->add(event, PERF_EF_START)) {
            event->state = PERF_EVENT_STATE_INACTIVE;
            event->oncpu = -1;
            ret = -EAGAIN;
            goto out;
        }
    
        event->tstamp_running += tstamp - event->tstamp_stopped;
    
        if (!is_software_event(event))
            cpuctx->active_oncpu++;
        if (!ctx->nr_active++)
            perf_event_ctx_activate(ctx);
        if (event->attr.freq && event->attr.sample_freq)
            ctx->nr_freq++;
    
        if (event->attr.exclusive)
            cpuctx->exclusive = 1;
    
        if (is_orphaned_child(event))
            schedule_orphans_remove(ctx);
    
    out:
        perf_pmu_enable(event->pmu);
    
        return ret;
    }

    如果是pid>0,cpu!=-1的event,在sched_in的时候会调用event_filter_match()判断当前cpu和event绑定的cpu(event->cpu)是否一致,只有符合条件event才能被使能:

    ctx_pinned_sched_in()/ctx_flexible_sched_in() -> event_filter_match():

    static inline int
    event_filter_match(struct perf_event *event)
    {
        /* 如果没有指定cpu:event->cpu == -1 
            或者event绑定cpu和当前cpu一致:event->cpu == smp_processor_id()
            cgroup的filter条件:perf_cgroup_match(event)
            pmu的filter条件:pmu_filter_match(event)
            上述条件符合的情况下,才能event才能被使能
         */
        return (event->cpu == -1 || event->cpu == smp_processor_id())
            && perf_cgroup_match(event) && pmu_filter_match(event);
    }

    2.3、cgroup

    暂不分析

    3、perf_ioctl

    通过perf_event_open()系统调用获得perf_event对应的fd后,可以通过操作fd的ioctl命令来配置perf_event。

    static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
        struct perf_event *event = file->private_data;
        struct perf_event_context *ctx;
        long ret;
    
        ctx = perf_event_ctx_lock(event);
        ret = _perf_ioctl(event, cmd, arg);
        perf_event_ctx_unlock(event, ctx);
    
        return ret;
    }
    
    ↓
    
    static long _perf_ioctl(struct perf_event *event, unsigned int cmd, unsigned long arg)
    {
        void (*func)(struct perf_event *);
        u32 flags = arg;
    
        switch (cmd) {
        case PERF_EVENT_IOC_ENABLE:
            func = _perf_event_enable;
            break;
        case PERF_EVENT_IOC_DISABLE:
            func = _perf_event_disable;
            break;
        case PERF_EVENT_IOC_RESET:
            func = _perf_event_reset;
            break;
    
        case PERF_EVENT_IOC_REFRESH:
            return _perf_event_refresh(event, arg);
    
        case PERF_EVENT_IOC_PERIOD:
            return perf_event_period(event, (u64 __user *)arg);
    
        case PERF_EVENT_IOC_ID:
        {
            u64 id = primary_event_id(event);
    
            if (copy_to_user((void __user *)arg, &id, sizeof(id)))
                return -EFAULT;
            return 0;
        }
    
        case PERF_EVENT_IOC_SET_OUTPUT:
        {
            int ret;
            if (arg != -1) {
                struct perf_event *output_event;
                struct fd output;
                ret = perf_fget_light(arg, &output);
                if (ret)
                    return ret;
                output_event = output.file->private_data;
                ret = perf_event_set_output(event, output_event);
                fdput(output);
            } else {
                ret = perf_event_set_output(event, NULL);
            }
            return ret;
        }
    
        case PERF_EVENT_IOC_SET_FILTER:
            return perf_event_set_filter(event, (void __user *)arg);
    
        case PERF_EVENT_IOC_SET_BPF:
            return perf_event_set_bpf_prog(event, arg);
    
        case PERF_EVENT_IOC_SET_DRV_CONFIGS:
            return perf_event_drv_configs(event, (void __user *)arg);
    
        default:
            return -ENOTTY;
        }
    
        if (flags & PERF_IOC_FLAG_GROUP)
            perf_event_for_each(event, func);
        else
            perf_event_for_each_child(event, func);
    
        return 0;
    }

    3.1、_perf_event_enable

    简单分析一下enable命令。该命令的主要作用就是把处于PERF_EVENT_STATE_OFF状态的event设置成PERF_EVENT_STATE_INACTIVE,以便该event能参与到perf sched当中去。

    static void _perf_event_enable(struct perf_event *event)
    {
        struct perf_event_context *ctx = event->ctx;
        struct task_struct *task = ctx->task;
    
        if (!task) {
            /*
             * Enable the event on the cpu that it's on
             */
            cpu_function_call(event->cpu, __perf_event_enable, event);
            return;
        }
    
        raw_spin_lock_irq(&ctx->lock);
        if (event->state >= PERF_EVENT_STATE_INACTIVE)
            goto out;
    
        /*
         * If the event is in error state, clear that first.
         * That way, if we see the event in error state below, we
         * know that it has gone back into error state, as distinct
         * from the task having been scheduled away before the
         * cross-call arrived.
         */
        if (event->state == PERF_EVENT_STATE_ERROR)
            event->state = PERF_EVENT_STATE_OFF;
    
    retry:
        if (!ctx->is_active) {
            __perf_event_mark_enabled(event);
            goto out;
        }
    
        raw_spin_unlock_irq(&ctx->lock);
    
        if (!task_function_call(task, __perf_event_enable, event))
            return;
    
        raw_spin_lock_irq(&ctx->lock);
    
        /*
         * If the context is active and the event is still off,
         * we need to retry the cross-call.
         */
        if (ctx->is_active && event->state == PERF_EVENT_STATE_OFF) {
            /*
             * task could have been flipped by a concurrent
             * perf_event_context_sched_out()
             */
            task = ctx->task;
            goto retry;
        }
    
    out:
        raw_spin_unlock_irq(&ctx->lock);
    }
    
    ↓
    
    static void __perf_event_mark_enabled(struct perf_event *event)
    {
        struct perf_event *sub;
        u64 tstamp = perf_event_time(event);
    
        event->state = PERF_EVENT_STATE_INACTIVE;
        event->tstamp_enabled = tstamp - event->total_time_enabled;
        list_for_each_entry(sub, &event->sibling_list, group_entry) {
            if (sub->state >= PERF_EVENT_STATE_INACTIVE)
                sub->tstamp_enabled = tstamp - sub->total_time_enabled;
        }
    }

    另外也可以设置attr.enable_on_exec,在执行exec()新的应用时enable perf_event。

    load_elf_binary() -> setup_new_exec() -> perf_event_exec() -> perf_event_enable_on_exec() -> event_enable_on_exec():

    static int event_enable_on_exec(struct perf_event *event,
                    struct perf_event_context *ctx)
    {
        if (!event->attr.enable_on_exec)
            return 0;
    
        event->attr.enable_on_exec = 0;
        if (event->state >= PERF_EVENT_STATE_INACTIVE)
            return 0;
    
        __perf_event_mark_enabled(event);
    
        return 1;
    }

    4、perf_read

    perf_event提供两种类型的信息:counter和sample。其中counter类型的数据就是通过read()操作读取的,最后会调到perf_read()函数。

    需要重点关注一下group方式的读并累加:它会读取所有相关的perf_event的count,并且累加起来。

    static ssize_t
    perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
    {
        struct perf_event *event = file->private_data;
        struct perf_event_context *ctx;
        int ret;
    
        ctx = perf_event_ctx_lock(event);
        ret = __perf_read(event, buf, count);
        perf_event_ctx_unlock(event, ctx);
    
        return ret;
    }
    
    ↓
    
    static ssize_t
    __perf_read(struct perf_event *event, char __user *buf, size_t count)
    {
        /* (1) 读取attr.read_format */
        u64 read_format = event->attr.read_format;
        int ret;
    
        /*
         * Return end-of-file for a read on a event that is in
         * error state (i.e. because it was pinned but it couldn't be
         * scheduled on to the CPU at some point).
         */
        if (event->state == PERF_EVENT_STATE_ERROR)
            return 0;
    
        if (count < event->read_size)
            return -ENOSPC;
    
        WARN_ON_ONCE(event->ctx->parent_ctx);
        /* (2) 如果是PERF_FORMAT_GROUP,本event为group_leader, 
            读取本event的count,以及group所有子event的count
         */
        if (read_format & PERF_FORMAT_GROUP)
            ret = perf_read_group(event, read_format, buf);
        /* (3) 仅仅读取本event的count */
        else
            ret = perf_read_one(event, read_format, buf);
    
        return ret;
    }
    
    |→
    
    static int perf_read_group(struct perf_event *event,
                       u64 read_format, char __user *buf)
    {
        struct perf_event *leader = event->group_leader, *child;
        struct perf_event_context *ctx = leader->ctx;
        int ret;
        u64 *values;
    
        lockdep_assert_held(&ctx->mutex);
    
        /* (2.1) 分配存储group中所有event count需要的空间 */
        values = kzalloc(event->read_size, GFP_KERNEL);
        if (!values)
            return -ENOMEM;
    
        /* (2.2) 第一个字节保存event的个数 */
        values[0] = 1 + leader->nr_siblings;
    
        /*
         * By locking the child_mutex of the leader we effectively
         * lock the child list of all siblings.. XXX explain how.
         */
        mutex_lock(&leader->child_mutex);
    
        /* (2.4) 遍历本group_leader的event以及所有子event,读取并累加count */
        ret = __perf_read_group_add(leader, read_format, values);
        if (ret)
            goto unlock;
    
        /* (2.5) 遍历所有子进程inherit的group leader,读取并累加count */
        list_for_each_entry(child, &leader->child_list, child_list) {
            ret = __perf_read_group_add(child, read_format, values);
            if (ret)
                goto unlock;
        }
    
        mutex_unlock(&leader->child_mutex);
    
        /* (2.6) 拷贝count内容到用户态 */
        ret = event->read_size;
        if (copy_to_user(buf, values, event->read_size))
            ret = -EFAULT;
        goto out;
    
    unlock:
        mutex_unlock(&leader->child_mutex);
    out:
        kfree(values);
        return ret;
    }
    
    ||→
    
    static int __perf_read_group_add(struct perf_event *leader,
                        u64 read_format, u64 *values)
    {
        struct perf_event *sub;
        int n = 1; /* skip @nr */
        int ret;
    
        /* (2.4.1) 准备好数据:硬件counter从寄存器中读出count保存到event结构中 
            如果event正在运行尝试更新最新的数据
         */
        ret = perf_event_read(leader, true);
        if (ret)
            return ret;
    
        /*
         * Since we co-schedule groups, {enabled,running} times of siblings
         * will be identical to those of the leader, so we only publish one
         * set.
         */
        /* (2.4.2) 读取enable time并累加:
            leader->total_time_enabled:本perf_event的值
            leader->child_total_time_enabled:inherit创建的子进程,如果子进程退出把值累加到父进程这里
         */
        if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
            values[n++] += leader->total_time_enabled +
                atomic64_read(&leader->child_total_time_enabled);
        }
    
        /* (2.4.3) 读取running time并累加 */
        if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
            values[n++] += leader->total_time_running +
                atomic64_read(&leader->child_total_time_running);
        }
    
        /*
         * Write {count,id} tuples for every sibling.
         */
        /* (2.4.4) 读取group leader的count并累加 */
        values[n++] += perf_event_count(leader);
        if (read_format & PERF_FORMAT_ID)
            values[n++] = primary_event_id(leader);
    
        /* (2.4.5) 逐个读取group member的count并累加 */
        list_for_each_entry(sub, &leader->sibling_list, group_entry) {
            values[n++] += perf_event_count(sub);
            if (read_format & PERF_FORMAT_ID)
                values[n++] = primary_event_id(sub);
        }
    
        return 0;
    }
    
    |||→
    
    static int perf_event_read(struct perf_event *event, bool group)
    {
        int ret = 0;
    
        /*
         * If event is enabled and currently active on a CPU, update the
         * value in the event structure:
         */
        /* (2.4.1.1) 如果event正在运行尝试更新最新的数据 */
        if (event->state == PERF_EVENT_STATE_ACTIVE &&
                            !cpu_isolated(event->oncpu)) {
            struct perf_read_data data = {
                .event = event,
                .group = group,
                .ret = 0,
            };
            if (!event->attr.exclude_idle ||
                        !per_cpu(is_idle, event->oncpu)) {
                smp_call_function_single(event->oncpu,
                    __perf_event_read, &data, 1);
                ret = data.ret;
            }
        } else if (event->state == PERF_EVENT_STATE_INACTIVE) {
            struct perf_event_context *ctx = event->ctx;
            unsigned long flags;
    
            raw_spin_lock_irqsave(&ctx->lock, flags);
            /*
             * may read while context is not active
             * (e.g., thread is blocked), in that case
             * we cannot update context time
             */
            if (ctx->is_active) {
                update_context_time(ctx);
                update_cgrp_time_from_event(event);
            }
            if (group)
                update_group_times(event);
            else
                update_event_times(event);
            raw_spin_unlock_irqrestore(&ctx->lock, flags);
        }
    
        return ret;
    }

    5、perf_mmap

    如果需要读取perf_event的sample类型的数据,需要先给perf_event分配一个对应的ringbuffer,为了减少开销这个ringbuffer会被mmap映射成用户态地址。

    所以分配ringbuffer空间和映射成用户态地址这个两个操作都在perf_mmap()函数中完成。

    static int perf_mmap(struct file *file, struct vm_area_struct *vma)
    {
        struct perf_event *event = file->private_data;
        unsigned long user_locked, user_lock_limit;
        struct user_struct *user = current_user();
        unsigned long locked, lock_limit;
        struct ring_buffer *rb = NULL;
        unsigned long vma_size;
        unsigned long nr_pages;
        long user_extra = 0, extra = 0;
        int ret = 0, flags = 0;
    
        /*
         * Don't allow mmap() of inherited per-task counters. This would
         * create a performance issue due to all children writing to the
         * same rb.
         *
        /* (1) 担心数据量过大 */
        if (event->cpu == -1 && event->attr.inherit)
            return -EINVAL;
    
        if (!(vma->vm_flags & VM_SHARED))
            return -EINVAL;
    
        vma_size = vma->vm_end - vma->vm_start;
    
        /* (2.1) 第一次mmap,必须是映射data区域, 
            size = (1 + 2^n)pages
         */
        if (vma->vm_pgoff == 0) {
            nr_pages = (vma_size / PAGE_SIZE) - 1;
    
        /* (2.2) 第二次mmap,可以映射aux data区域 */
        } else {
            /*
             * AUX area mapping: if rb->aux_nr_pages != 0, it's already
             * mapped, all subsequent mappings should have the same size
             * and offset. Must be above the normal perf buffer.
             */
            u64 aux_offset, aux_size;
    
            if (!event->rb)
                return -EINVAL;
    
            nr_pages = vma_size / PAGE_SIZE;
    
            mutex_lock(&event->mmap_mutex);
            ret = -EINVAL;
    
            rb = event->rb;
            if (!rb)
                goto aux_unlock;
    
            aux_offset = ACCESS_ONCE(rb->user_page->aux_offset);
            aux_size = ACCESS_ONCE(rb->user_page->aux_size);
    
            if (aux_offset < perf_data_size(rb) + PAGE_SIZE)
                goto aux_unlock;
    
            if (aux_offset != vma->vm_pgoff << PAGE_SHIFT)
                goto aux_unlock;
    
            /* already mapped with a different offset */
            if (rb_has_aux(rb) && rb->aux_pgoff != vma->vm_pgoff)
                goto aux_unlock;
    
            if (aux_size != vma_size || aux_size != nr_pages * PAGE_SIZE)
                goto aux_unlock;
    
            /* already mapped with a different size */
            if (rb_has_aux(rb) && rb->aux_nr_pages != nr_pages)
                goto aux_unlock;
    
            if (!is_power_of_2(nr_pages))
                goto aux_unlock;
    
            if (!atomic_inc_not_zero(&rb->mmap_count))
                goto aux_unlock;
    
            if (rb_has_aux(rb)) {
                atomic_inc(&rb->aux_mmap_count);
                ret = 0;
                goto unlock;
            }
    
            atomic_set(&rb->aux_mmap_count, 1);
            user_extra = nr_pages;
    
            goto accounting;
        }
    
        /*
         * If we have rb pages ensure they're a power-of-two number, so we
         * can do bitmasks instead of modulo.
         */
        if (nr_pages != 0 && !is_power_of_2(nr_pages))
            return -EINVAL;
    
        if (vma_size != PAGE_SIZE * (1 + nr_pages))
            return -EINVAL;
    
        WARN_ON_ONCE(event->ctx->parent_ctx);
    again:
        mutex_lock(&event->mmap_mutex);
        if (event->rb) {
            if (event->rb->nr_pages != nr_pages) {
                ret = -EINVAL;
                goto unlock;
            }
    
            if (!atomic_inc_not_zero(&event->rb->mmap_count)) {
                /*
                 * Raced against perf_mmap_close() through
                 * perf_event_set_output(). Try again, hope for better
                 * luck.
                 */
                mutex_unlock(&event->mmap_mutex);
                goto again;
            }
    
            goto unlock;
        }
    
        user_extra = nr_pages + 1;
    
    accounting:
        user_lock_limit = sysctl_perf_event_mlock >> (PAGE_SHIFT - 10);
    
        /*
         * Increase the limit linearly with more CPUs:
         */
        user_lock_limit *= num_online_cpus();
    
        user_locked = atomic_long_read(&user->locked_vm) + user_extra;
    
        if (user_locked > user_lock_limit)
            extra = user_locked - user_lock_limit;
    
        lock_limit = rlimit(RLIMIT_MEMLOCK);
        lock_limit >>= PAGE_SHIFT;
        locked = vma->vm_mm->pinned_vm + extra;
    
        if ((locked > lock_limit) && perf_paranoid_tracepoint_raw() &&
            !capable(CAP_IPC_LOCK)) {
            ret = -EPERM;
            goto unlock;
        }
    
        WARN_ON(!rb && event->rb);
    
        if (vma->vm_flags & VM_WRITE)
            flags |= RING_BUFFER_WRITABLE;
    
        /* (3) 分配ringbuffer */
        if (!rb) {
            rb = rb_alloc(nr_pages,
                      event->attr.watermark ? event->attr.wakeup_watermark : 0,
                      event->cpu, flags);
    
            if (!rb) {
                ret = -ENOMEM;
                goto unlock;
            }
    
            atomic_set(&rb->mmap_count, 1);
            rb->mmap_user = get_current_user();
            rb->mmap_locked = extra;
    
            /* (3.1) 绑定rb到event */
            ring_buffer_attach(event, rb);
    
            perf_event_init_userpage(event);
            perf_event_update_userpage(event);
    
        /* (4) 分配aux区域 */
        } else {
            ret = rb_alloc_aux(rb, event, vma->vm_pgoff, nr_pages,
                       event->attr.aux_watermark, flags);
            if (!ret)
                rb->aux_mmap_locked = extra;
        }
    
    unlock:
        if (!ret) {
            atomic_long_add(user_extra, &user->locked_vm);
            vma->vm_mm->pinned_vm += extra;
    
            atomic_inc(&event->mmap_count);
        } else if (rb) {
            atomic_dec(&rb->mmap_count);
        }
    aux_unlock:
        mutex_unlock(&event->mmap_mutex);
    
        /*
         * Since pinned accounting is per vm we cannot allow fork() to copy our
         * vma.
         */
        vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP;
    
        /* (5) 给mmap的ops赋值 */
        vma->vm_ops = &perf_mmap_vmops;
    
        if (event->pmu->event_mapped)
            event->pmu->event_mapped(event);
    
        return ret;
    }

    参考资料:

    1、Performance Counters for Linux

    2、Notes on Analysing Behaviour Using Events and Tracepoints

    3、系统级性能分析工具perf的介绍与使用

    4、性能分析工具—Perf简介汇总整理

    5、Linux 性能检测/调优之Perf_Event

    展开全文
  • Linux配置并编译内核

    万次阅读 多人点赞 2018-08-03 16:29:36
    配置工具会询问许多问题并且允许开发者配置内核的每个方面。如果你有不确定的问题或者特性,你最好使用配置工具提供的默认值。本系列教程会使读者逐步了解配置内核的整个过程。   配置代码前需要在源文件的文件夹...
  • LINUX内核目录文件说明以及配置并编译内核的方法

    万次阅读 多人点赞 2017-06-09 10:28:26
    在下载内核前,我们应该讨论一些重要的术语和事实。Linux内核是一个宏内核,这意味着整个操作系统都运行在内核预留的内存里。说的更清楚一些,内核是放在内存里的。内核所使用的空间是内核预留的。只有内核可以使用...
  • Nginx配置文件详解

    千次阅读 2021-12-01 13:45:51
    NGINX配置文件详解
  • nginx Event配置

    2017-04-20 09:43:24
    以下只能在 event 块中进行配置。 设置网络连接的序列化 当某一个时刻只有一个网络连接到来时,多个睡眠进程会被同时叫醒,,但只有一个进程可获得连接。如果每次唤醒的进程数太多,会影响一部分系统性能。为解决...
  • Linux内核配置选项简介

    千次阅读 2019-03-12 23:17:12
    目录 General setup常规设置 Enable loadable module support可加载模块支持 ...Enable the block layer块设备支持 ...把内核配置信息编译进内核中,以后可以通过scripts/extract-ikconfig脚本...
  • CentOS Linux实时性配置要点 rtoax 2021年2月 1. 概要 1.1. 实时性补丁 补丁地址:CentOS 7 - RealTime for x86_64: RealTime: kernel-rt-doc 3.10.0-693.2.2.rt56.623.el7.x86_64:下载 3.10.0-1127.rt56....
  • Linux内核 eBPF基础 ...在上篇讲到使用strace最终perf stat ls指令的系统调用,perf先从proc文件系统中获取内核支持的perf event,然后使用系统调用perf_event_open和ioctl控制内核中perf_event的使能,并
  • linux内核make menuconfig配置

    万次阅读 2018-04-08 12:37:22
    这个选项允许.config文件(即编译LINUX时的配置文件)保存在内核当中。  它提供正在运行中的或者还在硬盘中的内核的相关配置选项。可以通过内核镜像文件 kernel image file 用命令 script scripts/extract-ikconfig...
  • 史上最全linux内核配置详解

    千次阅读 2019-01-17 14:31:58
    对于每一个配置选项,用户可以回答"y"、"m"或"n"。其中"y"表示将相应特性的支持或设备驱动程序编译进内核;"m"表示将相应特性的支持或设备驱动程序编译成可加载...
  • Linux内核配置选项 (经典学习)

    万次阅读 2019-01-22 18:09:10
    下面以最新的Linux 2.6.20内核为例,介绍比较常用的一些Linux内核配置选项,其他选项读者可以参考系统提供的帮助信息。 需要说明的是,在内核配置中,某项选择Y表示把该项选择进内核,选择M则表示把该项编译成模块...
  • Nginx配置配置文件详解

    万次阅读 多人点赞 2018-10-24 22:45:23
    配置文件配置文件
  • Nginx(二):反向代理原理 与 配置文件详解 Nginx(三):负载均衡策略 与 Nginx静态服务器 Nginx(四):Nginx配置实战 Nginx概述: Nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器;同时...
  • Linux内核配置选项

    千次阅读 2018-04-25 16:08:37
    http://blog.csdn.net/wdsfup/article/details/52302142http://www.manew.com/blog-166674-12962.htmlGentoo LinuxGentoo内核(gentoo-sources)特有的选项Gentoo Linux supportCONFIG_GENTOO_LINUX选"...
  • linux Uevent事件机制 与 Input子系统
  • linux 内核配置详解

    千次阅读 2017-05-23 15:24:30
    1. make clean:删除大多的由编译生成的文件、但会保留内核配置文件.config。  make mrproper:删除所有的编译生成的文件,还有内核配置文件,再加上各种备份文件。  make distclean:mrproper删除的文件,...
  • Linux内核配置参考

    千次阅读 2016-11-17 22:31:11
    Linux 2.6内核配置说明 (国嵌)    Code maturity level options  代码成熟度选项 Prompt for development and/or incomplete code/drivers 显示尚在开发中或尚未完成的代码与驱动.除非你是测试人员或者...
  • kubelet配置文件参数

    千次阅读 2020-03-08 15:06:55
    --cloud-config string 云提供商的配置文件路径,没有配置文件时为空字符串。 --cloud-provider string 云服务提供商。默认情况下, kubelet 将尝试自动检测云提供商,如果不使用云提供商可以指定该参数 为空字符串...
  • RTX5 | 配置文件RTX_Config.h(一)

    千次阅读 2021-06-09 21:15:33
    **在学习RTX5的配置文件后,我认为RTX5的系统配置文件比FreeRTOS与ucosIII的系统配置文件都要简单。我在STM32F103与STM32H743都移植了RTX5,配置文件使用默认配置也能正常工作(FreeRTOS在移植后需要认真配置中断...
  • Linux内核配置选项详解

    千次阅读 2014-07-31 14:59:07
    第一部分 01、Code maturity level options ---> 代码成熟等级选项 01.01、 [ ] Prompt for development and/or ...你应该选择它,因为有许多设备可能必需选择这个选项才能进行配置,实际上它是安全的。 01
  • 64-bit kernel——支持64位 General setup——通用...Local version-append to kernel release——内核显示的版本信息,填入64字符以内的字符串,可用uname -a命令看到。 Automatically append version informatio...
  • 14.4.5 TDI_EVENT_CONNECT类型的设置事件的过滤 336 14.4.6 直接获取发送函数的过滤 337 14.4.7 清理请求的过滤 339 14.5 本书例子tdifw.lib的应用 341 14.5.1 tdifw库的回调接口 341 14.5.2 tdifw库的使用例子...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 39,224
精华内容 15,689
关键字:

内核的event事件配置文件