Skip to content

Commit

Permalink
gdb 调试多线程死锁,gdb 调试段错误,跨域问题,面试题:N 个线程循环打印,天空卫士 面试题
Browse files Browse the repository at this point in the history
  • Loading branch information
he3210 committed Mar 26, 2019
1 parent 5085faf commit ee8b994
Show file tree
Hide file tree
Showing 15 changed files with 620 additions and 293 deletions.
579 changes: 288 additions & 291 deletions algorithm notebook/二叉树的遍历.org

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion algorithm notebook/循环链表.org
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
p2 = h2;
}

int offset = len1-len2;
int offset = len1 < len2 ? len2-len1 : len1 - len2;
for(int i = 0; i < offset; ++i)
p1 = p1->next;
while(p1 && p2 && p1 != p2)
Expand Down
Binary file added images/bitree.tar.gz
Binary file not shown.
Binary file added images/bitree1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/bitree2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/bitree3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/天空卫士.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 77 additions & 0 deletions others notebook/gdb 调试多线程死锁.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#+TITLE: gdb 调试多线程死锁

** 死锁示例代码
#+BEGIN_SRC c
#include<pthread.h>
#include<unistd.h>
#include<stdio.h>

void* alive_thread(void *arg)
{
while(1)
{
usleep(1000*1000);
}
}

void* dead_thread(void *arg)
{
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
usleep(1000*1000);
fprintf(stderr, "timeout we will start dead lock\n");
pthread_mutex_lock(&mutex);
pthread_mutex_lock(&mutex);
}

int main(void)
{
pthread_t alive_pid;
pthread_create(&alive_pid, NULL, alive_thread, NULL);
pthread_t dead_pid;
pthread_create(&dead_pid, NULL, dead_thread, NULL);
void *ret1 = NULL;
pthread_join(alive_pid, &ret1);
void *ret2 = NULL;
pthread_join(dead_pid, &ret2);

return 0;
}
#+END_SRC

** 编译调试

#+BEGIN_SRC bash
$ gcc main.c -o main -g -lpthread
$ ./main
timeout we will start dead lock
#+END_SRC

先不要杀死该进程,打开另一个终端进行 gdb 调试

#+BEGIN_SRC bash
$ ps -ef | grep main | grep -v grep
root 22895 22671 0 15:30 pts/0 00:00:00 ./main
$ gdb main 22895
...
(gdb) info threads
Id Target Id Frame
3 Thread 0x7f03a291a700 (LWP 22896) "main" 0x00007f03a29da06d in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
2 Thread 0x7f03a2119700 (LWP 22897) "main" 0x00007f03a2ce789c in __lll_lock_wait () from /lib/x86_64-linux-gnu/libpthread.so.0
,* 1 Thread 0x7f03a310b700 (LWP 22895) "main" 0x00007f03a2ce2148 in pthread_join () from /lib/x86_64-linux-gnu/libpthread.so.0
(gdb) thread 2
[Switching to thread 2 (Thread 0x7f03a2119700 (LWP 22897))]
#0 0x00007f03a2ce789c in __lll_lock_wait () from /lib/x86_64-linux-gnu/libpthread.so.0
(gdb) bt
#0 0x00007f03a2ce789c in __lll_lock_wait () from /lib/x86_64-linux-gnu/libpthread.so.0
#1 0x00007f03a2ce3065 in _L_lock_858 () from /lib/x86_64-linux-gnu/libpthread.so.0
#2 0x00007f03a2ce2eba in pthread_mutex_lock () from /lib/x86_64-linux-gnu/libpthread.so.0
#3 0x000000000040084c in dead_thread ()
#4 0x00007f03a2ce0e9a in start_thread () from /lib/x86_64-linux-gnu/libpthread.so.0
#5 0x00007f03a2a0e36d in clone () from /lib/x86_64-linux-gnu/libc.so.6
#6 0x0000000000000000 in ?? ()
#+END_SRC

第一个线程运行到 pthread_join,显然是主线程
第二个线程运行到 __lll_lock_wait(),显然是 dead_thread() 函数中第二个 pthread_mutex_lock(&mutex) 加锁时在等待锁。通过 thread 2 跳转到该线程,bt 查看堆栈,显然发生死锁
第三个线程运行到 nanosleep(),显然是 alive_thread() 函数中无限休眠
80 changes: 80 additions & 0 deletions others notebook/gdb 调试段错误.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#+TITLE: gdb 调试段错误

** 段错误产生原因

段错误是指非法访问内存产生的错误

*** 访问不存在的内存地址
#+BEGIN_SRC c
#include<stdio.h>
#include<stdlib.h>
void main()
{
int *ptr = NULL;
*ptr = 0;
}
#+END_SRC

*** 访问系统保护的内存地址
#+BEGIN_SRC c
#include<stdio.h>
#include<stdlib.h>
void main()
{
int *ptr = (int *)0;
*ptr = 100;
}
#+END_SRC

*** 访问只读的内存地址
#+BEGIN_SRC c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void main()
{
char *ptr = "test";
strcpy(ptr, "TEST");
}
#+END_SRC

*** 栈溢出
#+BEGIN_SRC c
#include<stdio.h>
#include<stdlib.h>
void main()
{
main();
}
#+END_SRC

** gdb 调试
*** 以栈溢出为例直接使用 gdb 调试
#+BEGIN_SRC bash
$ gcc segfault.c -g -o segfault
$ ./segfault
Segmentation fault (core dumped)
$ gdb segfault
...
(gdb) run # 使用 run 命令运行后,会提示段错误出现在文件中的位置,如下所示
Starting program: /home/he/Desktop/GDB/segfault

Program received signal SIGSEGV, Segmentation fault.
main () at segfault.c:5
5 main();
#+END_SRC

*** 使用 core 文件调试段错误
#+BEGIN_SRC bash
$ gcc segfault.c -g -o segfault
$ ulimit -c # 显示 core 文件的大小
$ ulimit -c unlimited # 把 core 文件设为最大,只对本次有效
$ ./segfault # 运行程序生成 core 文件,设生成了 core.7963
Segmentation fault (core dumped)
$ gdb segfault core.7963
...
Core was generated by 'n./segfault'.
Program terminated with signal 11, Segmentation fault.
#0 main () at test2.c:12
12 main();
#+END_SRC
4 changes: 4 additions & 0 deletions others notebook/others.org
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@
- [[./打印目录下的文件.org][打印目录下的文件]]
- [[./offsetof和container_of.org][小技巧:offsetof 和 container_of 根据结构体成员计算结构体地址]]
- [[./signal和sigaction处理逻辑.org][连续到来多个信号时,signal 和 sigaction 的处理逻辑]]
- [[./跨域问题.org][跨域问题]]
- [[./面试题:N 个线程循环打印.org][面试题:N 个线程循环打印]]
- [[./gdb 调试段错误.org][gdb 调试段错误]]
- [[./gdb 调试多线程死锁.org][gdb 调试多线程死锁]]
5 changes: 5 additions & 0 deletions others notebook/跨域问题.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#+TITLE: 跨域问题

参考
- [[http://www.ruanyifeng.com/blog/2016/04/cors.html][跨域资源共享 CORS 详解--阮一峰]]
- [[https://segmentfault.com/a/1190000015597029][不要再问我跨域的问题了]]
88 changes: 88 additions & 0 deletions others notebook/面试题:N 个线程循环打印.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#+TITLE: 面试题:N 个线程循环打印

** 问题
阿里多线程面试题
#+BEGIN_QUOTE
通过 N 个线程顺序循环打印从 0 至 100,如给定 N = 3 则输出:
thread0: 0
thread1: 1
thread2: 2
thread0: 3
thread1: 4
...
#+END_QUOTE

** 代码
#+BEGIN_SRC c
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>

#define N 7 // 线程数
#define M 101 // 打印数字的总数

sem_t sem[N];

void * func(void *arg)
{
int id = (int)arg; // 线程编号
int offset = id < (M%N) ? 1 : 0;
for(int i = 0; i < M/N + offset; ++i)
{
sem_wait(&sem[id]);
printf("thread %d: %d\n", id, i*N + id);
sem_post(&sem[(id+1)%N]); // 下一个信号灯灯值加一,唤醒下一个线程
}
}

int main()
{
sem_init(&sem[0], 0, 1); // 初始化,只有第一个信号灯中有一个访问资源
for(int i = 1; i < N; ++i)
sem_init(&sem[i], 0, 0);

pthread_t thread[N];
for(int i = 0; i < N; ++i)
{
int arg = i;
pthread_create(&thread[i], NULL, &func, (void*)arg);
}

for(int i = 0; i < N; ++i)
pthread_join(thread[i], NULL);

for(int i = 0; i < N; ++i)
sem_destroy(&sem[i]);

return 0;
}
#+END_SRC

** 执行结果
#+BEGIN_SRC shell
$ gcc test.c -o test -std=c99 -pthread
$ ./test
thread0: 0
thread1: 1
thread2: 2
thread3: 3
thread4: 4
thread5: 5
thread6: 6
thread0: 7
thread1: 8
thread2: 9
thread3: 10
thread4: 11
thread5: 12
thread6: 13
thread0: 14
...
thread3: 94
thread4: 95
thread5: 96
thread6: 97
thread0: 98
thread1: 99
thread2: 100
#+END_SRC
1 change: 1 addition & 0 deletions qu/qu.org
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

- [[./ShadowsocksR.org][ShadowsocksR 配置]]
- [[./redis 面试题.org][redis 面试题]]
- [[./天空卫士 面试题.org][天空卫士 面试题]]
44 changes: 44 additions & 0 deletions qu/天空卫士 面试题.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#+TITLE: 天空卫士 面试题

* 岗位描述
[[../images/天空卫士.PNG]]

* 面试题
- 如何判断一个单链表有环,设置2个指针,一个快一个满

- 二叉树先序遍历、中序遍历、后续遍历,根据2个确定一个

- 线程互斥锁、条件锁的生产者消费者

- gdb 调试全面掌握

- 排序

- 父子进程之间的最优通信方式

- 哈希

- pthread_self() pthread_...

- epoll ET LT

- 父进程如何获取退出状态

- 写个 Deamon

- EAGAIN EINTR send recv read write

- 惊群

- errno 为全局变量,为什么多线程不用加锁
在支持线程的环境中,多个线程共享进程地址空间,每个线程都有属于它自己的局部 errno 以避免一个线程干扰另一个线程。
对于 errno 应当知道两条规则。
1. 如果没有出错,则其值不会被一个例程清除。因此,仅当函数的返回值指明出错时,才检验其值。
2. 任一函数都不会将 errno 值设置为 0,在 <errno.h> 中定义的所有常量都不为 0。

- __VA_ARGS__ 是一个可变参数的宏,这个可宏是新的C99规范中新增的,目前似乎 gcc 和 VC6.0 之后的都支持(VC6.0 的编译器不支持)。宏前面加上 ## 的作用在于,当可变参数的个数为 0 时,这里的 ## 起到把前面多余的 "," 去掉的作用。
- __FILE__ 宏在预编译时会替换成当前的源文件名
- __LINE__ 宏在预编译时会替换成当前的行号
- __FUNCTION__ 宏在预编译时会替换成当前的函数名称

- recv read 返回0
33 changes: 32 additions & 1 deletion redis notebook/redis源码阅读计划.org
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

* 底层数据结构实现
** DONE sds

文件:sds.c 和 sds.h
**数据结构**
#+BEGIN_SRC c
typedef char *sds; // sds 是一个 char* 指针,指向 sdshdr.buf
Expand All @@ -31,6 +31,37 @@
- 惰性空间释放。sds 字符串缩短时,并不会主动释放多余空间,只是记录在 sdshdr.free 中。当然,sds 也提供了相应的 API,真正的释放 sds 的未使用空间

** DONE 双端链表
文件:adlist.c 和 adlist.h

**数据结构**
#+BEGIN_SRC c
#define AL_START_HEAD 0 // 从表头向表尾进行迭代
#define AL_START_TAIL 1 // 从表尾到表头进行迭代

typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;

typedef struct list {
listNode *head;
listNode *tail;
void *(*dup)(void *ptr); // 节点值复制函数
void (*free)(void *ptr); // 节点值释放函数
int (*match)(void *ptr, void *key); // 节点值对比函数
unsigned long len; // 链表所包含的节点数量
} list;

typedef struct listIter {
listNode *next; // 当前迭代到的节点
int direction; // 迭代的方向
} listIter;
#+END_SRC

**redis 双端链表特点**
- redis 的链表是无环链表。因为链表表头结点的前置结点和表尾结点的后置结点都指向 NULL
- 通过为链表设置不同类型的特定函数(dup、free、match 函数),redis 的链表可以用于保存各种不同类型的值

** DONE 字典

Expand Down

0 comments on commit ee8b994

Please sign in to comment.