记录日常开发中的一些常用技巧,有的可能比较小,不适合专门写一篇文章,那么可以写在笔记总结系列里面。

scp命令用法

发送目录到服务器

比如我想把一个目录下的所有文件发送到服务器中,以C-Thead-Pool为例:

bogon:github coder52$ scp -r C-Thread-Pool/  root@10.211.55.49:~
root@10.211.55.49's password:
LICENSE                                       100% 1090   592.0KB/s   00:00
thpool.h                                      100% 4639     3.0MB/s   00:00
funcs.sh                                      100% 1004   953.8KB/s   00:00
memleaks.sh                                   100% 1912     2.3MB/s   00:00
api.sh                                        100%  395   583.6KB/s   00:00
normal_compile.sh                             100%  255   337.0KB/s   00:00
heap_stack_garbage.sh                         100%  480   559.4KB/s   00:00
README.md                                     100% 1042     1.3MB/s   00:00
memleak.c                                     100%  960     1.3MB/s   00:00
optimized_compile.sh                          100%  271   395.6KB/s   00:00
wait.sh                                       100% 1237     1.7MB/s   00:00
threadpool.sh                                 100%  521   570.4KB/s   00:00
pause_resume.sh                               100%  684   650.4KB/s   00:00
pause_resume.c                                100% 1041     1.1MB/s   00:00
api.c                                         100% 1122     1.1MB/s   00:00
nonzero_heap_stack.c                          100%  898     1.3MB/s   00:00
conc_increment.c                              100%  702   634.8KB/s   00:00
no_work.c                                     100%  437   567.5KB/s   00:00
wait.c                                        100% 1089     1.2MB/s   00:00
FAQ.md                                        100% 2138     1.9MB/s   00:00
Design.md                                     100% 2169     2.4MB/s   00:00
README.md                                     100% 4001     4.7MB/s   00:00
example.c                                     100% 1074     1.3MB/s   00:00
thpool.c                                      100%   13KB   9.8MB/s   00:00
config                                        100%  311   409.9KB/s   00:00
pack-f22075098f8ed8b0755df6699af6f077315ca459 100%   21KB  17.7MB/s   00:00
pack-f22075098f8ed8b0755df6699af6f077315ca459 100%  164KB  57.1MB/s   00:00
HEAD                                          100%   23    20.4KB/s   00:00
exclude                                       100%  250   264.5KB/s   00:00
HEAD                                          100%  184   206.5KB/s   00:00
master                                        100%  184   226.3KB/s   00:00
HEAD                                          100%  184   163.1KB/s   00:00
description                                   100%   73    48.3KB/s   00:00
commit-msg.sample                             100%  896     1.1MB/s   00:00
pre-rebase.sample                             100% 4951     5.4MB/s   00:00
pre-commit.sample                             100% 1642     2.2MB/s   00:00
applypatch-msg.sample                         100%  478   795.2KB/s   00:00
prepare-commit-msg.sample                     100% 1239     1.7MB/s   00:00
post-update.sample                            100%  189   309.2KB/s   00:00
pre-applypatch.sample                         100%  424   629.3KB/s   00:00
pre-push.sample                               100% 1348     2.0MB/s   00:00
update.sample                                 100% 3611     3.7MB/s   00:00
master                                        100%   41    53.5KB/s   00:00
HEAD                                          100%   32    31.4KB/s   00:00
index                                         100% 2179     1.9MB/s   00:00
packed-refs                                   100%  183   231.5KB/s   00:00
FETCH_HEAD                                    100%  204   235.5KB/s   00:00
sourcetreeconfig                              100%  372   329.4KB/s   00:00

这样就将Thread-Pool/ 目录下的所有文件发送到了服务器~/Thread-Pool目录下.

拷贝目录到本地

从远程服务器/root目录下,拷贝文件夹C-Thread-Pool到当前服务器/root目录,执行如下命令即可:

[root ~]#scp -r root@10.211.55.49:/root/C-Thread-Pool /root
root@10.211.55.49's password:
输入服务器正确密码即可

gcc参数-D -U

gcc -DDEBUG=1 foo.c
相当于在foo.c中添加了如下语句:

#define DEBUG 1

-D参数我们比较熟悉,可是-U参数却很少用到,-U选项用于删除宏的定义,效果相当于undef,可以使用-U选项来删除预定义宏或之前在命令行方式下用-D选项定义的宏。

函数strtok

/* strtok example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] ="- This, a sample string.";
  char * pch;
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str," ,.-");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, " ,.-");
  }
  return 0;
}

C语言字符串分割函数strtok,使用之后会更改原字符数组内容,在分隔符结束的地方添加结束字符,返回分隔符开始的地址。
可以使用gdb,查看str内容验证:

13          printf ("%s\n",pch);
(gdb)
string
14          pch = strtok (NULL, " ,.-");
(gdb)
11      while (pch != NULL)
(gdb)
17      printf("After call strtok str = %s\n",str);
(gdb) p str
$1 = "- This\000 a\000sample\000string\000"
(gdb) q
A debugging session is active.

    Inferior 1 [process 24120] will be killed.

hosts中添加域名解析

linux中添加域名解析,比如在无法连接外网的环境中添加hosts,可以指向内网指定ip,另一种常用的技巧是添加10.211.55.45 ubuntu,可以直接ssh ubuntu就可以连接到远端服务器。

[root workspace]#cat /etc/hosts
127.0.0.1   localhost
127.0.1.1   parallels-Parallels-Virtual-Platform

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.211.55.45 ubuntu

ssh远程登录服务器

[root workspace]#ssh ubuntu
The authenticity of host 'ubuntu (10.211.55.45)' can't be established.
ECDSA key fingerprint is SHA256:doiViafWwhWy0CLyM1L9tM13ou91FMSC473lrx1lpLY.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'ubuntu' (ECDSA) to the list of known hosts.
root@ubuntu's password:
Last login: Sat Oct  5 18:06:07 2019 from ubuntu-18.04-10.211.55.49.shared
[root ~]#

docker命令

学习极客时间里的教程里遇到如下命令:

docker run -it --rm -v $(mktemp):/etc/resolv.conf feisky/dnsutils bash

其它参数比较熟悉,-v参数是--volume:绑定一个卷,意思是主机中随机的一个文件映射到容器里的/etc/resolv.conf,在容器里修改这个文件,主机里的文件也相应的修改。
Linux性能优化实战

min宏实现

如果实现min宏:

#define min(a,b) a<b?a:b

当按照如下方式使用时会存在问题:

int value = -2<3 ? 2 :3; // Oops.. result will be 2

正确的方式应该:

#define min(a,b) (  (a) < (b) ?(a):(b) )

但是当调用的时候传入的参数比如为i++,不能保证i++执行一次还是两次。

asprintf函数替代snprintf

stackoverflow上有一篇比较好的回答,最近刚定位和解决项目中一个与之类似的问题。
Why use asprintf() instead of sprintf()?
snprintf例子:

#include <stdio.h>
#include <string.h>

int main()
{
    char x[5]={0};

    int size = snprintf(x,5,"%s%s%s","12","34","56");
    printf("%d\n",size);

    return 0;
}

gdb查看数组x的内容,可以看到x[4]确实为结束符null.

Breakpoint 1, main () at sprintf.c:5
5   {
(gdb) n
6       char x[5]={0};
(gdb)
8       int size = snprintf(x,5,"%s%s%s","12","34","56");
(gdb)
9       printf("%d\n",size);
(gdb) p x
$1 = "1234"
(gdb) p/x x
$2 = {0x31, 0x32, 0x33, 0x34, 0x0}
(gdb)

sizeof(a++)

sizeof的一个使用问题,示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    int a = 10;
    size_t size = sizeof(a++);

    printf("size:%lu,a:%d\n",size,a);

    return 0;
}

程序输出:

[root systemProgramming]#./sizeof
size:4,a:10

原因及解释:
Why does sizeof(x++) not increment x?

检测竞争条件

维基百科关于竞争条件的解释:
竞争冒险
源代码:
simple_race.c

#include <pthread.h>
#include <stdio.h>

int Global;

void *Thread1(void *x) {
    Global++;
    return NULL;
}

int main() {
    pthread_t t[2];
    pthread_create(&t[0], NULL, Thread1, NULL);
    Global = 100;
    pthread_join(t[0], NULL);
}
// compile with gcc -fsanitize=thread -pie -fPIC -ltsan -g simple_race.c

运行结果:

[root systemProgramming]#./a.out
==================
WARNING: ThreadSanitizer: data race (pid=3111)
  Write of size 4 at 0x556865a43014 by main thread:
    #0 main /root/systemProgramming/simple_race.c:14 (a.out+0xa79)

  Previous write of size 4 at 0x556865a43014 by thread T1:
    #0 Thread1 /root/systemProgramming/simple_race.c:7 (a.out+0xa0c)
    #1 <null> <null> (libtsan.so.0+0x296ad)

  Location is global 'Global' of size 4 at 0x556865a43014 (a.out+0x000000201014)

  Thread T1 (tid=3113, finished) created by main thread at:
    #0 pthread_create <null> (libtsan.so.0+0x2bcee)
    #1 main /root/systemProgramming/simple_race.c:13 (a.out+0xa6a)

SUMMARY: ThreadSanitizer: data race /root/systemProgramming/simple_race.c:14 in main
==================
ThreadSanitizer: reported 1 warnings

ThreadSanitizer这个工具来自于Google,目前已集成到clang gcc等,可以用于检测程序中的竞争条件,上面的输出我们可以看到变量Global存在竞争(主线程和线程tid 3113)。

程序中设置断点

在使用gdb调试一些复杂的C程序时,在源代码中添加断点非常实用。
示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    int val = 1;
    val = 42;

    asm("int $3");//set a breakpoint here
    val = 7;

    return 0;
}

gcc -g -o gdb_ex gdb_ex.c
然后使用gdb调试,运行,可以看到程序自动断到11行。

(gdb) r
Starting program: /root/systemProgramming/gdb_ex

Program received signal SIGTRAP, Trace/breakpoint trap.
main () at gdb.c:11
11      val = 7;
(gdb)

gdb打印内存地址

示例代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
    char bad_string[3] = {'C', 'a', 't'};
    printf("%s", bad_string);

    return 0;
}

GDB Commands

x 0xaddress
x/nfu 0xaddress 

Examine the contents of memory.
Examine the contents of memory and specify formatting.
n: number of display items to print
f: specify the format for the output
u: specify the size of the data unit (eg. byte, word, ...)
Example: x/4dw var

我们可以使用这个命令查看内存中的值:

(gdb) b main
Breakpoint 1 at 0x6b2: file bad_str.c, line 6.
(gdb) r
Starting program: /root/systemProgramming/bad_str

Breakpoint 1, main () at bad_str.c:6
6   {
(gdb) n
7       char bad_string[3] = {'C', 'a', 't'};
(gdb)
8       printf("%s", bad_string);
(gdb)
10      return 0;
(gdb) x/16db bad_
bad_key_err  bad_string
(gdb) x/16db bad_string
0x7fffffffe3d5: 67  97  116 0   122 -22 -107    -65
0x7fffffffe3dd: -2  98  -31 0   71  85  85  85
(gdb) x/16xb bad_string
0x7fffffffe3d5: 0x43    0x61    0x74    0x00    0x7a    0xea    0x95    0xbf
0x7fffffffe3dd: 0xfe    0x62    0xe1    0x00    0x47    0x55    0x55    0x55

二级指针的理解

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    int num = 10;
    int i = 0;
    char **ptr = malloc(num * sizeof(char *));
    char str[10] = {0};

    for(i = 0;i < num;i++)
    {
    ptr[i] = malloc(10 * (i + 1) *sizeof(char));
    memset(str,0x0,sizeof(str));
    snprintf(str,sizeof(str),"%s_%d","string",i);
    memcpy(ptr[i],str,sizeof(str));
    }

    for(i = 0;i < num;i++)
    {
    printf("ptr[%d] = %s\n",i,ptr[i]);
    free(ptr[i]);
    ptr[i] = NULL;
    }

    free(ptr);
    ptr = NULL;
    return 0;
}

编译运行结果(使用valgrind检测内存泄露)

[root systemProgramming]#gcc -g -o pointer pointer.c
[root systemProgramming]#valgrind --leak-check=full ./pointer
==31696== Memcheck, a memory error detector
==31696== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==31696== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==31696== Command: ./pointer
==31696==
ptr[0] = string_0
ptr[1] = string_1
ptr[2] = string_2
ptr[3] = string_3
ptr[4] = string_4
ptr[5] = string_5
ptr[6] = string_6
ptr[7] = string_7
ptr[8] = string_8
ptr[9] = string_9
==31696==
==31696== HEAP SUMMARY:
==31696==     in use at exit: 0 bytes in 0 blocks
==31696==   total heap usage: 12 allocs, 12 frees, 1,654 bytes allocated
==31696==
==31696== All heap blocks were freed -- no leaks are possible
==31696==
==31696== For counts of detected and suppressed errors, rerun with: -v
==31696== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Char ** usage and printing