基于android 6.0, 分析bugreport过程
framework/native/cmds/bugreport/bugreport.cpp
framework/native/cmds/dumpstate/dumpstate.cpp
framework/native/cmds/dumpstate/utils.c
一、概述
通过adb命令可获取bugrepport信息,并输出到文件当前路径的bugreport.txt文件:
adb bugreport > bugreport.txt
对于Android系统调试分析,bugreport信息量非常之大,几乎涵盖整个系统各个层面内容,对于分析BUG是一大利器,本文先从从源码角度来分析一下Bugreport的实现原理。
二、原理分析
Android系统源码中framework/native/cmds/bugreport目录通过Android.mk定义了bugreport项目,在系统编译完成后会生成bugreport可执行文件,位于系统/system/bin/bugreport。当执行adb bugreport
时,便会调用这个可执行文件,进入bugreport.cpp中的main()方法。
2.1 bugreport.main
[-> bugreport.cpp]
int main() {
//启动dumpstate服务
property_set("ctl.start", "dumpstate");
//需要多次尝试,直到dumpstate服务启动完成,才能建立socket通信
int s;
for (int i = 0; i < 20; i++) {
s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
if (s >= 0)
break;
//休眠1s后再次尝试连接
sleep(1);
}
if (s == -1) {
printf("Failed to connect to dumpstate service: %s\n", strerror(errno));
return 1;
}
//当3分钟没有任何数据可读,则超时停止读取并退出。
//dumpstate服务中不存在大于1分钟的timetout,因而不可预见的超时的情况下留有很大的回旋余地。
struct timeval tv;
tv.tv_sec = 3 * 60;
tv.tv_usec = 0;
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
printf("WARNING: Cannot set socket timeout: %s\n", strerror(errno));
}
while (1) {
char buffer[65536];
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)));
if (bytes_read == 0) {
break;
} else if (bytes_read == -1) {
// EAGAIN意味着timeout,Bugreport读异常终止
if (errno == EAGAIN) {
errno = ETIMEDOUT;
}
break;
}
ssize_t bytes_to_send = bytes_read;
ssize_t bytes_written;
//不断循环得将读取数据输出到stdout
do {
bytes_written = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,
buffer + bytes_read - bytes_to_send, bytes_to_send));
if (bytes_written == -1) {
return 1; //将数据无法写入stdout
}
bytes_to_send -= bytes_written;
} while (bytes_written != 0 && bytes_to_send > 0);
}
close(s);
return 0;
}
property_set(“ctl.start”, “dumpstate”)会触发init进程,来fork进程/system/bin/dumpstate
, 作为dumpstate服务的进程. Bugreport再通过socket建立于dumpstate的通信,这个过程会尝试20次socket连接建立直到成功连接。 在socket通道中如果持续3分钟没有任何数据可读,则超时停止读取并退出。由于dumpstate服务中不存在大于1分钟的timetout,因而不可预见的超时的情况下留有很大的回旋余地。
当从socket读取到数据后,写入到标准时输出或者重定向到文件。可见bugreport数据的来源都是dumpstate服务,那么接下来去看看dumpstate服务的工作。
2.2 dumpstate.main
[-> dumpstate.cpp]
int main(int argc, char *argv[]) {
struct sigaction sigact;
int do_add_date = 0;
int do_vibrate = 1;
char* use_outfile = 0;
int use_socket = 0;
int do_fb = 0;
int do_broadcast = 0;
if (getuid() != 0) {
//兼容性考虑,旧版本支持直接调用dumpstate命令,新版本通过调用/system/bin/bugreport来替代。
//当检测到直接调用,则强制执行bugreport命令。
return execl("/system/bin/bugreport", "/system/bin/bugreport", NULL);
}
ALOGI("begin\n");
//清空句柄SIGPIPE
memset(&sigact, 0, sizeof(sigact));
sigact.sa_handler = sigpipe_handler;
sigaction(SIGPIPE, &sigact, NULL);
//提高当前进程的优先级,防止被OOM Killer杀死
setpriority(PRIO_PROCESS, 0, -20);
FILE *oom_adj = fopen("/proc/self/oom_adj", "we");
if (oom_adj) {
fputs("-17", oom_adj);
fclose(oom_adj);
}
//参数解析
int c;
while ((c = getopt(argc, argv, "dho:svqzpB")) != -1) {
switch (c) {
case 'd': do_add_date = 1; break;
case 'o': use_outfile = optarg; break;
case 's': use_socket = 1; break;
case 'v': break; // compatibility no-op
case 'q': do_vibrate = 0; break;
case 'p': do_fb = 1; break;
case 'B': do_broadcast = 1; break;
case '?': printf("\n");
case 'h':
usage();
exit(1);
}
}
//建立socket
if (use_socket) {
redirect_to_socket(stdout, "dumpstate");
}
//打开vibrator
FILE *vibrator = 0;
if (do_vibrate) {
vibrator = fopen("/sys/class/timed_output/vibrator/enable", "we");
if (vibrator) {
vibrate(vibrator, 150);
}
}
//读取/proc/cmdline
FILE *cmdline = fopen("/proc/cmdline", "re");
if (cmdline != NULL) {
fgets(cmdline_buf, sizeof(cmdline_buf), cmdline);
fclose(cmdline);
}
//收集虚拟机和native进程的stack traces(需要root权限)
dump_traces_path = dump_traces();
//获取tombstone文件描述符
get_tombstone_fds(tombstone_data);
//确保capabilities
if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
return -1;
}
//切换到非root用户和组,在切换之前都是处于root权限
gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW,
AID_MOUNT, AID_INET, AID_NET_BW_STATS };
if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
ALOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
return -1;
}
if (setgid(AID_SHELL) != 0) {
ALOGE("Unable to setgid, aborting: %s\n", strerror(errno));
return -1;
}
if (setuid(AID_SHELL) != 0) {
ALOGE("Unable to setuid, aborting: %s\n", strerror(errno));
return -1;
}
struct __user_cap_header_struct capheader;
struct __user_cap_data_struct capdata[2];
memset(&capheader, 0, sizeof(capheader));
memset(&capdata, 0, sizeof(capdata));
capheader.version = _LINUX_CAPABILITY_VERSION_3;
capheader.pid = 0;
capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
capdata[0].inheritable = 0;
capdata[1].inheritable = 0;
if (capset(&capheader, &capdata[0]) < 0) {
ALOGE("capset failed: %s\n", strerror(errno));
return -1;
}
//如果需要,则重定向输出
char path[PATH_MAX], tmp_path[PATH_MAX];
pid_t gzip_pid = -1;
if (!use_socket && use_outfile) {
strlcpy(path, use_outfile, sizeof(path));
if (do_add_date) {
char date[80];
time_t now = time(NULL);
strftime(date, sizeof(date), "-%Y-%m-%d-%H-%M-%S", localtime(&now));
strlcat(path, date, sizeof(path));
}
if (do_fb) {
strlcpy(screenshot_path, path, sizeof(screenshot_path));
strlcat(screenshot_path, ".png", sizeof(screenshot_path));
}
strlcat(path, ".txt", sizeof(path));
strlcpy(tmp_path, path, sizeof(tmp_path));
strlcat(tmp_path, ".tmp", sizeof(tmp_path));
redirect_to_file(stdout, tmp_path);
}
//这里是真正干活的地方 【见小节 2.3】
dumpstate();
//通过震动提醒已完成所有dump操作
if (vibrator) {
for (int i = 0; i < 3; i++) {
vibrate(vibrator, 75);
usleep((75 + 50) * 1000);
}
fclose(vibrator);
}
//等待gzip的完成,等进程退出时则会被杀
if (gzip_pid > 0) {
fclose(stdout);
waitpid(gzip_pid, NULL, 0);
}
//重命名.tmp文件到最终位置
if (use_outfile && rename(tmp_path, path)) {
fprintf(stderr, "rename(%s, %s): %s\n", tmp_path, path, strerror(errno));
}
//通过发送广播告知ActivityManager已完成bugreport操作
if (do_broadcast && use_outfile && do_fb) {
run_command(NULL, 5, "/system/bin/am", "broadcast", "--user", "0",
"-a", "android.intent.action.BUGREPORT_FINISHED",
"--es", "android.intent.extra.BUGREPORT", path,
"--es", "android.intent.extra.SCREENSHOT", screenshot_path,
"--receiver-permission", "android.permission.DUMP", NULL);
}
ALOGI("done\n");
return 0;
}
整个过程的工作流程:
- 提高执行dumpsate所在进程的优先级,防止被OOM Killer杀死;
- 参数解析,可通过命令
adb shell dumpstate -h
查看dumpstate命令所支持的参数; - 打开vibrator,用于在执行bugreport时,手机会先震动一下用于提醒开始抓取系统信息;
- 通过dump_traces()来完成收集虚拟机和native进程的stack traces;
- 通过get_tombstone_fds来获取tombstone文件描述符;
- 开始执行切换到非root用户和组,在这之前的执行都处于root权限;
- 执行dumpstate(),这里是真正干活的地方;
- 再次通过震动以提醒dump操作执行完成;
- 发送广播,告知ActivityManager已完成bugreport操作。
接下来就重点说说dumpstate()
功能:
2.3 dumpstate()
该方法负责整个bugreport内容输出的最为核心的功能。
[-> dumpstate.cpp ]
static void dumpstate() {
...
property_get("ro.build.display.id", build, "(unknown)");
property_get("ro.build.fingerprint", fingerprint, "(unknown)");
property_get("ro.build.type", build_type, "(unknown)");
property_get("ro.baseband", radio, "(unknown)");
property_get("ro.bootloader", bootloader, "(unknown)");
property_get("gsm.operator.alpha", network, "(unknown)");
strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now));
//开头信息
printf("========================================================\n");
printf("== dumpstate: %s\n", date);
printf("========================================================\n");
printf("\n");
printf("Build: %s\n", build);
printf("Build fingerprint: '%s'\n", fingerprint);
printf("Bootloader: %s\n", bootloader);
printf("Radio: %s\n", radio);
printf("Network: %s\n", network);
printf("Kernel: "); dump_file(NULL, "/proc/version");
printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
printf("\n");
//记录系统运行时长和休眠时长
run_command("UPTIME", 10, "uptime", NULL);
//输出mmcblk0设备信息
dump_files("UPTIME MMC PERF", mmcblk0, skip_not_stat, dump_stat_from_fd);
dump_file("MEMORY INFO", "/proc/meminfo");
run_command("CPU INFO", 10, "top", "-n", "1", "-d", "1", "-m", "30", "-t", NULL);
run_command("PROCRANK", 20, "procrank", NULL);
dump_file("VIRTUAL MEMORY STATS", "/proc/vmstat");
dump_file("VMALLOC INFO", "/proc/vmallocinfo");
dump_file("SLAB INFO", "/proc/slabinfo");
dump_file("ZONEINFO", "/proc/zoneinfo");
dump_file("PAGETYPEINFO", "/proc/pagetypeinfo");
dump_file("BUDDYINFO", "/proc/buddyinfo");
dump_file("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
dump_file("KERNEL WAKE SOURCES", "/d/wakeup_sources");
dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
dump_file("KERNEL SYNC", "/d/sync");
run_command("PROCESSES", 10, "ps", "-P", NULL);
run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
run_command("PROCESSES (SELINUX LABELS)", 10, "ps", "-Z", NULL);
run_command("LIBRANK", 10, "librank", NULL);
//输出kernel log
do_dmesg();
//所有已打开文件
run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
//遍历所有进程的show map
for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
//显示所有线程的blocked位置
for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
//SYSTEM LOG
timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash");
if (timeout < 20000) {
timeout = 20000;
}
run_command("SYSTEM LOG", timeout / 1000, "logcat", "-v", "threadtime", "-d", "*:v", NULL);
//EVENT LOG
timeout = logcat_timeout("events");
if (timeout < 20000) {
timeout = 20000;
}
run_command("EVENT LOG", timeout / 1000, "logcat", "-b", "events", "-v", "threadtime", "-d", "*:v", NULL);
//RADIO LOG
timeout = logcat_timeout("radio");
if (timeout < 20000) {
timeout = 20000;
}
run_command("RADIO LOG", timeout / 1000, "logcat", "-b", "radio", "-v", "threadtime", "-d", "*:v", NULL);
//Log统计信息
run_command("LOG STATISTICS", 10, "logcat", "-b", "all", "-S", NULL);
//输出当前虚拟机和native进程的vm traces
if (dump_traces_path != NULL) {
dump_file("VM TRACES JUST NOW", dump_traces_path);
}
//输出上次发生ANR时vm traces,即路径/data/anr/traces.txt
struct stat st;
char anr_traces_path[PATH_MAX];
property_get("dalvik.vm.stack-trace-file", anr_traces_path, "");
if (!anr_traces_path[0]) {
printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
} else {
int fd = TEMP_FAILURE_RETRY(open(anr_traces_path,
O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
if (fd < 0) {
printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno));
} else {
dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path, fd);
}
}
//输出慢操作的vm traces,例如/data/anr/slow1.txt
if (anr_traces_path[0] != 0) {
int tail = strlen(anr_traces_path)-1;
while (tail > 0 && anr_traces_path[tail] != '/') {
tail--;
}
int i = 0;
while (1) {
//例如trace文件为/data/anr/slow1.txt
sprintf(anr_traces_path+tail+1, "slow%02d.txt", i);
if (stat(anr_traces_path, &st)) {
break;
}
dump_file("VM TRACES WHEN SLOW", anr_traces_path);
i++;
}
}
//输出tombstone信息,NUM_TOMBSTONES=10,例如/data/tombstones/tombstone_1
int dumped = 0;
for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
if (tombstone_data[i].fd != -1) {
dumped = 1;
dump_file_from_fd("TOMBSTONE", tombstone_data[i].name, tombstone_data[i].fd);
tombstone_data[i].fd = -1;
}
}
if (!dumped) {
printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
}
dump_file("NETWORK DEV INFO", "/proc/net/dev");
dump_file("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
dump_file("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
dump_file("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
//输出上次的kernel log
if (!stat(PSTORE_LAST_KMSG, &st)) {
//文件为/sys/fs/pstore/console-ramoops
dump_file("LAST KMSG", PSTORE_LAST_KMSG);
} else {
//文件为/proc/last_kmsg
dump_file("LAST KMSG", "/proc/last_kmsg");
}
//输出上次 logcat,内核必须设置CONFIG_PSTORE_PMSG
run_command("LAST LOGCAT", 10, "logcat", "-L", "-v", "threadtime",
"-b", "all", "-d", "*:v", NULL);
//wifi驱动/固件 以及ip相关信息
run_command("NETWORK INTERFACES", 10, "ip", "link", NULL);
run_command("IPv4 ADDRESSES", 10, "ip", "-4", "addr", "show", NULL);
run_command("IPv6 ADDRESSES", 10, "ip", "-6", "addr", "show", NULL);
run_command("IP RULES", 10, "ip", "rule", "show", NULL);
run_command("IP RULES v6", 10, "ip", "-6", "rule", "show", NULL);
dump_route_tables();
run_command("ARP CACHE", 10, "ip", "-4", "neigh", "show", NULL);
run_command("IPv6 ND CACHE", 10, "ip", "-6", "neigh", "show", NULL);
run_command("IPTABLES", 10, SU_PATH, "root", "iptables", "-L", "-nvx", NULL);
run_command("IP6TABLES", 10, SU_PATH, "root", "ip6tables", "-L", "-nvx", NULL);
run_command("IPTABLE NAT", 10, SU_PATH, "root", "iptables", "-t", "nat", "-L", "-nvx", NULL);
run_command("IPTABLE RAW", 10, SU_PATH, "root", "iptables", "-t", "raw", "-L", "-nvx", NULL);
run_command("IP6TABLE RAW", 10, SU_PATH, "root", "ip6tables", "-t", "raw", "-L", "-nvx", NULL);
run_command("WIFI NETWORKS", 20, SU_PATH, "root", "wpa_cli", "IFNAME=wlan0", "list_networks", NULL);
//中断向量表
dump_file("INTERRUPTS (1)", "/proc/interrupts");
run_command("NETWORK DIAGNOSTICS", 10, "dumpsys", "connectivity", "--diag", NULL);
//中断向量表(二次输出)
dump_file("INTERRUPTS (2)", "/proc/interrupts");
//获取properties属性值
print_properties();
run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
//可用空间
run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL);
run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
//背光信息
printf("------ BACKLIGHTS ------\n");
printf("LCD brightness="); dump_file(NULL, "/sys/class/leds/lcd-backlight/brightness");
printf("Button brightness="); dump_file(NULL, "/sys/class/leds/button-backlight/brightness");
printf("Keyboard brightness="); dump_file(NULL, "/sys/class/leds/keyboard-backlight/brightness");
printf("ALS mode="); dump_file(NULL, "/sys/class/leds/lcd-backlight/als");
printf("LCD driver registers:\n"); dump_file(NULL, "/sys/class/leds/lcd-backlight/registers");
printf("\n");
//Binder相关
dump_file("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
dump_file("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
dump_file("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
dump_file("BINDER STATS", "/sys/kernel/debug/binder/stats");
dump_file("BINDER STATE", "/sys/kernel/debug/binder/state");
printf("========================================================\n");
printf("== Board\n");
printf("========================================================\n");
dumpstate_board(); printf("\n");
//输出framework各种服务的dumpsys信息
printf("========================================================\n");
printf("== Android Framework Services\n");
printf("========================================================\n");
run_command("DUMPSYS", 60, "dumpsys", NULL); //很耗时则timeout=60s
printf("========================================================\n");
printf("== Checkins\n");
printf("========================================================\n");
run_command("CHECKIN BATTERYSTATS", 30, "dumpsys", "batterystats", "-c", NULL);
run_command("CHECKIN MEMINFO", 30, "dumpsys", "meminfo", "--checkin", NULL);
run_command("CHECKIN NETSTATS", 30, "dumpsys", "netstats", "--checkin", NULL);
run_command("CHECKIN PROCSTATS", 30, "dumpsys", "procstats", "-c", NULL);
run_command("CHECKIN USAGESTATS", 30, "dumpsys", "usagestats", "-c", NULL);
run_command("CHECKIN PACKAGE", 30, "dumpsys", "package", "--checkin", NULL);
//输出当前 运行中activity/service/provider信息
printf("========================================================\n");
printf("== Running Application Activities\n");
printf("========================================================\n");
run_command("APP ACTIVITIES", 30, "dumpsys", "activity", "all", NULL);
printf("========================================================\n");
printf("== Running Application Services\n");
printf("========================================================\n");
run_command("APP SERVICES", 30, "dumpsys", "activity", "service", "all", NULL);
printf("========================================================\n");
printf("== Running Application Providers\n");
printf("========================================================\n");
run_command("APP SERVICES", 30, "dumpsys", "activity", "provider", "all", NULL);
printf("========================================================\n");
printf("== dumpstate: done\n");
printf("========================================================\n");
}
该方法涉及run_command其他几个方法见下方:
2.3.1 run_command()
[-> utils.c]
int run_command(const char *title, int timeout_seconds, const char *command, ...) {
fflush(stdout);
uint64_t start = nanotime();
//通过fork创建子进程
pid_t pid = fork();
if (pid < 0) {
printf("*** fork: %s\n", strerror(errno));
return pid;
}
//子进程执行
if (pid == 0) {
const char *args[1024] = {command};
size_t arg;
//确保dumpstate结束后能关闭子进程
prctl(PR_SET_PDEATHSIG, SIGKILL);
struct sigaction sigact;
memset(&sigact, 0, sizeof(sigact));
sigact.sa_handler = SIG_IGN;
//忽略SIGPIPE
sigaction(SIGPIPE, &sigact, NULL);
va_list ap;
va_start(ap, command);
if (title) printf("------ %s (%s", title, command);
for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
args[arg] = va_arg(ap, const char *);
if (args[arg] == NULL) break;
if (title) printf(" %s", args[arg]);
}
if (title) printf(") ------\n");
fflush(stdout);
//执行命令
execvp(command, (char**) args);
printf("*** exec(%s): %s\n", command, strerror(errno));
fflush(stdout);
_exit(-1); //进程退出
}
//父进程执行,主要处理子进程退出
int status;
bool ret = waitpid_with_timeout(pid, timeout_seconds, &status);
uint64_t elapsed = nanotime() - start;
if (!ret) {
if (errno == ETIMEDOUT) {
printf("*** %s: Timed out after %.3fs (killing pid %d)\n", command,
(float) elapsed / NANOS_PER_SEC, pid);
} else {
printf("*** %s: Error after %.4fs (killing pid %d)\n", command,
(float) elapsed / NANOS_PER_SEC, pid);
}
kill(pid, SIGTERM);
if (!waitpid_with_timeout(pid, 5, NULL)) {
kill(pid, SIGKILL);
if (!waitpid_with_timeout(pid, 5, NULL)) {
printf("*** %s: Cannot kill %d even with SIGKILL.\n", command, pid);
}
}
return -1;
}
if (WIFSIGNALED(status)) {
printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
} else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
}
if (title) printf("[%s: %.3fs elapsed]\n\n", command, (float)elapsed / NANOS_PER_SEC);
return status;
}
功能是fork子进程并等待它执行完成,或者超时退出。当命令title
不为空时,每次输出结果,都分别以下面作为开头和结尾:
------ (<command>) ------
[<command>: <执行时长> elapsed]
</code></pre>
<h4>2.3.2 dump_file() </h4>
<p>[-> utils.c]</p>
<pre><code>int dump_file(const char *title, const char *path) {
//尝试打开文件
int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
if (fd < 0) {
//无法打开文件时,则输出如下信息
int err = errno;
if (title) printf("------ %s (%s) ------\n", title, path);
printf("*** %s: %s\n", path, strerror(err));
if (title) printf("\n");
return -1;
}
//输出文件内容
return _dump_file_from_fd(title, path, fd);
}
</code></pre>
<p>当可以正确打开文件时,则执行_dump_file_from_fd,输出文件内容</p>
<pre><code>static int _dump_file_from_fd(const char *title, const char *path, int fd) {
if (title) printf("------ %s (%s", title, path);
if (title) {
struct stat st;
//文件路径为/proc/或者/sys/
if (memcmp(path, "/proc/", 6) && memcmp(path, "/sys/", 5) && !fstat(fd, &st)) {
char stamp[80];
time_t mtime = st.st_mtime; //文件上次修改时间
strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
printf(": %s", stamp);
}
printf(") ------\n");
}
bool newline = false;
fd_set read_set;
struct timeval tm;
while (1) {
FD_ZERO(&read_set);
FD_SET(fd, &read_set);
//30s无数据可读则超时
tm.tv_sec = 30;
tm.tv_usec = 0;
uint64_t elapsed = nanotime();
int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, NULL, NULL, &tm));
if (ret == -1) {
printf("*** %s: select failed: %s\n", path, strerror(errno));
newline = true;
break;
} else if (ret == 0) {
elapsed = nanotime() - elapsed;
printf("*** %s: Timed out after %.3fs\n", path,
(float) elapsed / NANOS_PER_SEC);
newline = true;
break;
} else {
char buffer[65536];
// 读取数据
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
if (bytes_read > 0) {
fwrite(buffer, bytes_read, 1, stdout);
newline = (buffer[bytes_read-1] == '\n');
} else {
if (bytes_read == -1) {
printf("*** %s: Failed to read from fd: %s", path, strerror(errno));
newline = true;
}
break;
}
}
}
close(fd);
if (!newline) printf("\n");
if (title) printf("\n");
return 0;
}
</code></pre>
<p>当打不开文件或者出错则输出:</p>
<pre><code>------ <title> (<path>) ------
*** <path>: <err>
</code></pre>
<p>当文件路径为/proc/或者/sys/,则输出时间/文件上次修改时间:</p>
<pre><code>------ <title> (<path>: <文件修改时间>) ------
</code></pre>
<h4>2.3.3 dump_files() </h4>
<p>dump_files(“UPTIME MMC PERF”, mmcblk0, skip_not_stat, dump_stat_from_fd);</p>
<p>其中skip_not_stat是指忽略mmcblk0目录下的非stat文件,dump_files该方法遍历输出mmcblk0(即”/sys/block/mmcblk0/”)目录下所有stat文件,具体的输出调用dump_stat_from_fd方法来完成,该方法输出每个分区的读写速度:</p>
<pre><code>static int dump_stat_from_fd(const char *title __unused, const char *path, int fd) {
unsigned long fields[11], read_perf, write_perf;
bool z;
char *cp, *buffer = NULL;
size_t i = 0;
FILE *fp = fdopen(fd, "rb"); //打开文件
getline(&buffer, &i, fp);
fclose(fp);
if (!buffer) {
return -errno;
}
i = strlen(buffer);
while ((i > 0) && (buffer[i - 1] == '\n')) {
buffer[--i] = '\0';
}
if (!*buffer) {
free(buffer);
return 0;
}
z = true;
for (cp = buffer, i = 0; i < (sizeof(fields) / sizeof(fields[0])); ++i) {
fields[i] = strtol(cp, &cp, 0);
if (fields[i] != 0) {
z = false;
}
}
if (z) { /* never accessed */
free(buffer);
return 0;
}
if (!strncmp(path, mmcblk0, sizeof(mmcblk0) - 1)) {
path += sizeof(mmcblk0) - 1;
}
//例如输出/sys/block/mmcblk0/mmcblk0p13/stat内容
printf("%s: %s\n", path, buffer);
free(buffer);
read_perf = 0;
if (fields[3]) {
//计算读的性能
read_perf = 512 * fields[2] / fields[3];
}
write_perf = 0;
if (fields[7]) {
//计算写的性能
write_perf = 512 * fields[6] / fields[7];
}
printf("%s: read: %luKB/s write: %luKB/s\n", path, read_perf, write_perf);
//worst_write_perf默认值为20000kb/s
if ((write_perf > 1) && (write_perf < worst_write_perf)) {
worst_write_perf = write_perf;
}
return 0;
}
</code></pre>
<p>例如:stat文件共有11个数据:</p>
<pre><code>mmcblk0p13/stat: 15 369 100 10 57 7239 5000 250 0 900 2610
</code></pre>
<p>则mmcblk0p13/stat的read_perf = 512* 100/10 = 5120KB/s, write_perf= 512* 5000/250 = 10240KB/s</p>
<h4>2.3.4 dump_traces() </h4>
<p>dump虚拟机和native的stack traces,并返回trace文件位置</p>
<pre><code>const char *dump_traces() {
const char* result = NULL;
char traces_path[PROPERTY_VALUE_MAX] = "";
//traces_path等于/data/anr/traces.txt
property_get("dalvik.vm.stack-trace-file", traces_path, "");
if (!traces_path[0]) return NULL;
char anr_traces_path[PATH_MAX];
将traces_path文件名拷贝到anr_traces_path
strlcpy(anr_traces_path, traces_path, sizeof(anr_traces_path));
//连接后,anr_traces_path变成了/data/anr/traces.txt.anr
strlcat(anr_traces_path, ".anr", sizeof(anr_traces_path));
//文件重命名, 将/data/anr/traces.txt文件重命名为/data/anr/traces.txt.anr
if (rename(traces_path, anr_traces_path) && errno != ENOENT) {
return NULL; //没有权限重命令
}
char anr_traces_dir[PATH_MAX];
strlcpy(anr_traces_dir, traces_path, sizeof(anr_traces_dir));
// *slash为/traces.txt
char *slash = strrchr(anr_traces_dir, '/');
if (slash != NULL) {
*slash = '\0';
//创建文件夹/data/anr/traces.txt/
if (!mkdir(anr_traces_dir, 0775)) {
chown(anr_traces_dir, AID_SYSTEM, AID_SYSTEM);
chmod(anr_traces_dir, 0775);
if (selinux_android_restorecon(anr_traces_dir, 0) == -1) {
fprintf(stderr, "restorecon failed for %s: %s\n", anr_traces_dir, strerror(errno));
}
}
}
//创建一个新的空文件/data/anr/traces.txt
int fd = TEMP_FAILURE_RETRY(open(traces_path, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
0666)); /* -rw-rw-rw- */
int chmod_ret = fchmod(fd, 0666);
DIR *proc = opendir("/proc");
//当进程完成dump操作时,通过inotify来通知
int ifd = inotify_init();
int wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
struct dirent *d;
int dalvik_found = 0;
while ((d = readdir(proc))) {
int pid = atoi(d->d_name);
if (pid <= 0) continue;
char path[PATH_MAX];
char data[PATH_MAX];
snprintf(path, sizeof(path), "/proc/%d/exe", pid);
ssize_t len = readlink(path, data, sizeof(data) - 1);
if (len <= 0) {
continue;
}
data[len] = '\0';
if (!strncmp(data, "/system/bin/app_process", strlen("/system/bin/app_process"))) {
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
int cfd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC));
len = read(cfd, data, sizeof(data) - 1);
close(cfd);
if (len <= 0) {
continue;
}
data[len] = '\0';
//略过zygote,并不输出它的栈信息
if (!strncmp(data, "zygote", strlen("zygote"))) {
continue;
}
++dalvik_found;
uint64_t start = nanotime();
if (kill(pid, SIGQUIT)) {
fprintf(stderr, "kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
continue;
}
//等待来自inotify的可写关闭的通知
struct pollfd pfd = { ifd, POLLIN, 0 };
int ret = poll(&pfd, 1, 5000); /* 5s超时*/
if (ret < 0) {
fprintf(stderr, "poll: %s\n", strerror(errno));
} else if (ret == 0) {
fprintf(stderr, "warning: timed out dumping pid %d\n", pid);
} else {
struct inotify_event ie;
read(ifd, &ie, sizeof(ie));
}
if (lseek(fd, 0, SEEK_END) < 0) {
fprintf(stderr, "lseek: %s\n", strerror(errno));
} else {
dprintf(fd, "[dump dalvik stack %d: %.3fs elapsed]\n",
pid, (float)(nanotime() - start) / NANOS_PER_SEC);
}
} else if (should_dump_native_traces(data)) {
//native进程trace
if (lseek(fd, 0, SEEK_END) < 0) {
fprintf(stderr, "lseek: %s\n", strerror(errno));
} else {
static uint16_t timeout_failures = 0;
uint64_t start = nanotime();
/* If 3 backtrace dumps fail in a row, consider debuggerd dead. */
if (timeout_failures == 3) {
dprintf(fd, "too many stack dump failures, skipping...\n");
// 超时时长为20s
} else if (dump_backtrace_to_file_timeout(pid, fd, 20) == -1) {
dprintf(fd, "dumping failed, likely due to a timeout\n");
timeout_failures++;
} else {
timeout_failures = 0;
}
dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n",
pid, (float)(nanotime() - start) / NANOS_PER_SEC);
}
}
}
static char dump_traces_path[PATH_MAX];
//将/data/anr/tracex.txt字节拷贝到dump_traces_path
strlcpy(dump_traces_path, traces_path, sizeof(dump_traces_path));
//此时dump_traces_path就变成了/data/anr/tracex.txt.bugreport
strlcat(dump_traces_path, ".bugreport", sizeof(dump_traces_path));
if (rename(traces_path, dump_traces_path)) {
goto error_close_ifd;
}
result = dump_traces_path;
//再将/data/anr/traces.txt.anr 重命名回到/data/anr/traces.txt
rename(anr_traces_path, traces_path);
...
return result;
}
</code></pre>
<p>此处有多次文件名的拷贝/连接/重命名操作, 主要逻辑如下:</p>
<ol>
<li>首先,/data/anr/tracex.txt文件重命名为/data/anr/traces.txt.anr, 这样可以保护上次anr信息;</li>
<li>然后,bugreport输出的trace内容输出到/data/anr/tracex.txt文件, 然后再把该文件重命名为/data/anr/tracex.txt.bugreport;</li>
<li>最后,将/data/anr/traces.txt.anr文件名改为/data/anr/tracex.txt.</li>
</ol>
<p>其中,整个过程的效果就等价于将bugreport过程抓取的traces输出到/data/anr/tracex.txt.bugreport文件.</p>
<p>dump_traces主要完成如下两个功能的输出:</p>
<ul>
<li>输出Java进程的trace是通过发送signal 3来dump相应信息。</li>
<li>输出native进程的trace是通过dump_backtrace_to_file_timeout,并且超时时长为20s;</li>
</ul>
<h4>2.3.5 do_dmesg() </h4>
<pre><code>void do_dmesg() {
printf("------ KERNEL LOG (dmesg) ------\n");
//获取kernel buffer的大小
int size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
if (size <= 0) {
printf("Unexpected klogctl return value: %d\n\n", size);
return;
}
char *buf = (char *) malloc(size + 1);
if (buf == NULL) {
printf("memory allocation failed\n\n");
return;
}
//获取kernel log
int retval = klogctl(KLOG_READ_ALL, buf, size);
if (retval < 0) {
printf("klogctl failure\n\n");
free(buf);
return;
}
buf[retval] = '\0';
printf("%s\n\n", buf);
free(buf);
return;
}
</code></pre>
<h3>2.4 总结 </h3>
<p>bugreport通过socket与dumpstate服务建立通信,在dumpstate.cpp中的dumpstate()方法完成核心功能,该功能依次输出内容项, 主要分为5大类:</p>
<ol>
<li> <strong>current log</strong>: kernel,system, event, radio;</li>
<li> <strong>last log</strong>: kernel, system, radio;</li>
<li> <strong>vm traces</strong>: just now, last ANR, tombstones</li>
<li> <strong>dumpsys</strong>: all, checkin, app</li>
<li> <strong>system info</strong>:cpu, memory, io等</li>
</ol>
<p>从bugreport内容的输出顺序的角度,再详细列举其内容:</p>
<ol>
<li>系统build以及运行时长等相关信息;</li>
<li>内存/CPU/进程等信息;</li>
<li> <code>kernel log</code>;</li>
<li>lsof、map及Wait-Channels;</li>
<li> <code>system log</code>;</li>
<li> <code>event log</code>;</li>
<li>radio log;</li>
<li> <code>vm traces</code>:
<ol>
<li>VM TRACES JUST NOW (/data/anr/traces.txt.bugreport) (抓bugreport时主动触发)</li>
<li>VM TRACES AT LAST ANR (/data/anr/traces.txt) (存在则输出)</li>
<li>TOMBSTONE (/data/tombstones/tombstone_xx) (存在这输出)</li>
</ol> </li>
<li>network相关信息;</li>
<li> <code>last kernel log</code>;</li>
<li> <code>last system log</code>;</li>
<li>ip相关信息;</li>
<li>中断向量表</li>
<li>property以及fs等信息</li>
<li>last radio log;</li>
<li>Binder相关信息;</li>
<li>dumpsys all:</li>
<li>dumpsys checkin相关:</li>
</ol>
<pre><code>* dumpsys batterystats电池统计;
* dumpsys meminfo内存
* dumpsys netstats网络统计;
* dumpsys procstats进程统计;
* dumpsys usagestats使用情况;
* dumpsys package.
</code></pre>
<ol start="19">
<li>dumpsys app相关</li>
</ol>
<pre><code>* dumpsys activity;
* dumpsys activity service all;
* dumpsys activity provider all.
</code></pre>
<p><strong>Tips</strong>: bugreport几乎涵盖整个系统信息,内容非常长,每一个子项都以<code>------ xxx ------</code>开头。 例如APP ACTIVITIES的开头便是 <code>------ APP ACTIVITIES (dumpsys activity all) ------</code>,其中括号内的便是输出该信息指令,即<code>dumpsys activity all</code>,还有可能是内容所在节点,各个子项目类似的规律,看完前面的源码分析过程,相信你肯定能明白。下面一篇文章再进一步从bugreport内容的角度来说明其寓意。</p>
</article>
</div>
</div>
</div>
<!--PC和WAP自适应版-->
<div id="SOHUCS" sid="1493609632113909760"></div>
<script type="text/javascript" src="/views/front/js/chanyan.js"></script>
<!-- 文章页-底部 动态广告位 -->
<div class="youdao-fixed-ad" id="detail_ad_bottom"></div>
</div>
<div class="col-md-3">
<div class="row" id="ad">
<!-- 文章页-右侧1 动态广告位 -->
<div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
<div class="youdao-fixed-ad" id="detail_ad_1"> </div>
</div>
<!-- 文章页-右侧2 动态广告位 -->
<div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
<div class="youdao-fixed-ad" id="detail_ad_2"></div>
</div>
<!-- 文章页-右侧3 动态广告位 -->
<div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
<div class="youdao-fixed-ad" id="detail_ad_3"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container">
<h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(调试系列1:bugreport源码篇)</h4>
<div id="paradigm-article-related">
<div class="recommend-post mb30">
<ul class="widget-links">
<li><a href="/article/1889955746615980032.htm"
title="[HOT 100] 1234. 替换子串得到平衡字符串" target="_blank">[HOT 100] 1234. 替换子串得到平衡字符串</a>
<span class="text-muted">水蓝烟雨</span>
<a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/HOT/1.htm">HOT</a><a class="tag" taget="_blank" href="/search/100/1.htm">100</a>
<div>文章目录1.题目链接2.题目描述3.题目示例4.解题思路5.题解代码6.复杂度分析1.题目链接1234.替换子串得到平衡字符串-力扣(LeetCode)2.题目描述有一个只含有'Q','W','E','R'四种字符,且长度为n的字符串。假如在该字符串中,这四个字符都恰好出现n/4次,那么它就是一个「平衡字符串」。给你一个这样的字符串s,请通过「替换一个子串」的方式,使原字符串s变成一个「平衡字符串</div>
</li>
<li><a href="/article/1889955493313572864.htm"
title="leetcode_深度搜索和广度搜索 104. 二叉树的最大深度" target="_blank">leetcode_深度搜索和广度搜索 104. 二叉树的最大深度</a>
<span class="text-muted">MiyamiKK57</span>
<a class="tag" taget="_blank" href="/search/leetcode/1.htm">leetcode</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E8%81%8C%E5%9C%BA%E5%92%8C%E5%8F%91%E5%B1%95/1.htm">职场和发展</a>
<div>104.二叉树的最大深度二叉树的最大深度是指从根节点到最远叶子节点的最长路径上的节点数。#Definitionforabinarytreenode.#classTreeNode(object):#def__init__(self,val=0,left=None,right=None):#self.val=val#self.left=left#self.right=rightclassSolutio</div>
</li>
<li><a href="/article/1889955494395703296.htm"
title="leetcode_深度搜索和广度搜索 112. 路径总和" target="_blank">leetcode_深度搜索和广度搜索 112. 路径总和</a>
<span class="text-muted">MiyamiKK57</span>
<a class="tag" taget="_blank" href="/search/leetcode/1.htm">leetcode</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E8%81%8C%E5%9C%BA%E5%92%8C%E5%8F%91%E5%B1%95/1.htm">职场和发展</a>
<div>112.路径总和给你二叉树的根节点root和一个表示目标和的整数targetSum。判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和targetSum。如果存在,返回true;否则,返回false。叶子节点是指没有子节点的节点。1.深度优先搜索(DFS)首先判断根节点是否为空。如果为空,则返回False然后检查当前节点是否是叶子节点(即同时没有左右子树)。如果是叶子节点</div>
</li>
<li><a href="/article/1889955490239148032.htm"
title="leetcode_双指针 125.验证回文串" target="_blank">leetcode_双指针 125.验证回文串</a>
<span class="text-muted">MiyamiKK57</span>
<a class="tag" taget="_blank" href="/search/leetcode/1.htm">leetcode</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E8%81%8C%E5%9C%BA%E5%92%8C%E5%8F%91%E5%B1%95/1.htm">职场和发展</a>
<div>125.验证回文串如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个回文串。字母和数字都属于字母数字字符。给你一个字符串s,如果它是回文串,返回true;否则,返回false。法1:re.subre.sub()是Pythonre(正则表达式)模块中的一个函数,用于替换字符串中匹配指定正则表达式的部分基本语法:re.sub(patter</div>
</li>
<li><a href="/article/1889954734224240640.htm"
title="C++进阶篇:从高手到大师" target="_blank">C++进阶篇:从高手到大师</a>
<span class="text-muted">杨胜增</span>
<a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
<div>C++进阶篇:从高手到大师如果你已经掌握了C++的基础知识并开始写出一些较为复杂的程序,接下来该是深入挖掘C++高级特性的时候了。从内存管理的细节到多线程编程,从模板的深度到STL的运用,这篇文章将带你深入C++的核心,帮助你成为C++的真正高手。目录C++内存管理的艺术手动内存管理与智能指针内存泄漏与RAIIC++中的模板编程模板的基本使用高级模板技术:SFINAE与变参模板C++的多线程与并发</div>
</li>
<li><a href="/article/1889954607958913024.htm"
title="微信小程序请求接口封装" target="_blank">微信小程序请求接口封装</a>
<span class="text-muted">大可不必_妄</span>
<a class="tag" taget="_blank" href="/search/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/1.htm">微信小程序</a><a class="tag" taget="_blank" href="/search/web%E5%89%8D%E7%AB%AF/1.htm">web前端</a><a class="tag" taget="_blank" href="/search/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/1.htm">微信小程序</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
<div>1、新建封装请求api/request.js//配置的域名constbaseUrl="xxxxxxxxxxxxxx"//请求公共接口//封装请求module.exports={/***二次封装wx.request*url:请求的接口地址*method:请求方式GET,POST....*data:要传递的参数*/request:(url,method,data)=>{returnnewPromise</div>
</li>
<li><a href="/article/1889954576006705152.htm"
title="AI时代前端开发的学习能力:与时俱进的挑战与机遇" target="_blank">AI时代前端开发的学习能力:与时俱进的挑战与机遇</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
<div>在飞速发展的科技浪潮中,前端开发领域正经历着翻天覆地的变化。得益于AI写代码工具的兴起,低代码/无代码开发模式日益普及,各种新技术、新框架、新工具层出不穷,这无疑对前端工程师的学习能力提出了更高的要求。本文将探讨AI时代前端工程师所需的学习能力,以及如何应对这些挑战与机遇,最终在AI浪潮中立于不败之地。AI如何改变前端开发模式传统的软件开发流程往往需要大量的代码编写和调试,耗时费力。而AI的介入,</div>
</li>
<li><a href="/article/1889954321672499200.htm"
title="微软 LayoutLMv3:通过统一文本和图像掩码进行文档人工智能预训练" target="_blank">微软 LayoutLMv3:通过统一文本和图像掩码进行文档人工智能预训练</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a>
<div>LayoutLMv3:通过统一文本和图像掩码进行文档人工智能预训练LayoutLMv3应用统一的文本-图像多模态Transformer来学习跨模态表示。Transformer具有多层架构,每层主要由多头自注意力机制和逐位置全连接前馈网络组成。Transformer的输入是文本嵌入$Y=y_{1:L}$和图像嵌入$X=x_{1:M}$序列的连接,其中$L$和$M$分别是文本和图像的序列长度。通过Tr</div>
</li>
<li><a href="/article/1889954194622836736.htm"
title="AI前端开发技能提升路径:从入门到精通,成为AI时代的前端专家" target="_blank">AI前端开发技能提升路径:从入门到精通,成为AI时代的前端专家</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
<div>在数字时代飞速发展的今天,AI写代码工具的出现为前端开发带来了革命性的变化。AI前端开发,这个融合人工智能与前端技术的领域,正以前所未有的速度蓬勃发展,为开发者们带来了巨大的机遇与挑战。本文将为你详细解读AI前端开发技能提升路径,助你成为AI时代的前端专家。1.AI前端开发:机遇与挑战并存AI前端开发,简单来说,就是将人工智能技术融入到前端应用中,例如开发智能推荐系统、AI图像处理工具、基于自然语</div>
</li>
<li><a href="/article/1889953941630808064.htm"
title="CyclicBarrier、CountDownLatch和Semaphore" target="_blank">CyclicBarrier、CountDownLatch和Semaphore</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
<div>1.基本用法(1)CountDownLatch用途:允许一个或多个线程等待其他线程完成操作。核心方法:countDown()(减少计数器)、await()(阻塞直到计数器归零)。不可重置,计数器归零后失效。publicstaticvoidCountDownLatchTest()throwsInterruptedException{//主线程等待所有子线程完成任务CountDownLatchlatc</div>
</li>
<li><a href="/article/1889953688676528128.htm"
title="美团一面,有点难度。" target="_blank">美团一面,有点难度。</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/go/1.htm">go</a>
<div>一位粉丝朋友分享了最近参与美团民宿旅游业务线的一面的经历,全程约1小时,面试官围绕高并发、分布式事务、性能优化等高频考点展开追问,问题密集且注重落地细节。以下是完整问题整理+回答思路+扩展解析,助你避坑!一、项目与高并发场景1.“介绍一个项目中的难点,并说明QPS和用户量峰值?”回答示例:项目背景:民宿节日大促活动,瞬时流量激增(如春节、国庆),用户抢购特价房源。核心数据:QPS峰值:约8000(</div>
</li>
<li><a href="/article/1889953474997710848.htm"
title="leetcode116. 填充每个节点的下一个右侧节点指针" target="_blank">leetcode116. 填充每个节点的下一个右侧节点指针</a>
<span class="text-muted">Chevy_cxw</span>
<a class="tag" taget="_blank" href="/search/c%2Fc%2B%2B/1.htm">c/c++</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95%E8%AE%BE%E8%AE%A1/1.htm">算法设计</a>
<div>题目链接:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/题意:给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:structNode{intval;Node*left;Node*right;Node*next;}填充它的每个next指针,让这个指针指向其</div>
</li>
<li><a href="/article/1889953096721821696.htm"
title="DeepSeek 指导手册从入门到精通" target="_blank">DeepSeek 指导手册从入门到精通</a>
<span class="text-muted">长久的梦</span>
<a class="tag" taget="_blank" href="/search/DeepSeek/1.htm">DeepSeek</a><a class="tag" taget="_blank" href="/search/DeepSeek/1.htm">DeepSeek</a><a class="tag" taget="_blank" href="/search/%E6%8A%80%E6%9C%AF%E6%9E%B6%E6%9E%84%E8%A7%A3%E6%9E%90/1.htm">技术架构解析</a><a class="tag" taget="_blank" href="/search/DeepSeek/1.htm">DeepSeek</a><a class="tag" taget="_blank" href="/search/%E4%BB%A3%E7%A0%81%E9%87%8D%E6%9E%84%E5%BA%94%E7%94%A8/1.htm">代码重构应用</a><a class="tag" taget="_blank" href="/search/DeepSeek/1.htm">DeepSeek</a><a class="tag" taget="_blank" href="/search/%E6%8F%90%E7%A4%BA%E8%AF%8D%E6%A8%A1%E6%9D%BF/1.htm">提示词模板</a><a class="tag" taget="_blank" href="/search/DeepSeek/1.htm">DeepSeek</a><a class="tag" taget="_blank" href="/search/%E8%81%94%E7%BD%91%E6%90%9C%E7%B4%A2%E6%8A%80%E5%B7%A7/1.htm">联网搜索技巧</a><a class="tag" taget="_blank" href="/search/DeepSeek/1.htm">DeepSeek</a><a class="tag" taget="_blank" href="/search/%E6%9C%AA%E6%9D%A5%E8%B6%8B%E5%8A%BF/1.htm">未来趋势</a><a class="tag" taget="_blank" href="/search/DeepSeek/1.htm">DeepSeek</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90%E7%9A%84%E6%84%8F%E4%B9%89/1.htm">开源的意义</a><a class="tag" taget="_blank" href="/search/DeepSeek/1.htm">DeepSeek</a><a class="tag" taget="_blank" href="/search/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E6%96%B9%E6%B3%95/1.htm">性能优化方法</a>
<div>目录正文第⼀章:准备篇(30分钟上手)❄️1.1三分钟创建你的AI伙伴❄️1.2认识你的AI控制台第⼆章:基础对话篇(像交朋友⼀样学交流)❄️2.1有效提问的五个⻩⾦法则❄️2.2新⼿必学的10个魔法指令第三章:效率⻜跃篇(⽂件处理与复杂任务)❄️3.1五分钟学会⽂档分析❄️3.2让AI帮你写代码第四章:场景实战篇⸺解决真实世界问题❄️4.1学术论⽂全流程辅助(从开题到答辩)❄️阶段⼀:开题攻坚❄</div>
</li>
<li><a href="/article/1889952968472588288.htm"
title="无人机遥感在农林信息提取中的实现方法与GIS融合制图教程" target="_blank">无人机遥感在农林信息提取中的实现方法与GIS融合制图教程</a>
<span class="text-muted">岁月如歌,青春不败</span>
<a class="tag" taget="_blank" href="/search/%E7%94%9F%E6%80%81%E9%81%A5%E6%84%9F/1.htm">生态遥感</a><a class="tag" taget="_blank" href="/search/%E6%97%A0%E4%BA%BA%E6%9C%BA/1.htm">无人机</a><a class="tag" taget="_blank" href="/search/%E5%86%9C%E4%B8%9A%E7%A7%91%E5%AD%A6/1.htm">农业科学</a><a class="tag" taget="_blank" href="/search/%E6%9E%97%E4%B8%9A%E7%A7%91%E5%AD%A6/1.htm">林业科学</a><a class="tag" taget="_blank" href="/search/GIS/1.htm">GIS</a><a class="tag" taget="_blank" href="/search/%E5%88%B6%E5%9B%BE/1.htm">制图</a><a class="tag" taget="_blank" href="/search/%E9%81%A5%E6%84%9F/1.htm">遥感</a><a class="tag" taget="_blank" href="/search/%E7%94%9F%E6%80%81%E5%AD%A6/1.htm">生态学</a>
<div>遥感技术作为一种空间大数据手段,能够从多时、多维、多地等角度,获取大量的农情数据。数据具有面状、实时、非接触、无伤检测等显著优势,是智慧农业必须采用的重要技术之一。一:综合态势分析1.1研究区及作物品种分析(1)形态指标分析(2)生理生化指标分析(3)胁迫指标分析(4)产量指标分析(5)综合分析1.2无人机平台分析:析目前常用于农林行业的无人机平台。1.3无人机机载传感器分析:析目前常用于农林行业</div>
</li>
<li><a href="/article/1889952970313887744.htm"
title="React 前端框架开发入门:从零开始构建你的第一个应用" target="_blank">React 前端框架开发入门:从零开始构建你的第一个应用</a>
<span class="text-muted">2401_89793006</span>
<a class="tag" taget="_blank" href="/search/%E7%83%AD%E9%97%A8%E8%AF%9D%E9%A2%98/1.htm">热门话题</a><a class="tag" taget="_blank" href="/search/react.js/1.htm">react.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/1.htm">前端框架</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
<div>React前端框架开发入门:从零开始构建你的第一个应用React是当前最流行的前端框架之一,由Facebook(Meta)开发并开源。它采用组件化开发的模式,使UI变得更易管理和复用。本文将带你从零开始,掌握React的基础知识,并构建你的第一个React应用!1.什么是React?React是一个用于构建用户界面的JavaScript库,主要用于构建单页应用(SPA)。其核心特点包括:✅组件化开</div>
</li>
<li><a href="/article/1889952932326076416.htm"
title="如何训练LLMs进行“思考”(如o1和DeepSeek-R1)" target="_blank">如何训练LLMs进行“思考”(如o1和DeepSeek-R1)</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a>
<div>如何训练LLMs进行“思考”(如o1和DeepSeek-R1)阅读时长:19分钟发布时间:2025-02-13近日热文:全网最全的神经网络数学原理(代码和公式)直观解释欢迎关注知乎和公众号的专栏内容LLM架构专栏知乎LLM专栏知乎【柏企】公众号【柏企科技说】【柏企阅文】一台会思考的笔记本电脑OpenAI的o1模型为大型语言模型(LLM)的训练开创了全新范式。它引入了所谓的“思考”令牌(tokens</div>
</li>
<li><a href="/article/1889952678012841984.htm"
title="MES系统:加速制造业数字化转型的驱动力" target="_blank">MES系统:加速制造业数字化转型的驱动力</a>
<span class="text-muted"></span>
<div>MES系统是一种集成化的车间生产信息化管理系统,它处于企业计划层与控制层之间,负责接收上层ERP系统下达的生产计划,并监控和指导车间生产过程的执行。MES系统通过数据采集、任务分配、过程监控、质量管理、设备维护、物料追踪等一系列功能,实现了生产现场的透明化、精益化和智能化管理。一、MES系统的核心功能1、生产计划管理:MES系统能依据订单任务和车间资源状况,制定高效的生产计划和产能调度计划,从而充</div>
</li>
<li><a href="/article/1889952425117282304.htm"
title="2025带你看清DevSecOps的发展背景、现状及未来趋势和最佳实践" target="_blank">2025带你看清DevSecOps的发展背景、现状及未来趋势和最佳实践</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a>
<div>DevSecOps的概念在2012年由Gartner首次提出,并逐渐受到国内企业的追捧。随着数字化转型加速和企业上云进程的推进,敏捷开发模式使软件开发生命周期缩短(几天到几周),留给安全的时间越来越短,因此必须在DevOps中有效地融入安全,即DevSecOps。业界已经达成一种共识,即DevSecOps是DevOps发展的必然结果。概览1.1.DevSecOps产生背景传统安全模式局限:传统的安</div>
</li>
<li><a href="/article/1889951709023760384.htm"
title="python pandas中apply()方法用法汇总" target="_blank">python pandas中apply()方法用法汇总</a>
<span class="text-muted">whale fall</span>
<a class="tag" taget="_blank" href="/search/python%E8%BF%9B%E9%98%B6/1.htm">python进阶</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/pandas/1.htm">pandas</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/1.htm">数据分析</a>
<div>apply函数是pandas中用于对DataFrame或Series中的每一行或每一列应用一个函数的强大工具。在apply()方法中,通常会传入一个函数作为参数,这个函数会应用到DataFrame的每一行或每一列上,或Series的每个元素上。下面是一些常见的用法示例:1.对Series使用apply(),传入一个函数如果你想对某一列(Series)应用函数,可以直接调用apply方法。impor</div>
</li>
<li><a href="/article/1889951289735966720.htm"
title="团队领导者指南:如何选择和应用项目管理方法论" target="_blank">团队领导者指南:如何选择和应用项目管理方法论</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86%E8%BD%AF%E4%BB%B6/1.htm">项目管理软件</a>
<div>项目管理方法论是用于规划、执行和控制项目的系统化框架和流程。不同的方法论适用于不同类型的项目和团队需求。以下是几种常见的项目管理方法论:1.瀑布模型(Waterfall)●特点:线性顺序的项目管理方法,项目分为多个阶段(如需求分析、设计、开发、测试、部署),每个阶段完成后才能进入下一个阶段。●适用场景:需求明确、变更较少的项目,如建筑、制造业等。●优点:结构清晰,易于理解和执行。●缺点:缺乏灵活性</div>
</li>
<li><a href="/article/1889950953063378944.htm"
title="WebSocket 握手过程" target="_blank">WebSocket 握手过程</a>
<span class="text-muted">子羽bro</span>
<a class="tag" taget="_blank" href="/search/%E6%97%A5%E5%B8%B8%E5%BC%80%E5%8F%91%E5%90%88%E9%9B%86/1.htm">日常开发合集</a><a class="tag" taget="_blank" href="/search/websocket/1.htm">websocket</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/1.htm">网络协议</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a>
<div>文章目录1.WebSocket握手过程概述2.客户端发送握手请求3.服务器响应握手请求4.客户端验证握手响应5.建立WebSocket连接6.安全性与注意事项7.应用示例在现代Web开发中,WebSocket协议因其高效的实时通信能力而被广泛应用。WebSocket允许客户端和服务器之间建立持久的双向通信连接,从而实现诸如实时聊天、在线游戏、物联网设备监控等场景。然而,WebSocket连接的建立</div>
</li>
<li><a href="/article/1889950698620121088.htm"
title="【C++指南】解锁C++ STL:从入门到进阶的技术之旅" target="_blank">【C++指南】解锁C++ STL:从入门到进阶的技术之旅</a>
<span class="text-muted">倔强的石头_</span>
<a class="tag" taget="_blank" href="/search/C%2B%2B%E6%8C%87%E5%8D%97/1.htm">C++指南</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
<div>博客主页:倔强的石头的CSDN主页Gitee主页:倔强的石头的gitee主页⏩文章专栏:《C++指南》期待您的关注目录一、STL是什么二、STL的核心组件2.1容器(Containers)2.2算法(Algorithms)2.3迭代器(Iterators)2.4其他组件三、STL的优势3.1高效开发3.2高性能3.3泛型与可扩展性3.4代码简洁与可维护性3.5跨平台兼容性四、结语一、STL是什么S</div>
</li>
<li><a href="/article/1889950700805353472.htm"
title="【深度学习】常见模型-GPT(Generative Pre-trained Transformer,生成式预训练 Transformer)" target="_blank">【深度学习】常见模型-GPT(Generative Pre-trained Transformer,生成式预训练 Transformer)</a>
<span class="text-muted">IT古董</span>
<a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/1.htm">深度学习</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/1.htm">深度学习</a><a class="tag" taget="_blank" href="/search/gpt/1.htm">gpt</a><a class="tag" taget="_blank" href="/search/transformer/1.htm">transformer</a>
<div>GPT(GenerativePre-trainedTransformer)1️⃣什么是GPT?GPT(GenerativePre-trainedTransformer,生成式预训练Transformer)是由OpenAI开发的基于Transformer解码器(Decoder)的自回归(Autoregressive)语言模型。它能够通过大量无监督数据预训练,然后微调(Fine-tuning)以适应特</div>
</li>
<li><a href="/article/1889950656664498176.htm"
title="产品和品牌谁的优先级更高?看看 Curve 的初版界面就知道了" target="_blank">产品和品牌谁的优先级更高?看看 Curve 的初版界面就知道了</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8%E5%8C%BA%E5%9D%97%E9%93%BE/1.htm">安全区块链</a>
<div>撰文:BramVanRoelen,Maven11Capital产品主管编译:Tia,TechubNews「初创公司在不同阶段应如何平衡产品建设与品牌营销:初期应专注于构建优秀产品,品牌营销应在后期逐步增加,避免过早依赖品牌包装。」每周,总有一些初创公司雇佣昂贵的代理商来为他们设计「品牌故事」。但Aave却从一个看起来像黑客马拉松项目的小玩意,成长为DeFi借贷市场的中坚力量。这不是巧合——这是一个</div>
</li>
<li><a href="/article/1889950657922789376.htm"
title="从负数绝对值的计算来看Ruby的一个“奇葩”行为" target="_blank">从负数绝对值的计算来看Ruby的一个“奇葩”行为</a>
<span class="text-muted"></span>
<div>计算一个数的绝对值是非常基础的操作,几乎所有主流的编程语言都内置了相应的函数或方法。在PHP、Python、SQL等语言中,直接调用abs()函数即可,例如abs(-1)。到了Java、C#这类面向对象的语言中,abs()通常是Math类的静态方法,调用时要加上前缀Math.,即Math.abs(-1)。Go语言就要稍微麻烦一点了,因为math包中的Abs()函数仅支持float64类型的参数,如</div>
</li>
<li><a href="/article/1889950659243995136.htm"
title="特朗普家族搅局加密界:原以为的「正本清源」却成了深陷泥潭" target="_blank">特朗普家族搅局加密界:原以为的「正本清源」却成了深陷泥潭</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E5%8C%BA%E5%9D%97%E9%93%BEweb3%E6%AF%94%E7%89%B9%E5%B8%81/1.htm">区块链web3比特币</a>
<div>作者:Techub精选编译原标题:Crypto’sFirstFamilyIsDeepeningtheSwamp撰文:LionelLaurent,彭博社观点专栏作家编译:J1N,TechubNews美国总统特朗普的次子EricTrump认为现在是购买以太坊的好时机,他认为由于他对以太坊的支持推动了币价的短暂上涨。但与此同时,现在也是政客和监管机构采取行动的好时机,以建立更严格的监管措施,针对特朗普家</div>
</li>
<li><a href="/article/1889950655418789888.htm"
title="外贸行业常用的五款业务管理软件,你用过哪款?" target="_blank">外贸行业常用的五款业务管理软件,你用过哪款?</a>
<span class="text-muted"></span>
<div>在外贸行业中,选择合适的业务管理软件对于提高工作效率、优化客户关系管理和增强企业竞争力至关重要。下面我们将介绍ZohoBooks、青虹云、孚盟等五款被广泛使用的外贸业务管理软件,帮助您了解它们的特点和适用场景。一、ZohoBooksZohoBooks是一款面向外贸企业设计的业务管理软件,特别适合出海、外贸、跨境电商企业使用。它不仅简化了日常账务处理,还提供了一系列专门针对国际贸易的功能,确保企业的</div>
</li>
<li><a href="/article/1889949899173195776.htm"
title="【FAQ】HarmonyOS SDK 闭源开放能力 — IAP Kit(4)" target="_blank">【FAQ】HarmonyOS SDK 闭源开放能力 — IAP Kit(4)</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/harmonyos-next/1.htm">harmonyos-next</a>
<div>1.问题描述:发布了一个订阅,看日志显示订阅发布成功了,但是在消费的时候没有值,这个是什么原因?人脸活体检测返回上一页App由沉浸式变为非沉浸式多了上下安全区域。解决方案:对于公共事件来说就是提供这个能力,需要调用方保证时序,订阅成功之后再发广播才能收到。2.问题描述:微信支付,支付宝支付,银联支付SDK是否已经支持?解决方案:1、支付宝:鸿蒙支付SDK获取链接:https://opendocs.</div>
</li>
<li><a href="/article/1889949519014064128.htm"
title="【FAQ】HarmonyOS SDK 闭源开放能力 —Remote Communication Kit" target="_blank">【FAQ】HarmonyOS SDK 闭源开放能力 —Remote Communication Kit</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/harmonyos-next/1.htm">harmonyos-next</a>
<div>1.问题描述:DynamicDnsRule有没有示例?这个地址是怎么解析出来https://developer.huawei.com/consumer/cn/doc/harmonyos-refere...解决方案:'DynamicDnsRule':表示优先使用函数中返回的地址。/***域名和端口会自行获取,不需要传入,这边需要开发者指定Ip地址数组*@paramhost域名*@param_端口*@</div>
</li>
<li><a href="/article/1889949520377212928.htm"
title="【FAQ】HarmonyOS SDK 闭源开放能力 —Push Kit(7)" target="_blank">【FAQ】HarmonyOS SDK 闭源开放能力 —Push Kit(7)</a>
<span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/harmonyos-next/1.htm">harmonyos-next</a>
<div>1.问题描述:推送通知到手机,怎么配置拉起应用指定的页面?解决方案:1、如果点击通知栏打开默认Ability的话,actionType可以设置为0,同时可以在.clickAction.data中,指定待跳转的page页面,命名为pageUri。2、然后在UIAbility的onNewWant或者onCreate方法中解析配置的pageUri;3、如果应用进程不存在将会触发onCreate方法,可以</div>
</li>
<li><a href="/article/102.htm"
title="xml解析" target="_blank">xml解析</a>
<span class="text-muted">小猪猪08</span>
<a class="tag" taget="_blank" href="/search/xml/1.htm">xml</a>
<div>1、DOM解析的步奏
准备工作:
1.创建DocumentBuilderFactory的对象
2.创建DocumentBuilder对象
3.通过DocumentBuilder对象的parse(String fileName)方法解析xml文件
4.通过Document的getElem</div>
</li>
<li><a href="/article/229.htm"
title="每个开发人员都需要了解的一个SQL技巧" target="_blank">每个开发人员都需要了解的一个SQL技巧</a>
<span class="text-muted">brotherlamp</span>
<a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/linux%E8%A7%86%E9%A2%91/1.htm">linux视频</a><a class="tag" taget="_blank" href="/search/linux%E6%95%99%E7%A8%8B/1.htm">linux教程</a><a class="tag" taget="_blank" href="/search/linux%E8%87%AA%E5%AD%A6/1.htm">linux自学</a><a class="tag" taget="_blank" href="/search/linux%E8%B5%84%E6%96%99/1.htm">linux资料</a>
<div>
对于数据过滤而言CHECK约束已经算是相当不错了。然而它仍存在一些缺陷,比如说它们是应用到表上面的,但有的时候你可能希望指定一条约束,而它只在特定条件下才生效。
使用SQL标准的WITH CHECK OPTION子句就能完成这点,至少Oracle和SQL Server都实现了这个功能。下面是实现方式:
CREATE TABLE books (
id &</div>
</li>
<li><a href="/article/356.htm"
title="Quartz——CronTrigger触发器" target="_blank">Quartz——CronTrigger触发器</a>
<span class="text-muted">eksliang</span>
<a class="tag" taget="_blank" href="/search/quartz/1.htm">quartz</a><a class="tag" taget="_blank" href="/search/CronTrigger/1.htm">CronTrigger</a>
<div>转载请出自出处:http://eksliang.iteye.com/blog/2208295 一.概述
CronTrigger 能够提供比 SimpleTrigger 更有具体实际意义的调度方案,调度规则基于 Cron 表达式,CronTrigger 支持日历相关的重复时间间隔(比如每月第一个周一执行),而不是简单的周期时间间隔。 二.Cron表达式介绍 1)Cron表达式规则表
Quartz</div>
</li>
<li><a href="/article/483.htm"
title="Informatica基础" target="_blank">Informatica基础</a>
<span class="text-muted">18289753290</span>
<a class="tag" taget="_blank" href="/search/Informatica/1.htm">Informatica</a><a class="tag" taget="_blank" href="/search/Monitor/1.htm">Monitor</a><a class="tag" taget="_blank" href="/search/manager/1.htm">manager</a><a class="tag" taget="_blank" href="/search/workflow/1.htm">workflow</a><a class="tag" taget="_blank" href="/search/Designer/1.htm">Designer</a>
<div>1.
1)PowerCenter Designer:设计开发环境,定义源及目标数据结构;设计转换规则,生成ETL映射。
2)Workflow Manager:合理地实现复杂的ETL工作流,基于时间,事件的作业调度
3)Workflow Monitor:监控Workflow和Session运行情况,生成日志和报告
4)Repository Manager:</div>
</li>
<li><a href="/article/610.htm"
title="linux下为程序创建启动和关闭的的sh文件,scrapyd为例" target="_blank">linux下为程序创建启动和关闭的的sh文件,scrapyd为例</a>
<span class="text-muted">酷的飞上天空</span>
<a class="tag" taget="_blank" href="/search/scrapy/1.htm">scrapy</a>
<div>对于一些未提供service管理的程序 每次启动和关闭都要加上全部路径,想到可以做一个简单的启动和关闭控制的文件
下面以scrapy启动server为例,文件名为run.sh:
#端口号,根据此端口号确定PID
PORT=6800
#启动命令所在目录
HOME='/home/jmscra/scrapy/'
#查询出监听了PORT端口</div>
</li>
<li><a href="/article/737.htm"
title="人--自私与无私" target="_blank">人--自私与无私</a>
<span class="text-muted">永夜-极光</span>
<div> 今天上毛概课,老师提出一个问题--人是自私的还是无私的,根源是什么?
从客观的角度来看,人有自私的行为,也有无私的</div>
</li>
<li><a href="/article/864.htm"
title="Ubuntu安装NS-3 环境脚本" target="_blank">Ubuntu安装NS-3 环境脚本</a>
<span class="text-muted">随便小屋</span>
<a class="tag" taget="_blank" href="/search/ubuntu/1.htm">ubuntu</a>
<div>
将附件下载下来之后解压,将解压后的文件ns3environment.sh复制到下载目录下(其实放在哪里都可以,就是为了和我下面的命令相统一)。输入命令:
sudo ./ns3environment.sh >>result
这样系统就自动安装ns3的环境,运行的结果在result文件中,如果提示
com</div>
</li>
<li><a href="/article/991.htm"
title="创业的简单感受" target="_blank">创业的简单感受</a>
<span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/%E5%88%9B%E4%B8%9A%E7%9A%84%E7%AE%80%E5%8D%95%E6%84%9F%E5%8F%97/1.htm">创业的简单感受</a>
<div>
2009年11月9日我进入a公司实习,2012年4月26日,我离开a公司,开始自己的创业之旅。
今天是2012年5月30日,我忽然很想谈谈自己创业一个月的感受。
当初离开边锋时,我就对自己说:“自己选择的路,就是跪着也要把他走完”,我也做好了心理准备,准备迎接一次次的困难。我这次走出来,不管成败</div>
</li>
<li><a href="/article/1118.htm"
title="如何经营自己的独立人脉" target="_blank">如何经营自己的独立人脉</a>
<span class="text-muted">aoyouzi</span>
<a class="tag" taget="_blank" href="/search/%E5%A6%82%E4%BD%95%E7%BB%8F%E8%90%A5%E8%87%AA%E5%B7%B1%E7%9A%84%E7%8B%AC%E7%AB%8B%E4%BA%BA%E8%84%89/1.htm">如何经营自己的独立人脉</a>
<div>独立人脉不是父母、亲戚的人脉,而是自己主动投入构造的人脉圈。“放长线,钓大鱼”,先行投入才能产生后续产出。 现在几乎做所有的事情都需要人脉。以银行柜员为例,需要拉储户,而其本质就是社会人脉,就是社交!很多人都说,人脉我不行,因为我爸不行、我妈不行、我姨不行、我舅不行……我谁谁谁都不行,怎么能建立人脉?我这里说的人脉,是你的独立人脉。 以一个普通的银行柜员</div>
</li>
<li><a href="/article/1245.htm"
title="JSP基础" target="_blank">JSP基础</a>
<span class="text-muted">百合不是茶</span>
<a class="tag" taget="_blank" href="/search/jsp/1.htm">jsp</a><a class="tag" taget="_blank" href="/search/%E6%B3%A8%E9%87%8A/1.htm">注释</a><a class="tag" taget="_blank" href="/search/%E9%9A%90%E5%BC%8F%E5%AF%B9%E8%B1%A1/1.htm">隐式对象</a>
<div>
1,JSP语句的声明
<%! 声明 %> 声明:这个就是提供java代码声明变量、方法等的场所。
表达式 <%= 表达式 %> 这个相当于赋值,可以在页面上显示表达式的结果,
程序代码段/小型指令 <% 程序代码片段 %>
2,JSP的注释
<!-- --> </div>
</li>
<li><a href="/article/1372.htm"
title="web.xml之session-config、mime-mapping" target="_blank">web.xml之session-config、mime-mapping</a>
<span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/web.xml/1.htm">web.xml</a><a class="tag" taget="_blank" href="/search/servlet/1.htm">servlet</a><a class="tag" taget="_blank" href="/search/session-config/1.htm">session-config</a><a class="tag" taget="_blank" href="/search/mime-mapping/1.htm">mime-mapping</a>
<div>session-config
1.定义:
<session-config>
<session-timeout>20</session-timeout>
</session-config>
2.作用:用于定义整个WEB站点session的有效期限,单位是分钟。
mime-mapping
1.定义:
<mime-m</div>
</li>
<li><a href="/article/1499.htm"
title="互联网开放平台(1)" target="_blank">互联网开放平台(1)</a>
<span class="text-muted">Bill_chen</span>
<a class="tag" taget="_blank" href="/search/%E4%BA%92%E8%81%94%E7%BD%91/1.htm">互联网</a><a class="tag" taget="_blank" href="/search/qq/1.htm">qq</a><a class="tag" taget="_blank" href="/search/%E6%96%B0%E6%B5%AA%E5%BE%AE%E5%8D%9A/1.htm">新浪微博</a><a class="tag" taget="_blank" href="/search/%E7%99%BE%E5%BA%A6/1.htm">百度</a><a class="tag" taget="_blank" href="/search/%E8%85%BE%E8%AE%AF/1.htm">腾讯</a>
<div>现在各互联网公司都推出了自己的开放平台供用户创造自己的应用,互联网的开放技术欣欣向荣,自己总结如下:
1.淘宝开放平台(TOP)
网址:http://open.taobao.com/
依赖淘宝强大的电子商务数据,将淘宝内部业务数据作为API开放出去,同时将外部ISV的应用引入进来。
目前TOP的三条主线:
TOP访问网站:open.taobao.com
ISV后台:my.open.ta</div>
</li>
<li><a href="/article/1626.htm"
title="【MongoDB学习笔记九】MongoDB索引" target="_blank">【MongoDB学习笔记九】MongoDB索引</a>
<span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a>
<div>索引
可以在任意列上建立索引
索引的构造和使用与传统关系型数据库几乎一样,适用于Oracle的索引优化技巧也适用于Mongodb
使用索引可以加快查询,但同时会降低修改,插入等的性能
内嵌文档照样可以建立使用索引
测试数据
var p1 = {
"name":"Jack",
"age&q</div>
</li>
<li><a href="/article/1753.htm"
title="JDBC常用API之外的总结" target="_blank">JDBC常用API之外的总结</a>
<span class="text-muted">白糖_</span>
<a class="tag" taget="_blank" href="/search/jdbc/1.htm">jdbc</a>
<div> 做JAVA的人玩JDBC肯定已经很熟练了,像DriverManager、Connection、ResultSet、Statement这些基本类大家肯定很常用啦,我不赘述那些诸如注册JDBC驱动、创建连接、获取数据集的API了,在这我介绍一些写框架时常用的API,大家共同学习吧。
ResultSetMetaData获取ResultSet对象的元数据信息
</div>
</li>
<li><a href="/article/1880.htm"
title="apache VelocityEngine使用记录" target="_blank">apache VelocityEngine使用记录</a>
<span class="text-muted">bozch</span>
<a class="tag" taget="_blank" href="/search/VelocityEngine/1.htm">VelocityEngine</a>
<div>VelocityEngine是一个模板引擎,能够基于模板生成指定的文件代码。
使用方法如下:
VelocityEngine engine = new VelocityEngine();// 定义模板引擎
Properties properties = new Properties();// 模板引擎属</div>
</li>
<li><a href="/article/2007.htm"
title="编程之美-快速找出故障机器" target="_blank">编程之美-快速找出故障机器</a>
<span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E/1.htm">编程之美</a>
<div>
package beautyOfCoding;
import java.util.Arrays;
public class TheLostID {
/*编程之美
假设一个机器仅存储一个标号为ID的记录,假设机器总量在10亿以下且ID是小于10亿的整数,假设每份数据保存两个备份,这样就有两个机器存储了同样的数据。
1.假设在某个时间得到一个数据文件ID的列表,是</div>
</li>
<li><a href="/article/2134.htm"
title="关于Java中redirect与forward的区别" target="_blank">关于Java中redirect与forward的区别</a>
<span class="text-muted">chenbowen00</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/servlet/1.htm">servlet</a>
<div>在Servlet中两种实现:
forward方式:request.getRequestDispatcher(“/somePage.jsp”).forward(request, response);
redirect方式:response.sendRedirect(“/somePage.jsp”);
forward是服务器内部重定向,程序收到请求后重新定向到另一个程序,客户机并不知</div>
</li>
<li><a href="/article/2261.htm"
title="[信号与系统]人体最关键的两个信号节点" target="_blank">[信号与系统]人体最关键的两个信号节点</a>
<span class="text-muted">comsci</span>
<a class="tag" taget="_blank" href="/search/%E7%B3%BB%E7%BB%9F/1.htm">系统</a>
<div>
如果把人体看做是一个带生物磁场的导体,那么这个导体有两个很重要的节点,第一个在头部,中医的名称叫做 百汇穴, 另外一个节点在腰部,中医的名称叫做 命门
如果要保护自己的脑部磁场不受到外界有害信号的攻击,最简单的</div>
</li>
<li><a href="/article/2388.htm"
title="oracle 存储过程执行权限" target="_blank">oracle 存储过程执行权限</a>
<span class="text-muted">daizj</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/%E5%AD%98%E5%82%A8%E8%BF%87%E7%A8%8B/1.htm">存储过程</a><a class="tag" taget="_blank" href="/search/%E6%9D%83%E9%99%90/1.htm">权限</a><a class="tag" taget="_blank" href="/search/%E6%89%A7%E8%A1%8C%E8%80%85/1.htm">执行者</a><a class="tag" taget="_blank" href="/search/%E8%B0%83%E7%94%A8%E8%80%85/1.htm">调用者</a>
<div>在数据库系统中存储过程是必不可少的利器,存储过程是预先编译好的为实现一个复杂功能的一段Sql语句集合。它的优点我就不多说了,说一下我碰到的问题吧。我在项目开发的过程中需要用存储过程来实现一个功能,其中涉及到判断一张表是否已经建立,没有建立就由存储过程来建立这张表。
CREATE OR REPLACE PROCEDURE TestProc
IS
fla</div>
</li>
<li><a href="/article/2515.htm"
title="为mysql数据库建立索引" target="_blank">为mysql数据库建立索引</a>
<span class="text-muted">dengkane</span>
<a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/%E6%80%A7%E8%83%BD/1.htm">性能</a><a class="tag" taget="_blank" href="/search/%E7%B4%A2%E5%BC%95/1.htm">索引</a>
<div>前些时候,一位颇高级的程序员居然问我什么叫做索引,令我感到十分的惊奇,我想这绝不会是沧海一粟,因为有成千上万的开发者(可能大部分是使用MySQL的)都没有受过有关数据库的正规培训,尽管他们都为客户做过一些开发,但却对如何为数据库建立适当的索引所知较少,因此我起了写一篇相关文章的念头。 最普通的情况,是为出现在where子句的字段建一个索引。为方便讲述,我们先建立一个如下的表。</div>
</li>
<li><a href="/article/2642.htm"
title="学习C语言常见误区 如何看懂一个程序 如何掌握一个程序以及几个小题目示例" target="_blank">学习C语言常见误区 如何看懂一个程序 如何掌握一个程序以及几个小题目示例</a>
<span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/c/1.htm">c</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a>
<div>如果看懂一个程序,分三步
1、流程
2、每个语句的功能
3、试数
如何学习一些小算法的程序
尝试自己去编程解决它,大部分人都自己无法解决
如果解决不了就看答案
关键是把答案看懂,这个是要花很大的精力,也是我们学习的重点
看懂之后尝试自己去修改程序,并且知道修改之后程序的不同输出结果的含义
照着答案去敲
调试错误
</div>
</li>
<li><a href="/article/2769.htm"
title="centos6.3安装php5.4报错" target="_blank">centos6.3安装php5.4报错</a>
<span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/centos6/1.htm">centos6</a>
<div>报错内容如下:
Resolving Dependencies
--> Running transaction check
---> Package php54w.x86_64 0:5.4.38-1.w6 will be installed
--> Processing Dependency: php54w-common(x86-64) = 5.4.38-1.w6 for </div>
</li>
<li><a href="/article/2896.htm"
title="JSONP请求" target="_blank">JSONP请求</a>
<span class="text-muted">flyer0126</span>
<a class="tag" taget="_blank" href="/search/jsonp/1.htm">jsonp</a>
<div>
使用jsonp不能发起POST请求。
It is not possible to make a JSONP POST request.
JSONP works by creating a <script> tag that executes Javascript from a different domain; it is not pos</div>
</li>
<li><a href="/article/3023.htm"
title="Spring Security(03)——核心类简介" target="_blank">Spring Security(03)——核心类简介</a>
<span class="text-muted">234390216</span>
<a class="tag" taget="_blank" href="/search/Authentication/1.htm">Authentication</a>
<div>核心类简介
目录
1.1 Authentication
1.2 SecurityContextHolder
1.3 AuthenticationManager和AuthenticationProvider
1.3.1 &nb</div>
</li>
<li><a href="/article/3150.htm"
title="在CentOS上部署JAVA服务" target="_blank">在CentOS上部署JAVA服务</a>
<span class="text-muted">java--hhf</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/jdk/1.htm">jdk</a><a class="tag" taget="_blank" href="/search/centos/1.htm">centos</a><a class="tag" taget="_blank" href="/search/Java%E6%9C%8D%E5%8A%A1/1.htm">Java服务</a>
<div> 本文将介绍如何在CentOS上运行Java Web服务,其中将包括如何搭建JAVA运行环境、如何开启端口号、如何使得服务在命令执行窗口关闭后依旧运行
第一步:卸载旧Linux自带的JDK
①查看本机JDK版本
java -version
结果如下
java version "1.6.0"</div>
</li>
<li><a href="/article/3277.htm"
title="oracle、sqlserver、mysql常用函数对比[to_char、to_number、to_date]" target="_blank">oracle、sqlserver、mysql常用函数对比[to_char、to_number、to_date]</a>
<span class="text-muted">ldzyz007</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/SQL+Server/1.htm">SQL Server</a>
<div>oracle &n</div>
</li>
<li><a href="/article/3404.htm"
title="记Protocol Oriented Programming in Swift of WWDC 2015" target="_blank">记Protocol Oriented Programming in Swift of WWDC 2015</a>
<span class="text-muted">ningandjin</span>
<a class="tag" taget="_blank" href="/search/protocol/1.htm">protocol</a><a class="tag" taget="_blank" href="/search/WWDC+2015/1.htm">WWDC 2015</a><a class="tag" taget="_blank" href="/search/Swift2.0/1.htm">Swift2.0</a>
<div>其实最先朋友让我就这个题目写篇文章的时候,我是拒绝的,因为觉得苹果就是在炒冷饭, 把已经流行了数十年的OOP中的“面向接口编程”还拿来讲,看完整个Session之后呢,虽然还是觉得在炒冷饭,但是毕竟还是加了蛋的,有些东西还是值得说说的。
通常谈到面向接口编程,其主要作用是把系统设计和具体实现分离开,让系统的每个部分都可以在不影响别的部分的情况下,改变自身的具体实现。接口的设计就反映了系统</div>
</li>
<li><a href="/article/3531.htm"
title="搭建 CentOS 6 服务器(15) - Keepalived、HAProxy、LVS" target="_blank">搭建 CentOS 6 服务器(15) - Keepalived、HAProxy、LVS</a>
<span class="text-muted">rensanning</span>
<a class="tag" taget="_blank" href="/search/keepalived/1.htm">keepalived</a>
<div>(一)Keepalived
(1)安装
# cd /usr/local/src
# wget http://www.keepalived.org/software/keepalived-1.2.15.tar.gz
# tar zxvf keepalived-1.2.15.tar.gz
# cd keepalived-1.2.15
# ./configure
# make &a</div>
</li>
<li><a href="/article/3658.htm"
title="ORACLE数据库SCN和时间的互相转换" target="_blank">ORACLE数据库SCN和时间的互相转换</a>
<span class="text-muted">tomcat_oracle</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a>
<div>SCN(System Change Number 简称 SCN)是当Oracle数据库更新后,由DBMS自动维护去累积递增的一个数字,可以理解成ORACLE数据库的时间戳,从ORACLE 10G开始,提供了函数可以实现SCN和时间进行相互转换;
用途:在进行数据库的还原和利用数据库的闪回功能时,进行SCN和时间的转换就变的非常必要了;
操作方法: 1、通过dbms_f</div>
</li>
<li><a href="/article/3785.htm"
title="Spring MVC 方法注解拦截器" target="_blank">Spring MVC 方法注解拦截器</a>
<span class="text-muted">xp9802</span>
<a class="tag" taget="_blank" href="/search/spring+mvc/1.htm">spring mvc</a>
<div>应用场景,在方法级别对本次调用进行鉴权,如api接口中有个用户唯一标示accessToken,对于有accessToken的每次请求可以在方法加一个拦截器,获得本次请求的用户,存放到request或者session域。
python中,之前在python flask中可以使用装饰器来对方法进行预处理,进行权限处理
先看一个实例,使用@access_required拦截:
? </div>
</li>
</ul>
</div>
</div>
</div>
<div>
<div class="container">
<div class="indexes">
<strong>按字母分类:</strong>
<a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a
href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a
href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a
href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a
href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a
href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a
href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a
href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a
href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a>
</div>
</div>
</div>
<footer id="footer" class="mb30 mt30">
<div class="container">
<div class="footBglm">
<a target="_blank" href="/">首页</a> -
<a target="_blank" href="/custom/about.htm">关于我们</a> -
<a target="_blank" href="/search/Java/1.htm">站内搜索</a> -
<a target="_blank" href="/sitemap.txt">Sitemap</a> -
<a target="_blank" href="/custom/delete.htm">侵权投诉</a>
</div>
<div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved.
<!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>-->
</div>
</div>
</footer>
<!-- 代码高亮 -->
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script>
<link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/>
<script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script>
</body>
</html>