客户有一个4K的屏幕,放在两个显示器上面显示,现在需要将其对半分割为两个正常的显示器。这么说不好理解,转换需求可以这样理解,一个4K的显示分辨率的显示器,需要系统将其认为两个2K的显示器显示。通过前期调研,发现了两种方法实现。分别如下
此方案是为X增加了两个monitor,然后根据窗管的识别,将其认定为两个单独的屏幕,此方法需要修改系统代码。主要如下
--- xorg-server-21.1.4/randr/rrmonitor.c.orig 2022-09-30 00:09:40.458561832 +0200 +++ xorg-server-21.1.4/randr/rrmonitor.c 2022-09-30 00:09:46.298529786 +0200 @@ -528,27 +528,6 @@ continue; } - /* For each output in 'info.outputs', each one is removed from all - * pre-existing Monitors. If removing the output causes the list - * of outputs for that Monitor to become empty, then that - * Monitor will be deleted as if RRDeleteMonitor were called. - */ - - for (eo = 0; eo < existing->numOutputs; eo++) { - for (o = 0; o < monitor->numOutputs; o++) { - if (monitor->outputs[o] == existing->outputs[eo]) { - memmove(existing->outputs + eo, existing->outputs + eo + 1, - (existing->numOutputs - (eo + 1)) * sizeof (RROutput)); - --existing->numOutputs; - --eo; - break; - } - } - if (existing->numOutputs == 0) { - (void) RRMonitorDelete(client, screen, existing->name); - break; - } - } if (monitor->primary) existing->primary = FALSE; }
这里屏蔽了Xorg本身将存在的output的monitor删除的代码
--- src/gtk/gdk/x11/gdkscreen-x11.c.orig 2022-09-30 16:24:00.181850959 +0200 +++ src/gtk/gdk/x11/gdkscreen-x11.c 2022-09-30 21:32:47.288912422 +0200 @@ -644,21 +644,14 @@ #undef EDID_LENGTH } - monitor = find_monitor_by_output (x11_display, output); - if (monitor) - monitor->remove = FALSE; - else - { - monitor = g_object_new (GDK_TYPE_X11_MONITOR, - "display", display, - NULL); - monitor->output = output; - monitor->add = TRUE; - g_ptr_array_add (x11_display->monitors, monitor); - } + monitor = g_object_new (GDK_TYPE_X11_MONITOR, + "display", display, + NULL); + monitor->add = TRUE; + g_ptr_array_add (x11_display->monitors, monitor); gdk_monitor_get_geometry (GDK_MONITOR (monitor), &geometry); - name = g_strndup (output_info->name, output_info->nameLen); + name = gdk_x11_get_xatom_name_for_display(display, rr_monitors[i].name); newgeo.x = rr_monitors[i].x / x11_screen->window_scale; newgeo.y = rr_monitors[i].y / x11_screen->window_scale; @@ -687,7 +680,6 @@ gdk_monitor_set_connector (GDK_MONITOR (monitor), name); gdk_monitor_set_manufacturer (GDK_MONITOR (monitor), manufacturer); g_free (manufacturer); - g_free (name); if (rr_monitors[i].primary) primary_output = monitor->output;
这里找到monitor不是通过output寻找,而是直接增加monitor,这就避免了同一个显示器上不能输出两块屏幕的问题
这边尝试了metacity和mutter,均没办法正常识别,但是基于gtk3的xfce是能够正常识别到两个屏幕的。
apt install -y xfce4
根据如上理解,我们后续要做的工作是,将例如kwin这类的窗管,在创建屏幕的过程中,不应该绑定一个output就一个monitor的方式。而是根据所有的monitor来都创建显示display
xrandr --delmonitor VIRTUAL-LEFT xrandr --delmonitor VIRTUAL-RIGHT xrandr --setmonitor VIRTUAL-LEFT 960/0x1200/1+0+0 DSI-1 xrandr --setmonitor VIRTUAL-RIGHT 960/1x1200/1+960+0 none xrandr --listmonitors xrandr --fb 1921x1200 xrandr --fb 1920x1200
https://github.com/micw/xorg-split-screen-archlinux/blob/packages/gtk3/trunk/multiple_monitors_per_output.patch https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/981/diffs?commit_id=21e569ef1e87e4dfcedbdac99216e7147e2b05ff
https://gitlab.gnome.org/GNOME/gtk/-/issues/2013#note_1280968
通过修改xrandr的api,可以实现将x划分为多个显示接口,也就是xrandr显示的是自己设置的虚假的显示器
参考仓库地址
https://github.com/phillipberndt/fakexrandr
参考测试示例
https://gist.github.com/phillipberndt/7688785#file-fake-xinerama-L50
相应论坛信息
https://unix.stackexchange.com/questions/605755/use-xrandr-to-split-display-in-two-virtual-screens
DWM窗管支持全屏补丁
https://dwm.suckless.org/patches/fakefullscreen/dwm-fakefullscreen-20210714-138b405.diff
fakeXinerama仓库
https://github.com/Xpra-org/libfakeXinerama
xrdp补丁
https://github.com/asafge/xrdp_dualmon/blob/master/fakexinerama/Xinerama.c
参考论坛
https://gitlab.gnome.org/GNOME/gtk/-/issues/2013#note_1280968
测试例子
fakexrandr-manage.py gui
通过上述两种办法试验,均可以在X86上正常切换屏幕。当前在麒麟系统上的kwin不能直接使用,需要修改代码
bluetoothctl是bluez协议的调试工具,用于前期的蓝牙基本功能的验证和测试。本文基于bluetoothctl工具,介绍了如何使用其进行蓝牙的基本通讯
root@kylin:/home/kylin# dpkg -l | grep bluez ii bluez 5.53-0kylin3k4 arm64 Bluetooth tools and daemons ii bluez-obexd 5.53-0kylin3k4 arm64 bluez obex daemon
root@kylin:/home/kylin# hciconfig hci0: Type: Primary Bus: UART BD Address: 9C:71:D9:BC:DD:BF ACL MTU: 1021:8 SCO MTU: 64:1 UP RUNNING PSCAN RX bytes:441417 acl:200 sco:0 events:10560 errors:0 TX bytes:12117 acl:184 sco:0 commands:173 errors:0
如果蓝牙状态不是 “UP RUNNING PSCAN” 则需要手动启动蓝牙
hciconfig hci0 up
root@kylin:/home/kylin# rfkill list 0: bt_default: Bluetooth Soft blocked: no Hard blocked: no 1: phy0: Wireless LAN Soft blocked: no Hard blocked: no 2: brcmfmac-wifi: Wireless LAN Soft blocked: no Hard blocked: no 3: hci0: Bluetooth Soft blocked: no Hard blocked: no
如果蓝牙被rfkill block,则需要通过rfkill命令unblock,否则蓝牙功能仍是被系统关闭的
rfkill unblock hci0
hcitool lescan LE Scan ... 21:0E:38:14:CE:18 (unknown) 5F:5A:FC:85:1C:05 (unknown)
如果能够探测到低功耗蓝牙设备,代表蓝牙的基本功能正常
root@kylin:/home/kylin# l2ping 54:09:10:38:28:97 Ping: 54:09:10:38:28:97 from 9C:71:D9:BC:DD:BF (data size 44) ... 0 bytes from 54:09:10:38:28:97 id 0 time 9.91ms 0 bytes from 54:09:10:38:28:97 id 1 time 8.55ms 0 bytes from 54:09:10:38:28:97 id 2 time 8.48ms
root@kylin:/home/kylin# bluetoothctl Agent registered [bluetooth]#
[bluetooth]# scan on
等到扫到自己的蓝牙设备之后,可以关闭蓝牙扫描
[bluetooth]# scan off
[bluetooth]# devices Device 54:09:10:38:28:97 iPhone
由上可以发现,设备已经搜到蓝牙名字为iPhone,MAC为 54:09:10:38:28:97 的设备
[bluetooth]# agent on Agent is already registered [bluetooth]# default-agent Default agent request successful
[bluetooth]# trust 54:09:10:38:28:97 [CHG] Device 54:09:10:38:28:97 Trusted: yes Changing 54:09:10:38:28:97 trust succeeded [bluetooth]# pair 54:09:10:38:28:97 Attempting to pair with 54:09:10:38:28:97 [CHG] Device 54:09:10:38:28:97 Connected: yes Request confirmation [agent] Confirm passkey 886360 (yes/no): yes
如果之前之前配对过,就无需配对,如果仍选择配对,会报如下错误,但不影响使用
[bluetooth]# pair 54:09:10:38:28:97 Attempting to pair with 54:09:10:38:28:97 Failed to pair: org.bluez.Error.AlreadyExists
查看已经配对过的设备
[iPhone]# paired-devices Device 54:09:10:38:28:97 iPhone
[bluetooth]# connect 54:09:10:38:28:97 Attempting to connect to 54:09:10:38:28:97 [CHG] Device 54:09:10:38:28:97 Connected: yes [iPhone]#
如果蓝牙正常连接成功,会出现蓝牙名字的终端,如 [iPhone]
menu gatt [Blank]# list-attributes Characteristic (Handle 0x9240) /org/bluez/hci0/dev_60_B7_4C_51_AC_D7/service0014/char0015 00002222-0000-1000-8000-00805f9b34fb Unknown
select-attribute /org/bluez/hci0/dev_60_B7_4C_51_AC_D7/service0023/char0024 [iPhone:/service0014/char0015]# attribute-info Characteristic - Unknown UUID: 00002222-0000-1000-8000-00805f9b34fb Service: /org/bluez/hci0/dev_60_B7_4C_51_AC_D7/service0014 Flags: read Flags: write Flags: extended-properties Flags: reliable-write
[iPhone:/service0023/char0024]# write 1 Attempting to write /org/bluez/hci0/dev_60_B7_4C_51_AC_D7/service0023/char0024 [iPhone:/service0023/char0024]# read Attempting to read /org/bluez/hci0/dev_60_B7_4C_51_AC_D7/service0023/char0024 [CHG] Attribute /org/bluez/hci0/dev_60_B7_4C_51_AC_D7/service0023/char0024 Value: 01 . 01 .
1.下载LightBlue 2.选择Virtual Devices 3.点击+ 4.选择默认的profile,这里默认可以是blank 5.点击Blank,进去服务 6.进入默认的uuid 2222 7.修改uuid,修改描述,修改属性 8.利用bluetoothctl进行读写操作验证
最近在某个项目上,需要使用字符终端的显示,并且还需要显示中文。通过网络发现,Framebuffer Console支持中文的功能默认却没在linux的主分支,但是github上有人在维护这个patch。https://github.com/zhmars/cjktty-patches 。鉴于此,本文介绍一下Framebuffer Console的配置方法以及cjktty的配置步骤
# # Console display driver support # CONFIG_DUMMY_CONSOLE=y CONFIG_DUMMY_CONSOLE_COLUMNS=80 CONFIG_DUMMY_CONSOLE_ROWS=25 CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
这样就打开了内核的字符终端
对应defconfig打开CONFIG_FRAMEBUFFER_CONSOLE即可
make bindeb-pkg -j64
这里编译出来会产生如下包,安装即可
../linux-headers-5.10.66_5.10.66-13_arm64.deb ../linux-headers-5.10.66_5.10.66_arm64.deb ../linux-image-5.10.66_5.10.66-13_arm64.deb ../linux-image-5.10.66_5.10.66_arm64.deb ../linux-image-5.10.66-dbg_5.10.66-13_arm64.deb ../linux-image-5.10.66-dbg_5.10.66_arm64.deb ../linux-libc-dev_5.10.66-13_arm64.deb ../linux-libc-dev_5.10.66_arm64.deb
如果使用rk平台默认boot编译,可使用如下编译方法
export ARCH=arm64 export CROSS_COMPILE=aarch64-none-linux-gnu- export PATH=$PATH:$YOURSDK/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/ make rockchip_linux_defconfig make rk3588-evb1-lp4-v10-linux.img -j64
编译完成将生成boot.img
在正常进入系统之后,通过组合按键
Ctrl+Alt+F{1-6} 即可进入字符终端界面
但是,默认的字符界面,没办法正常的显示中文。原系统的中文的文件显示乱码。于是找到了cjktty
cjktty的patch仓库地址在 https://github.com/zhmars/cjktty-patches ,通过其README.md 可以发现其已经不断支持到最新内核上了
因为我们的内核版本是5.10,故拉取5.10的patch
wget https://raw.githubusercontent.com/zhmars/cjktty-patches/master/v5.x/cjktty-5.10.patch
打补丁就简单了
patch -p1 < cjktty-5.10.patch
CONFIG_FONT_CJK_16x16=y CONFIG_FONT_CJK_32x32=y
编译方法同上,使用时可以发现,该作者实际就是把两个原英文字符叠加成一个中文字符,从而显示了的中文字符
在使用shell环境的时候,难免会遇到对文本进行操作的情况,shell提供了一些功能强大的文本操作命令,这里做了简单汇总,有助于了解和学习这些工具,提高工作中处理文本的效率
grep是经常使用的文本查找工具,下面列举grep常用的一些命令。当然,也有比grep更好的工具ripgrep。这个可以后续探索
grep -vE "^#|^$" /etc/ssh/sshd_config
-v是选中不匹配的行,-E是明确使用正则 ^$ 是没有数据的行 ^#是以#开头的行
dmesg | grep -i cfg80211 -A 5 dmesg | grep -i cfg80211 -B 5 dmesg | grep -i cfg80211 -C 5
A after 之后的5行, B before 之前的5行, C 是A+B的合计,前后5行
-i 忽略大小写
cut是一个字符截取命令,其命令在处理一些字符串上,可以方便的对一些字符串直接截取。如果文本内容格式比较规律的话,也可以用来筛选指定分隔符下的内容
ifconfig | grep -vE "^ |^$" | cut -d ' ' -f 1 | cut -d ":" -f 1
-d 是指定分隔符, -f 是指定某个域。
-d ' ' -f 1 是以单个空格为分隔符找到第一个域
cut -d ":" -f 1 是以冒号为分隔符找到第一个域
dmesg | cut -b -15
sed是重要的字符替换工具,也就流编辑器,可以方便的直接替换一些文本内容,从而不用打开文本去处理,这使得在shell内对文本替换来说十分方便
-e :添加一个命令,多个sed命令可以用这个 -i :直接修改文件内容 -n :取消自动打印,只打印模式匹配的行 -E :支持扩展表达式;
cat filename | sed -e“s/text/replace/g”-e“s/text/replace/g”
-e 允许运行多个命令,s///g是替换的语法, s指的搜索,g指的全局。也就是搜索全局,然后统一替换
cat filename | sed -e "4a help"
在文本第4行后添加help作为新的一行
cat filename | sed -e "4i help"
在文本第4行前添加help作为新的一行
cat filename | sed -e "/^$/d"
删除空格行
cat filename | sed "s/[a-z]\|[A-Z]/[&]/g"
对文本中的所有字符加上[] 。&指的是上一次搜索到的值。 | 是或,意思可以多条规则匹配
echo a b | sed -E -e "s/([a-z]) ([a-z])/\2 \1/"
()可以声明这是一个字串,()声明的字串,可以通过\1 \2等通配来引用。
-E 代表使用正则,如果不加-E 则使用()的时候需要转换成 () 那么匹配一个a-z的字符需要这样表示 ([a-z])
那么不加-E 的效果如下
echo a b c | sed -e "s/\([a-z]\) \([a-z]\) \(c\)/\3 \2 \1/"
sort是重要的字符和数字排序工具,可以直接对文本进行升序和降序操作
grep -n '.' /etc/os-release | sort -t ':' -k1 -nru
-t 指定分隔符
-k 指定分隔符划分的域
-n 指定排序方式为数字(默认为字符)
-r 指定反序
cat /etc/os-release | sort -t '=' -k 1.2 -u
-u 去掉重复的排序行
uniq是找重复的工具,可以快速的识别出文本中完全重复的行,并记录次数
cat Readme.md | uniq -dci
cat Readme.md | uniq -u
-d 是找到完全重复的行
-c 是统计重复次数
-i 是忽略大小写
-u 是打印不重复的行
awk模式是模式扫描及处理语言,也就是说文本处理规则太多了,我定义一个语言,你们想怎么匹配就怎么匹配
NF 一条记录的字段的数目 NR 行号,当前处理的文本行的行号 FS 定义间隔符 OFS 定义输出字段分隔符,默认空格 RS 输入记录分隔符,默认换行
awk默认格式为
awk '{pattern + action}' {filenames}
这里'{}'里面就是代码域,用于编写awk代码的地方。
最简单的打印可以如下
echo | awk '{print "Hello World"}'
直接打印hello world
echo Hello World | awk '{print $1" "$2}'
打印传过来的参数
cat /etc/passwd | awk 'BEGIN{count=0;print "begin is:" count} {count=count+1;print count} END{print "end is:"count}'
这里统计了passwd的文件行数
awk不仅默认{}里面填写代码,也可以在BEGIN和END宏里面填写代码,BEGIN填写的代码,在{}前运行,END填写的代码在{}后运行
ll |awk 'BEGIN {byte=0;print "统计当前目录占用字节数"} {byte=byte+$5;} END{print "当前目录字节数为:",byte}'
这里统计了当前目录的字节数,不包括下级目录
cat /etc/passwd | awk '{if(NR>=10 && NR<=20) print $1}'
这里取passwd文件的第10行到20行内容
'{}'的if语句可以和C语言一样,print可以和python语言一样。
cat /etc/passwd | awk -F '' 'BEGIN{count=0} {for(i=1; i<=NF; i++) count++} END{print count}'
这里对每个字符做计数,最后打印存在的字符总数。
echo abcdefg | awk -F '' '{i=1; while(i<=length($0)) {print $i;i++}}'
这里对一行abcdefg转换成一个字母多行
echo "1 2,3:4" | awk -F [" ",:] '{print $1$2$3$4}'
以空格,逗号,冒号作为分隔符打印第1,2,3,4个匹配的内容
echo "1 2,3:4" | awk BEGIN{'FS="[ ,:]"} {print $1$2$3$4}'
FS宏也可以代替-F参数,需要注意的是,FS参数需要在后面加上"" 双引号
这里介绍了grep sort uniq awk sed cut一共六个命令,每个命令都可以搭配起来使用。这里简要介绍一下在哪些情况下可以用哪些命令
1.搜索一些匹配单词的时候,可以用grep,例如 grep -nr text filename 2.面临排序问题的时候,可以用sort, 例如 sort -r 3.去重复行的时候,可以用uniq, 例如 uniq -u 4.批量替换的时候,可以用sed,例如 sed -i “s/text/replace/g” 5.对特定格式字符串进行截取的时候,可以用cut ,例如 cut b 1-3 6.在对一些列的域做划分的时候,可以用awk , 例如 awk -F ‘:’ '{print $1}' 7.对文本进行高级编辑操作的时候,awk都能完成.
RK3588的实时内核,这里测试验证其指标,用于后续评估判断RK3588相关的实时指标作为参考借鉴。测试结果或有偏差,最终以实际测试为准
RT内核两笔补丁需要修改,如下:
diff --git a/include/trace/hooks/dtask.h b/include/trace/hooks/dtask.h index 9890bfe5c41d..fc2a51ce547e 100644 --- a/include/trace/hooks/dtask.h +++ b/include/trace/hooks/dtask.h @@ -58,7 +58,7 @@ DECLARE_HOOK(android_vh_sched_show_task, TP_ARGS(task)); DECLARE_HOOK(android_vh_alter_mutex_list_add, TP_PROTO(struct mutex *lock, - struct mutex_waiter *waiter, + struct rt_mutex_waiter *waiter, struct list_head *list, bool *already_on_list), TP_ARGS(lock, waiter, list, already_on_list)); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index a2506b92e0c1..8d794db5e4d6 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -67,6 +67,7 @@ #ifdef CONFIG_PRINTK_TIME_FROM_ARM_ARCH_TIMER #include <clocksource/arm_arch_timer.h> +#if 0 static u64 get_local_clock(void) { u64 ns; @@ -76,6 +77,7 @@ static u64 get_local_clock(void) return ns; } +#endif #else static inline u64 get_local_clock(void) {
编译命令:
export ARCH=arm64 export CROSS_COMPILE=aarch64-none-linux-gnu- export PATH=$PATH:/path/to/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin make rockchip_linux_defconfig rockchip_rt.config make kylin.img -j123
这里项目有kylin.dts,故编译为kylin.img的内核,其他项目自行修改
#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <stdlib.h> #include <string.h> #include <inttypes.h> #include <unistd.h> #include <stdio.h> #include <limits.h> #include <sched.h> #include <signal.h> #include <netinet/if_ether.h> #include <netinet/ip.h> #include <stdbool.h> #include <pthread.h> //#include <lock.h> #include <fcntl.h> #include <time.h> #include <mqueue.h> #include <semaphore.h> #include <stdarg.h> #include <sys/mman.h> #define TESTMQ_NAME "/testmsg" #define MSG_SIZE 8 char *testmsg = "Testing"; static int print_mode, TestCount; mqd_t sendsyncmq, recvsyncmq, testmq; struct timespec tv_s[10], tv_e[10]; static int over; struct mq_attr mqstat; pthread_t send_tid, recv_tid, send_recv_tid; static int tracemark_fd = -1; #define FILE_TRACEMARK "/sys/kernel/debug/tracing/trace_marker" static void open_tracemark() { if ((tracemark_fd = open(FILE_TRACEMARK, O_WRONLY)) == -1) printf("unable to open trace_marker file: %s\n", FILE_TRACEMARK); } static void close_tracemark() { close(tracemark_fd); } #define TRACEBUFSIZ 1024 static __thread char tracebuf[TRACEBUFSIZ]; static void tracemark(char *fmt, ...) __attribute__((format(printf, 1, 2))); static void tracemark(char *fmt, ...) { va_list ap; int len; /* bail out if we're not tracing */ /* or if the kernel doesn't support trace_mark */ if (tracemark_fd < 0) return; va_start(ap, fmt); len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap); va_end(ap); write(tracemark_fd, tracebuf, len); } /* * * * 终止ftrace,这样可以在ftrace的ringbuffer的底部保留问题现场 * * */ static int trace_fd = -1; #define FILE_TRACEON "/sys/kernel/debug/tracing/tracing_on" static void open_tracingon() { if ((trace_fd = open(FILE_TRACEON, O_WRONLY)) == -1) printf("unable to open trace_on file: %s\n", FILE_TRACEON); } static void close_tracingon() { close(trace_fd); } static void disable_trace() { if (trace_fd < 0) return; write(trace_fd, "0", 1); } pthread_t test_task_create(char *name, void *thread_func, size_t stack_size, int prio, int core) { int ret; struct sched_param param; pthread_attr_t attr; pthread_t thread_id; cpu_set_t mask; ret = pthread_attr_init(&attr); if (ret) { printf("ERROR: pthread_attr_init failed\n"); return -1; } ret = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); if (ret) { printf("ERROR: pthread_attr_setscope failed\n"); goto out; } ret = pthread_attr_setstacksize(&attr, stack_size); if (ret) { printf("ERROR: pthread_attr_setstacksize failed\n"); goto out; } ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); if (ret) { printf("ERROR: pthread_attr_setinheritsched failed\n"); goto out; } /* ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (ret) { printf("ERROR: pthread_attr_setdetachstate failed\n"); goto out; } */ ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO); if (ret) { printf("ERROR: pthread_attr_setschedpolicy failed\n"); goto out; } param.sched_priority = prio; ret = pthread_attr_setschedparam(&attr, ¶m); if (ret) { printf("ERROR: pthread_attr_setschedparam failed\n"); goto out; } ret = pthread_create(&thread_id, &attr, thread_func, NULL); if (ret) { printf("ERROR: pthread_create %s failed\n", name); goto out; } CPU_ZERO(&mask); CPU_SET(core, &mask); if (pthread_setaffinity_np(thread_id, sizeof(mask), &mask) == -1) printf("ERROR: %s pthread_setaffinity_np failed, cpu_core = %d\n", name, core); return thread_id; out: pthread_attr_destroy(&attr); return -1; } struct timespec send_tv, recv_tv; bool flag = false; bool flag_send = false; void send_task_pthread(int core) { int i, ret; struct timespec tv1, tv2; unsigned int send_diff, send_sum; unsigned int send_mindiff, send_avg, send_maxdiff; char recvtestmsg[MSG_SIZE]; send_sum = 0; send_avg =0; send_mindiff = 100000000; send_maxdiff = 0; for (i = 0; i < TestCount; i++) { if (over) break; /* * send the test massege and calculate the cost time */ if (i > 1) { send_diff = (recv_tv.tv_sec * 1000000000 + recv_tv.tv_nsec) - (send_tv.tv_sec * 1000000000 + send_tv.tv_nsec); send_sum += send_diff; send_avg = send_sum / (i + 1); if (send_diff < send_mindiff) { send_mindiff = send_diff; } /* * 从第三次开始计入最大时间 * 原因:第二次发送消息时,如果接受线程未创建成功,mq_send阻塞, * 等待接受线程创建,第二次计算时间并不准确。 * 所以等接收线程接收一次消息后,开始统计最大时间 */ if ((send_diff > send_maxdiff) && (i > 1)) { send_maxdiff = send_diff; } // if (((i % 10) == 0)) printf("Task Switch Time, loop: %d, Min: %4d ns, Cur: %4d ns, Avg: %4d ns, Max: %4d ns\n\n", i, send_mindiff, send_diff, send_avg, send_maxdiff); } flag_send = false; ret = mq_send(testmq, testmsg, strlen(testmsg), 1); clock_gettime(CLOCK_REALTIME, &send_tv); flag_send = true; flag = false; while (!flag) { usleep(10000); } // if (!flag){ // usleep(10000); // flag = true; // printf("ERROR: send test message failed\n"); // sleep(1); // goto retry; // } } over = 1; printf("mq_send test count: %d \n", i); printf("Final result, Min: %4d ns, Cur: %4d ns, Avg: %4d ns, Max: %4d ns\n\n", send_mindiff, send_diff, send_avg, send_maxdiff); /* close_tracemark(); close_tracingon(); */ pthread_kill(recv_tid, SIGTERM); return; } void recv_task_pthread(int core) { int i, ret; ssize_t recv_length; struct timespec tv1, tv2; unsigned int recv_diff, recv_sum; unsigned int recv_mindiff, recv_avg, recv_maxdiff; char recvsyncmsg[MSG_SIZE]; char recvtestmsg[MSG_SIZE]; while (!over) { /* * receive the test massege and calculate the cost time */ recv_length = mq_receive(testmq, recvtestmsg, MSG_SIZE, NULL); while (!flag_send) { } clock_gettime(CLOCK_REALTIME, &recv_tv); flag = true; if ( recv_length != strlen(testmsg)) { perror("could not receive test message"); over = 1; } } return; } void send_recv_pthread(int core) { int i, ret; ssize_t recv_length; struct timespec tv1, tv2; unsigned int recv_diff, recv_sum; unsigned int recv_mindiff, recv_avg, recv_maxdiff; char recvtestmsg[MSG_SIZE]; recv_sum = 0; recv_avg =0; // recv_mindiff = UINT_MAX; recv_mindiff = 100000000; recv_maxdiff = 0; for (i = 0; i < TestCount; i++) { /* * send the test massege */ ret = mq_send(testmq, testmsg, strlen(testmsg), 1); if (ret < 0) { printf("ERROR: send test message failed\n"); over = 1; } usleep(200); /* * receive the test massege and calculate the cost time */ clock_gettime(CLOCK_REALTIME, &tv1); recv_length = mq_receive(testmq, recvtestmsg, MSG_SIZE, NULL); clock_gettime(CLOCK_REALTIME, &tv2); if ( recv_length != strlen(testmsg)) { perror("could not receive test message"); over = 1; } recv_diff = (tv2.tv_sec * 1000000000 + tv2.tv_nsec) - (tv1.tv_sec * 1000000000 + tv1.tv_nsec); recv_sum += recv_diff; recv_avg = recv_sum / (i + 1); if (recv_diff < recv_mindiff) { recv_mindiff = recv_diff; } if ((recv_diff > recv_maxdiff) && (i > 0)) { recv_maxdiff = recv_diff; } if (((i % 10) == 0)) printf("MQ Receive Time,loop: %d, Min: %4d ns, Cur: %4d ns, Avg: %4d ns, Max: %4d ns\n\n", i, recv_mindiff, recv_diff, recv_avg, recv_maxdiff); usleep(100); } printf("mq_receive test count: %d \n", i); printf("Final result, Min: %4d ns, Cur: %4d ns, Avg: %4d ns, Max: %4d ns\n\n", recv_mindiff, recv_diff, recv_avg, recv_maxdiff); return; } int main(int argc, char **argv) { int SendCore, SendThreadPrio, RecvCore, RecvThreadPrio, mode; if (argc != 7) { printf("Wrong CMD, check the parameters, please!\n"); printf("./mq_task_switch 1 90 2 90 10000 1\n"); return -1; } if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) { perror("mlockall"); return -1; } SendCore = atoi(argv[1]); SendThreadPrio = atoi(argv[2]); RecvCore = atoi(argv[3]); RecvThreadPrio = atoi(argv[4]); TestCount = atoi(argv[5]); mode = atoi(argv[6]); over = 0; memset(&mqstat, 0, sizeof(mqstat)); mqstat.mq_maxmsg = 1; mqstat.mq_msgsize = 8; mqstat.mq_flags = 0; mq_unlink(TESTMQ_NAME); testmq = mq_open(TESTMQ_NAME, O_RDWR | O_CREAT, 0666, &mqstat); if (testmq < 0) { printf("open test message queue failed\n"); return -1; } if (mode == 0) { send_recv_tid = test_task_create("SndRECVTask", send_recv_pthread, 262144, SendThreadPrio, SendCore); if (send_recv_tid < 0) { printf("ERROR: Create send_recv_pthread failed!\n"); return -1; } pthread_join(send_recv_tid, NULL); } else { send_tid = test_task_create("sndTask", send_task_pthread, 262144, SendThreadPrio, SendCore); if (recv_tid < 0) { printf("ERROR: Create send_task_pthread failed!\n"); return -1; } usleep(10); recv_tid = test_task_create("RcvTask", recv_task_pthread, 262144, RecvThreadPrio, RecvCore); if (recv_tid < 0) { printf("ERROR: Create recv_task_pthread failed!\n"); return -1; } pthread_join(recv_tid, NULL); pthread_join(send_tid, NULL); } munlockall(); return 0; }
编译得到二进制mq_task_switch,开始测试
./mq_task_switch 1 90 2 90 10000 1
输出结果
Final result, Min: 10208 ns, Cur: 11084 ns, Avg: 11113 ns, Max: 24791 ns
这里可以确定最大值是24791ns ,平时值是11113ns.
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <time.h> #include <sched.h> #include <sys/types.h> #include <unistd.h> //pipe() int main() { int x, i, fd[2], p[2]; int tv[2]; //用于进程间数据通信 char send = 's'; char receive; pipe(fd); pipe(p); pipe(tv); struct timeval tv_start,tv_end; struct sched_param param; param.sched_priority = 0; while ((x = fork()) == -1); if (x==0) { //子进程 sched_setscheduler(getpid(), SCHED_FIFO, ¶m); gettimeofday(&tv_start, NULL); write(tv[1], &tv_start, sizeof(tv_start));//保存数据,以便传递到父进程中进行计算 for (i = 0; i < 10000; i++) { read(fd[0], &receive, 1); //printf("Child read!\n"); write(p[1], &send, 1); //printf("Child write!\n"); } exit(0); } else { //父进程 sched_setscheduler(getpid(), SCHED_FIFO, ¶m); for (i = 0; i < 10000; i++) { write(fd[1], &send, 1); //printf("Parent write!\n"); read(p[0], &receive, 1); //printf("Parent read!\n"); } gettimeofday(&tv_end, NULL); } read(tv[0], &tv_start, sizeof(tv_start)); printf("Task Switch Time: %f us\n", (1000000*(tv_end.tv_sec-tv_start.tv_sec) + tv_end.tv_usec - tv_start.tv_usec)/20000.0); return 0; }
编译:
gcc task_switch_pipe.c -o task_switch_pipe
运行:
root@kylin:~# while((1));do ./task_switch_pipe ; done Task Switch Time: 7.925000 us Task Switch Time: 7.978100 us Task Switch Time: 7.542050 us Task Switch Time: 9.470300 us Task Switch Time: 7.518850 us Task Switch Time: 8.346700 us Task Switch Time: 8.005150 us Task Switch Time: 7.560750 us Task Switch Time: 7.625400 us Task Switch Time: 8.143850 us Task Switch Time: 6.504850 us Task Switch Time: 8.422350 us Task Switch Time: 8.029650 us Task Switch Time: 7.612400 us Task Switch Time: 7.706200 us Task Switch Time: 6.831750 us Task Switch Time: 6.637150 us Task Switch Time: 9.136150 us Task Switch Time: 7.259700 us Task Switch Time: 9.774700 us Task Switch Time: 6.781400 us Task Switch Time: 7.425350 us Task Switch Time: 7.738900 us Task Switch Time: 8.812100 us Task Switch Time: 7.271150 us Task Switch Time: 8.677250 us Task Switch Time: 7.514600 us Task Switch Time: 6.683600 us Task Switch Time: 7.421900 us Task Switch Time: 8.019550 us Task Switch Time: 8.250450 us Task Switch Time: 8.571800 us Task Switch Time: 7.957400 us Task Switch Time: 7.837050 us Task Switch Time: 7.161250 us Task Switch Time: 8.998750 us Task Switch Time: 7.160500 us Task Switch Time: 6.629200 us Task Switch Time: 7.082150 us Task Switch Time: 7.570450 us Task Switch Time: 8.423500 us Task Switch Time: 7.531950 us Task Switch Time: 7.856900 us Task Switch Time: 8.980750 us Task Switch Time: 7.072300 us Task Switch Time: 9.112650 us Task Switch Time: 7.871100 us Task Switch Time: 7.168500 us Task Switch Time: 7.774850 us Task Switch Time: 7.308050 us Task Switch Time: 6.532000 us Task Switch Time: 6.994950 us Task Switch Time: 7.805700 us Task Switch Time: 7.980750 us Task Switch Time: 7.990150 us Task Switch Time: 8.017550 us Task Switch Time: 6.781300 us Task Switch Time: 6.755500 us Task Switch Time: 6.418300 us Task Switch Time: 8.239600 us Task Switch Time: 8.661350 us Task Switch Time: 8.688850 us Task Switch Time: 7.245000 us Task Switch Time: 8.793250 us Task Switch Time: 7.628300 us Task Switch Time: 6.593100 us Task Switch Time: 8.685100 us Task Switch Time: 6.575550 us Task Switch Time: 8.380500 us Task Switch Time: 8.607750 us Task Switch Time: 6.581850 us Task Switch Time: 6.862200 us Task Switch Time: 6.401400 us Task Switch Time: 6.690150 us Task Switch Time: 7.945100 us Task Switch Time: 7.820750 us Task Switch Time: 7.935500 us Task Switch Time: 8.156100 us Task Switch Time: 7.198700 us Task Switch Time: 7.230950 us Task Switch Time: 7.623400 us Task Switch Time: 7.438650 us Task Switch Time: 7.172000 us Task Switch Time: 8.439000 us Task Switch Time: 6.927050 us Task Switch Time: 8.215500 us Task Switch Time: 7.361300 us Task Switch Time: 7.231800 us Task Switch Time: 7.303450 us Task Switch Time: 8.140700 us Task Switch Time: 8.331800 us Task Switch Time: 7.962950 us Task Switch Time: 6.757600 us Task Switch Time: 7.251000 us
这里测试最大延迟为9.774700 us
安装测试包
apt install rt-tests
测试命令:
pmqtest -p 90 -t 2 -i 1000 -l 7200000
测试结果
root@kylin:~# pmqtest -p 90 -t 2 -i 1000 -l 7200000 #0: ID2607, P90, CPU5, I1000; #1: ID2608, P90, CPU4, TO 0, Cycles 782505 #2: ID2609, P89, CPU6, I1500; #3: ID2610, P89, CPU7, TO 0, Cycles 524798 #1 -> #0, Min 3, Cur 6, Avg 6, Max 27 #3 -> #2, Min 3, Cur 6, Avg 7, Max 36
这里可以确定最小值3us,平均值7us,最大值36us
测试命令:
cyclictest -p 90 -m -c 0 -i 1000 -t 4 -l 7200000
测试结果:
root@kylin:~# cyclictest -p 90 -m -c 0 -i 1000 -t 4 -l 7200000 policy: fifo: loadavg: 2.61 1.48 0.68 1/288 257policy: fifo: loadavg: 2.91 2.71 2.14 1/285 2595 # /dev/cpu_dma_latency set to 0us T: 0 ( 2567) P:90 I:1000 C:1217830 Min: 4 Act: 4 Avg: 4 Max: 19 T: 1 ( 2568) P:90 I:1500 C: 811877 Min: 4 Act: 4 Avg: 4 Max: 15 T: 2 ( 2569) P:90 I:2000 C: 608904 Min: 4 Act: 5 Avg: 4 Max: 14 T: 3 ( 2570) P:90 I:2500 C: 487120 Min: 4 Act: 4 Avg: 4 Max: 13
这里可以确定最大延迟19us,平均延迟4us
当前测试结论如下:
3588任务上下文切换最大时间为:24.791us,通过pipe令牌测试最大延迟为9.774700 us
3588消息传递最大延迟为:36us
3588中断响应最大延迟为:19us
当前测试结果不代表实际测试结果,并且测试结果仅供参考,具体情况根据系统负载,硬件情况决定。