├── 1C语言
└── C语言.md
├── 3线程与进程
├── fifo.c
├── fifo_read.c
├── fifo_write.c
├── msg_read.c
├── msg_write.c
├── mutex.c
├── sem.c
├── shm_read.c
├── shm_write.c
├── sig.c
├── 死锁.png
└── 进程线程.md
├── 4IO模型
├── IO模型.md
└── io模型.png
├── 5网络编程
├── 网络拓扑.png
└── 网络编程.md
├── 6错题集
├── 错题集1.md
├── 错题集2.md
└── 面试题1.md
└── README.md
/1C语言/C语言.md:
--------------------------------------------------------------------------------
1 | # C语言
2 |
3 | **1.static的用法**
4 |
5 | + **作用于函数局部变量**
6 |
7 | 特点:初始化为0,运行一开始开辟内存,内存放在全局,增加了变量的存活时间
8 |
9 | + **作用于全局函数**
10 |
11 | 特点:静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可定义相同变量名,两者互不影响
12 |
13 | + **作用于全局变量**
14 |
15 | 特点:静态函数只能在声明它的文件中可见,其他文件不能引用该函数,其他文件函数名可相同,互不影响
16 |
17 | ----------------
18 |
19 | **2.volatile的用法**
20 |
21 | 因为访问寄存器要比访问内存单元快的多,所以编译器一般都会做减少存取内存的优化,但有可能会读脏数据。当要求使用volatile声明变量值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。精确地说就是,遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问;如果不使用volatile,则编译器将对所声明的语句进行优化。
22 |
23 | 简洁的说就是:volatile关键词影响编译器编译的结果,用volatile声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化,以免出错。
24 |
25 | **面试常问:**
26 |
27 | ①一个参数既可以是const还可以是volatile吗?
28 |
29 | 答:是的。一个例子是只读寄存器状态。它是volatile因为它可能被意向不到地改变,它是const因为程序不应该试图去修改它。
30 |
31 | ②一个指针可以是volatile吗?
32 |
33 | 答:是的。一个例子是当一个中断服务子程序修改指向一个buffer的指针时。
34 |
35 | ------------
36 |
37 | **3.const的用法**
38 |
39 | * const修饰变量
40 |
41 | > ①修饰的变量可以认为只有只读属性
42 | >
43 | > ②在声明时必须对其进行初始化
44 |
45 | * const修饰指针变量
46 |
47 | > ① int *const p; 该语句表示指向整形的**常量指针**,它不能在指向别的变量,但指向变量的值可以修改
48 | >
49 | > ② const int *p; 该语句表示指向整形常量的**指针**,它的指向可以修改,指向常量的值不能修改
50 | >
51 | > ③ int const *p; 该语句和②相同
52 | >
53 | > ④ const int * const p; 该语句表示指向整形常量的常量指针。既不能改变指向也不能改变指向的值
54 | >
55 | > ⑤ int const * const p; 该语句与④相同
56 |
57 | * const修饰函数形参
58 |
59 | >通常用于参数为指针或引用的情况,且只能修饰输入参数
60 |
61 | * const修饰函数返回值
62 |
63 | > 使用情况较少
64 |
65 | -----
66 |
67 | **4.堆栈队列**
68 |
69 | + 堆
70 |
71 | > ①堆是特殊的“队列”,从堆中取出元素是按照**元素优先级大小**,而不是元素进入的先后
72 | >
73 | > ②堆是一颗完全二叉树,其结点的值大于或小于其子结点的值(大于是最大堆,小于是最小堆)
74 |
75 | + 栈
76 |
77 | > 一种特殊的线性表,只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除的一端称为栈顶,另一端称为栈底,遵循**先进后出**
78 |
79 | + 队列
80 |
81 | > 队列是一种操作受限的线性表,是指只允许在固定的一端进行插入的数据结构,遵循**先进先出**
82 |
83 | ----
84 |
85 | **5.大端小端**
86 |
87 | 16进制数:unsigned int value = 0x12345678 保存在 unsigned char buf[3]中
88 |
89 | + **大端**
90 |
91 | |buf[0]| 0x12 |
92 | |:---:|:---:|
93 | |buf[1]| 0x34 |
94 | |buf[2]| 0x56 |
95 | |buf[3 | 0x78 |
96 |
97 | + **小端**
98 | |buf[0]| 0x78 |
99 | |:---:|:---:|
100 | |buf[1]| 0x56 |
101 | |buf[2]| 0x34 |
102 | |buf[3 | 0x12 |
103 |
104 | ---
105 |
106 | **6.结构体对齐**
107 |
108 | ```c
109 | struct A {
110 | char a; //1
111 | char b; //1
112 | char c; //1
113 | };
114 | //进行整体对齐,最大类型为1<对齐系数4,按1整体对齐,所以1+1+1=3
115 |
116 | struct B {
117 | int a; //4
118 | char b; //1
119 | short c;//2
120 | };
121 | //进行整体对齐,最大类型为4=对齐系数4,所以按4整体对齐4 1+2=3<4 对齐4 所以4+4=8
122 |
123 | struct C {
124 | char b; //1
125 | int a; //4
126 | short c;//2
127 | };
128 | //进行整体对齐,最大类型为4=对齐系数4,所以按4整体对齐 1<4(对齐4) 4=4 2<4(对齐4) 所以4+4+4=12
129 |
130 | #pragma pack(2)
131 | struct D {
132 | char b; //1
133 | int a; //4
134 | short c;//2
135 | };
136 | //进行整体对齐,最大类型为4>对齐系数n(2),所以按2整体对齐 1<2(对齐2)4>2(2的倍数) 2=2 所以2+4+2=8
137 | ```
138 |
139 | ```c
140 | 有一个如下的结构体:
141 | struct A{
142 | long a1;
143 | short a2;
144 | int a3;
145 | int *a4;
146 | };
147 | 请问在64位编译器下用sizeof(struct A)计算出的大小是多少?
148 |
149 |
150 | 答案及解析:24
151 | 64位编译器下:指针8字节(一定不能忘记),题目不说默认4字节对齐
152 | long a1; //8
153 | short a2; //2 8+2=10(不是4的倍数)对齐到4的倍数12
154 | int a3; //4 4+12=16(4的倍数)
155 | int *a4; //8 8+16=24(4的倍数)
156 | ```
157 |
158 | ---
159 |
160 | **7.段**
161 |
162 | + 代码段:代码段就是程序中的可执行部分,直观理解代码段就是函数堆积组成的
163 | + 数据段:数据段就是程序中的数据,直观理解就是C语言程序中的全局变量
--------------------------------------------------------------------------------
/3线程与进程/fifo.c:
--------------------------------------------------------------------------------
1 | /*************************************************************************
2 | > File Name: fifo.c
3 | > Author:
4 | > Mail:
5 | > Created Time:
6 | ************************************************************************/
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | int main()
14 | {
15 | int pd[2];
16 | int ret;
17 | char buf[100];
18 | pid_t pid;
19 | //创建管道
20 | ret = pipe(pd);
21 | if(ret != 0)
22 | {
23 | perror("pipe err!");
24 | return -1;
25 | }
26 | //创建子进程
27 | pid = fork();
28 | if(pid < 0)
29 | {
30 | perror("fork err!");
31 | return -1;
32 | }
33 | else if(pid == 0)
34 | {
35 | //在子进程中
36 | //关闭写端
37 | close(pd[1]);
38 |
39 | while(1)
40 | {
41 | memset(buf,0,sizeof(buf));
42 | //从管道中读取数据
43 | ret = read(pd[0],buf,sizeof(buf));
44 | if(ret == 0)
45 | {
46 | printf("the write is close!\n");
47 | exit(0);
48 | }
49 | else
50 | {
51 | printf("%s",buf);
52 | }
53 |
54 | }
55 | }
56 | else if(pid > 0)
57 | {
58 | //在父进程中
59 | memset(buf,0,sizeof(buf));
60 | stpcpy(buf,"hello world!\n");
61 | //关闭读端
62 | close(pd[0]);
63 | //向管道中写入数据
64 | write(pd[1],buf,sizeof(buf));
65 | close(pd[1]);
66 | wait(&ret);
67 | }
68 |
69 |
70 | return 0;
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/3线程与进程/fifo_read.c:
--------------------------------------------------------------------------------
1 | /*************************************************************************
2 | > File Name: read.c
3 | > Author:
4 | > Mail:
5 | > Created Time:
6 | ************************************************************************/
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #define PATH "/home/xwq/tmp/fifo"
15 |
16 |
17 | int main()
18 | {
19 | int ret,fd,buf;
20 | //创建有名管道文件
21 | ret = mkfifo(PATH,0666);
22 | if(ret != 0)
23 | {
24 | perror("mkfifo err!");
25 | return -1;
26 | }
27 | //打开有名管道,生成文件描述符
28 | fd = open(PATH,O_RDONLY);
29 | if(fd < 0)
30 | {
31 | perror("open fifo err!");
32 | return -1;
33 | }
34 |
35 | //循环读取数据
36 | while(1)
37 | {
38 | buf =0;
39 | ret = read(fd,&buf,sizeof(int));
40 | if(ret == 0)
41 | {
42 | fprintf(stderr,"the fifo is close!\n");
43 | return -1;
44 | }
45 | else
46 | {
47 | printf("buf = %d\n",buf);
48 | }
49 | }
50 |
51 | return 0;
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/3线程与进程/fifo_write.c:
--------------------------------------------------------------------------------
1 | /*************************************************************************
2 | > File Name: fifo.c
3 | > Author:
4 | > Mail:
5 | > Created Time:
6 | ************************************************************************/
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #define PATH "/home/xwq/tmp/fifo"
15 |
16 |
17 | int main()
18 | {
19 | int fd,ret,buf;
20 | //打开有名管道
21 | fd = open(PATH,O_WRONLY);
22 | if(fd < 0)
23 | {
24 | perror("open fifo err!");
25 | return -1;
26 | }
27 |
28 | buf = 7;
29 | //向管道中写入数据
30 | while(buf--)
31 | {
32 | write(fd,&buf,sizeof(int));
33 | sleep(1);
34 | }
35 | close(fd);
36 |
37 | return 0;
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/3线程与进程/msg_read.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 |
12 | #define MSG_LEN 32
13 | struct myMsg {
14 | long mtype;
15 | char buf[MSG_LEN];
16 | };
17 |
18 | // gcc write.c -o write
19 | int main() {
20 | // 1 创建 "消息队列" key = 0x1000
21 |
22 | key_t key = 0x1000;
23 | // int msgget(key_t key, int msgflg);
24 | int msgid = msgget(key, IPC_CREAT | 0666);
25 | if(msgid == -1){
26 | perror("msgget IPC_CREAT | 0666 ERROR!");
27 | exit(0);
28 | }
29 |
30 | // 2. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
31 | struct myMsg msg = {0};
32 | long type = 0;
33 | if(msgrcv(msgid, &msg, MSG_LEN, type, 0) == -1){
34 | perror("Msg Send error!");
35 | exit(0);
36 | }
37 |
38 | printf("%ld: %s\n", msg.mtype, msg.buf);
39 |
40 |
41 | return 0;
42 | }
43 |
--------------------------------------------------------------------------------
/3线程与进程/msg_write.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 |
12 | #define MSG_LEN 32
13 | struct myMsg {
14 | long mtype;
15 | char buf[MSG_LEN];
16 | };
17 |
18 | // gcc write.c -o write
19 | int main() {
20 | // 1 创建 "消息队列" key = 0x1000
21 |
22 | key_t key = 0x1000;
23 | // int msgget(key_t key, int msgflg);
24 | int msgid = msgget(key, IPC_CREAT | 0666);
25 | if(msgid == -1){
26 | perror("msgget IPC_CREAT | 0666 ERROR!");
27 | exit(0);
28 | }
29 |
30 | // 2. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
31 | struct myMsg msg = { 100, "hello world"};
32 | if(msgsnd(msgid, &msg, MSG_LEN, 0) == -1) {
33 | perror("Msg Send error!");
34 | exit(0);
35 | }
36 |
37 |
38 | // 3. 删除MSG对象
39 | // int msgctl(int msqid, int cmd, struct msqid_ds *buf);
40 | if(msgctl(msgid, IPC_RMID, 0) == -1){
41 | perror("IPC_RMID ERROR!");
42 | exit(0);
43 | }
44 |
45 | return 0;
46 | }
47 |
--------------------------------------------------------------------------------
/3线程与进程/mutex.c:
--------------------------------------------------------------------------------
1 | /*************************************************************************
2 | > File Name: mutex.c
3 | > Author:
4 | > Mail:
5 | > Created Time:
6 | ************************************************************************/
7 |
8 | #include
9 | #include
10 |
11 | pthread_mutex_t mut; //锁本身 锁终端的打印
12 |
13 | void *th1 (void *tmp)
14 | {
15 | int ret,i= 5;
16 | //线程1获得锁
17 | ret = pthread_mutex_lock(&mut);
18 | if(ret < 0)
19 | {
20 | fprintf(stderr,"pthread_mutex_lock err!\n");
21 | return NULL;
22 | }
23 | //使用资源 终端的打印
24 | while(i--)
25 | {
26 | printf("in th1 i = %d\n",i);
27 | sleep(1);
28 | }
29 |
30 | //释放锁
31 | ret = pthread_mutex_unlock(&mut);
32 | if(ret < 0)
33 | {
34 | fprintf(stderr,"pthread_mutex_unlock err!\n");
35 | return NULL;
36 | }
37 |
38 | pthread_exit(NULL);
39 | }
40 |
41 | void *th2 (void *tmp)
42 | {
43 | int ret,i= 10;
44 | //线程2获得锁
45 | ret = pthread_mutex_lock(&mut);
46 | if(ret < 0)
47 | {
48 | fprintf(stderr,"pthread_mutex_lock err!\n");
49 | return NULL;
50 | }
51 | //使用资源 终端的打印
52 | while(i-- > 5 )
53 | {
54 | printf("in th2 i = %d\n",i);
55 | sleep(1);
56 | }
57 |
58 | //释放锁
59 | ret = pthread_mutex_unlock(&mut);
60 | if(ret < 0)
61 | {
62 | fprintf(stderr,"pthread_mutex_unlock err!\n");
63 | return NULL;
64 | }
65 |
66 |
67 | pthread_exit(NULL);
68 | }
69 |
70 | int main()
71 | {
72 | pthread_t tid1,tid2;
73 | int ret;
74 | //初始化互斥锁
75 | ret = pthread_mutex_init(&mut,NULL);
76 | if(ret < 0)
77 | {
78 | fprintf(stderr,"pthread_mutex_init err!\n");
79 | return -1;
80 | }
81 |
82 | pthread_create(&tid1,NULL,th1,NULL);
83 | pthread_create(&tid2,NULL,th2,NULL);
84 |
85 | pthread_join(tid1,NULL);
86 | pthread_join(tid2,NULL);
87 | //销毁互斥锁
88 | ret = pthread_mutex_destroy(&mut);
89 | if(ret < 0)
90 | {
91 | fprintf(stderr,"pthread_mutex_destroy err!\n");
92 | return -1;
93 | }
94 |
95 | return 0;
96 | }
97 |
98 |
99 |
--------------------------------------------------------------------------------
/3线程与进程/sem.c:
--------------------------------------------------------------------------------
1 | /*************************************************************************
2 | > File Name: sem.c
3 | > Author:
4 | > Mail:
5 | > Created Time:
6 | ************************************************************************/
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | sem_t sem;
14 | //生产者
15 | void * pthread1(void * tmp)
16 | {
17 | // v操作 释放资源 产出产品
18 | int i = 6,num;
19 | while(i--)
20 | {
21 | printf("我生产了一个产品!\n");
22 | sem_post(&sem);
23 | sem_getvalue(&sem,&num);
24 | printf("库存数量 %d\n",num);
25 | sleep(1);
26 | }
27 |
28 | pthread_exit(NULL);
29 | }
30 |
31 | //消费者 消费产品
32 | void * pthread2(void * tmp)
33 | {
34 | //p操作 申请得到产品
35 | int num;
36 | while(1)
37 | {
38 | sem_wait(&sem);
39 | printf("我得到了一个产品!消费掉了\n");
40 | sem_getvalue(&sem,&num);
41 | printf("消费后库存数量 %d\n",num);
42 | sleep(1);
43 | }
44 | pthread_exit(NULL);
45 | }
46 |
47 | int main()
48 | {
49 | pthread_t tid1,tid2;
50 | int ret;
51 | ret = sem_init(&sem, 0, 0);
52 | if(ret != 0)
53 | {
54 | fprintf(stderr,"sem_init err!\n");
55 | return -1;
56 | }
57 |
58 | //创建生产者 线程
59 | pthread_create(&tid1,NULL,pthread1,NULL);
60 |
61 | //创建消费者 线程
62 | pthread_create(&tid2,NULL,pthread2,NULL);
63 |
64 |
65 |
66 | pthread_join(tid1,NULL);
67 | pthread_join(tid2,NULL);
68 |
69 | return 0;
70 | }
71 |
72 |
73 |
--------------------------------------------------------------------------------
/3线程与进程/shm_read.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 |
12 | // gcc write.c -o write
13 | int main() {
14 | // 1 创建 "共享内存" key = 0x1000
15 | // int shmget(key_t key, size_t size, int shmflg);
16 | int shmid = shmget(0x1000, 4096, IPC_CREAT | 0666);
17 | if(shmid == -1){
18 | perror("shmget 0x1000:4096 ERROR!");
19 | exit(0);
20 | }
21 |
22 |
23 | // 2. 映射
24 | // void *shmat(int shmid, const void *shmaddr, int shmflg); // SHM_REONLY
25 | void *shm_addr = shmat(shmid, NULL, 0);
26 | if(shm_addr == (void *)-1){
27 | perror("shmat ERROR!");
28 | exit(0);
29 | }
30 |
31 | // 3. 访问
32 | char *str = (char *)shm_addr;
33 | puts(str);
34 |
35 |
36 | // 4. int shmdt(const void *shmaddr);
37 | if(shmdt(shm_addr) == -1){
38 | perror("shmdt() ERROR!");
39 | exit(0);
40 | }
41 |
42 | exit(0);
43 |
44 | return 0;
45 | }
46 |
--------------------------------------------------------------------------------
/3线程与进程/shm_write.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 |
12 | // gcc write.c -o write
13 | int main() {
14 | // 1 创建 "共享内存" key = 0x1000
15 | // int shmget(key_t key, size_t size, int shmflg);
16 | int shmid = shmget(0x1000, 4096, IPC_CREAT | 0666);
17 | if(shmid == -1){
18 | perror("shmget 0x1000:4096 ERROR!");
19 | exit(0);
20 | }
21 |
22 |
23 | // 2. 映射
24 | // void *shmat(int shmid, const void *shmaddr, int shmflg); // SHM_REONLY
25 | void *shm_addr = shmat(shmid, NULL, 0);
26 | if(shm_addr == (void *)-1){
27 | perror("shmat ERROR!");
28 | exit(0);
29 | }
30 |
31 | // 3. 访问
32 | char *str = (char *)shm_addr;
33 | strcpy(str, "hello world");
34 |
35 |
36 | // 4. int shmdt(const void *shmaddr);
37 | if(shmdt(shm_addr) == -1){
38 | perror("shmdt() ERROR!");
39 | exit(0);
40 | }
41 |
42 |
43 | // 5. delete
44 |
45 | exit(0);
46 |
47 | return 0;
48 | }
49 |
--------------------------------------------------------------------------------
/3线程与进程/sig.c:
--------------------------------------------------------------------------------
1 | /*************************************************************************
2 | > File Name: sig.c
3 | > Author:
4 | > Mail:
5 | > Created Time: Fri 21 Apr 2017 02:40:36 PM HKT
6 | ************************************************************************/
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | void sighandler(int sig)
16 | {
17 | printf("sig=%d\n",sig);
18 | return ;
19 | }
20 |
21 |
22 |
23 |
24 | int main()
25 | {
26 | pid_t pid;
27 | int ret;
28 | //创建子进程
29 | pid = fork();
30 | if(pid < 0)
31 | {
32 | perror("fork err!");
33 | return -1;
34 | }
35 | else if(pid == 0)
36 | {
37 | //在子进程中
38 | kill(getppid(),SIGCHLD);
39 | sleep(2);
40 | exit(0);
41 | }
42 | else if (pid > 0)
43 | {
44 | //在父进程中
45 | signal(SIGCHLD, sighandler);
46 |
47 | wait(&ret);
48 | }
49 |
50 | return 0;
51 | }
52 |
53 |
54 |
--------------------------------------------------------------------------------
/3线程与进程/死锁.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengxinfd/some-method/21865c34241a69fdb6873bb52cbb067b84018ddf/3线程与进程/死锁.png
--------------------------------------------------------------------------------
/3线程与进程/进程线程.md:
--------------------------------------------------------------------------------
1 | # 进程与线程
2 |
3 | **1.进程间的通信方式**
4 |
5 | + **管道**
6 |
7 | 管道是一种把两个进程之间的标准输入和标准输出连接起来的机制,可分为无名管道和有名管道,用于具有亲缘关系进程间的通信。有名管道克服了没有名字的限制,因此允许无亲缘关系进程间的通信。
8 |
9 | **特点**
10 |
11 | > ①只能在有亲缘关系的进程之间进行通信,也就是在父子进程之间通信
12 | >
13 | > ②单向通信,一个读端,一个写端。如果要双向通信就要建立两个管道
14 | >
15 | > ③接收数据流,与数据格式无关
16 | >
17 | > ④一般而言,进程退出时管道即被释放,因此管道的生命周期会随进程的关闭而消亡
18 | >
19 | > ⑤同步互斥原则,内核会对管道操作进行同步和互斥
20 |
21 | [无名管道参考](https://github.com/chengxinfd/some-method/blob/master/3线程与进程/fifo.c)
22 |
23 | [有名管道**读**操作参考](https://github.com/chengxinfd/some-method/blob/master/3线程与进程/fifo_read.c)
24 |
25 | [有名管道**写**操作参考](https://github.com/chengxinfd/some-method/blob/master/3线程与进程/fifo_write.c)
26 |
27 | + **信号**
28 |
29 | 信号机制是UNIX系统中最为古老的进程之间通信机制。它用于一个进程或多个进程之间传递异步信号,来通知进程有某种事件发生,除了用于进程间通信外,使用信号还可以使进程发送信号给进程本身。
30 |
31 | [信号参考](https://github.com/chengxinfd/some-method/blob/master/3线程与进程/sig.c)
32 |
33 | + **消息队列**
34 |
35 | 消息队列是内核地址空间中的消息的链表,通过Linux内核在进程之间传递内容。消息顺序地发送到消息队列中,并以几种不同的方式从队列中获取,每个消息队列可以用IPC标识符唯一的进行标识。不同的消息队列之间是相互独立的,每个消息队列中的消息构成一个独立的链表。有写权限的进程可以对消息队列添加消息,有读权限的进程可以对消息队列读取信息
36 |
37 | **特点**
38 |
39 | > ①消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限的缺点。消息队列 是基于消息的,而管道是基于字节流的,且消息队列的读取不一定先入先出
40 | >
41 | > ②消息队列中每个消息的最大长度是有上限的,每个消息队列的总的字节数也是有上限的,系统上消息队 列的总数也有一个上限
42 |
43 | [消息队列**读**参考](https://github.com/chengxinfd/some-method/blob/master/3线程与进程/msg_read.c)
44 |
45 | [消息队列**写**参考](https://github.com/chengxinfd/some-method/blob/master/3线程与进程/msg_write.c)
46 |
47 | + **共享内存**
48 |
49 | 共享内存是在多个进程之间共享一块内存区域的一种进程间的通信方式,它是通过在多个进程之间对内存段进行映射的方式来实现共享内存的,是进程间通信最快捷的方式,之所以最快,是因为共享内存方式的通信没有中间过程,而管道、消息队列等方式则是需要将数据通过中间机制进行转换。共享内存往往与其他通信机制(如信号量)结合使用,来达到进程间的同步及互斥
50 |
51 | **特点**
52 |
53 | > ①共享内存就是允许两个不相关的进程访问同一个内存
54 | >
55 | > ②共享内存是两个正在运行的进程之间共享和数据的最有效的方式
56 | >
57 | > ③共享内存不提供任何互斥和同步机制,一般用信号量对临界资源进行保护
58 | >
59 | > ④不同进程之间共享的内存通常安排为同一段物理内存
60 |
61 | [共享内存**读**参考](https://github.com/chengxinfd/some-method/blob/master/3线程与进程/shm_read.c)
62 |
63 | [共享内存**写**参考](https://github.com/chengxinfd/some-method/blob/master/3线程与进程/shm_write.c)
64 |
65 | + **信号量**
66 |
67 | 信号量是一种计数器,用来控制对多个进程共享的资源所进行的访问,它们常常被用作一个锁机制,在某个进程正在对特定资源进行操作时,信号量可以防止另一个进程去访问它。简单来说就是信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行P(申请)V(释放)操作
68 |
69 | + **socket**
70 |
71 | 最为一般的进程间通信机制。可用于不同机器之间的不同进程之间的通信
72 |
73 | **2.线程同步方法**
74 |
75 | + **互斥锁(mutex)**
76 |
77 | 通过锁机制实现线程间的同步
78 |
79 | ```c
80 | 1、初始化互斥锁;
81 | int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex attr_t *mutexattr);
82 | //mutex:锁容器; mutexattr:锁的属性NULL;
83 |
84 | 2、申请锁,如果锁不可用,阻塞等待
85 | int pthread_mutex_lock(pthread_mutex_t *mutex);
86 | //mutex:你要申请的锁 返回值:0成功-1失败
87 |
88 | 3、测试锁 如果锁不可用,立即返回
89 | int pthread_mutex_trylock(pthread_mutex_t *mutex);
90 |
91 | 4、释放互斥锁 ,打开锁
92 | int pthread_mutex_unlock(pthread_mutex_t *mutex);
93 |
94 | 5、销毁锁
95 | int pthread_mutex_destroy(pthread_mutex_t *mutex);
96 | ```
97 |
98 | [互斥锁参考代码](https://github.com/chengxinfd/some-method/blob/master/3线程与进程/mutex.c)
99 |
100 | + **信号量(sem)**
101 |
102 | ```c
103 | 1、初始化信号量
104 | int sem_init(sem_t *sem, int pshared, unsigned int value);
105 | //sem:一个存放信号量的容器 pshared:使用范围 0:线程间使用 !0 进程间使用
106 | //value:信号量的初始值
107 | //返回值:0成功 -1失败
108 |
109 | 2、P操作 申请资源
110 | int sem_wait(sem_t *sem);
111 | //sem:要申请的资源类型,容器 返回值:0成功 -1失败
112 |
113 | 3、v操作 释放资源
114 | int sem_post(sem_t *sem);
115 | //sem:要释放的资源类型,容器
116 |
117 | 4、查看信号量的值
118 | int sem_getvalue(sem_t *sem, int *sval);
119 | //sval:存放信号量数值的容器
120 | ```
121 |
122 | [信号量参考例子](https://github.com/chengxinfd/some-method/blob/master/3线程与进程/sem.c)
123 |
124 | **3.进程线程区别**
125 |
126 | **进程**由3部分构成 进程控制块, 程序段,数据段,进程是操作系统进程资源分配的基本单元。
127 | **线程**是进程的一个实体,是被系统独立调度和分派的基本单元,线程自己不具有系统资源,只拥有一点在运行中必不可少的资源。线程与同属一个进程的其他线程共享进程多拥有的所有资源。一个线程可以创建和撤销另一个线程,同一进程中多个线程之间可以并发执行。
128 |
129 | + 一个程序至少有一个进程,一个进程至少有一个线程
130 | + 进程在执行过程中所拥有独立内存单元,而多个线程共享进程所拥有的内存
131 | + 进程可以独立运行,但线程不能独立执行,必须依存在进程中,由使用该进程的应用程提供多个线程执
132 |
133 | **4.死锁[解决方式:顺序加锁 获取时限]**
134 |
135 | 
136 |
--------------------------------------------------------------------------------
/4IO模型/IO模型.md:
--------------------------------------------------------------------------------
1 | # IO模型
2 |
3 | **1.IO模型**
4 |
5 | 就是为了解决通信双方在通信过程中遇到的各种问题,比如一方没有响应/忙
6 |
7 | + **阻塞**
8 |
9 | 双方通信的时候,如果一方资源不满足/忙,那么另一方进行等待,绝大多数交互的函数都默认阻塞
10 |
11 | read write accept sendto recv recvfrom send
12 |
13 | + **非阻塞**
14 |
15 | + **多路复用**
16 |
17 | + **异步通知**
18 |
19 | 
--------------------------------------------------------------------------------
/4IO模型/io模型.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengxinfd/some-method/21865c34241a69fdb6873bb52cbb067b84018ddf/4IO模型/io模型.png
--------------------------------------------------------------------------------
/5网络编程/网络拓扑.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengxinfd/some-method/21865c34241a69fdb6873bb52cbb067b84018ddf/5网络编程/网络拓扑.png
--------------------------------------------------------------------------------
/5网络编程/网络编程.md:
--------------------------------------------------------------------------------
1 | # 网络编程
2 |
3 | **1.OSI分层(了解)**
4 |
5 | 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层
6 |
7 | **2.TCP/IP分层(熟练)**
8 |
9 | + **APP(应用层)**
10 |
11 | app准备好数据,调用TCP层的接口
12 |
13 | > 应用层:负责提供数据
14 |
15 | + **TCP(传输控制层)**
16 |
17 | TCP层进行数据的校验,添加ECC校验码,调用IP层接口,使用IP层的服务
18 |
19 | > 传输控制层:负责安全性的问题:将数据丢给IP进行发送,然后不断地检测数据有没有到达/丢失/破坏
20 |
21 | + **IP(网络层)**
22 |
23 | IP层进行寻址,找到对方的位置,根据对方的IP地址,获取对方的信息,调用网络接口层的接口,进行数据发送
24 |
25 | > 网络层:负责网络寻址,你想和某方通信,必须知道对方的IP地址,IP层提供端到端的服务,从IP到IP,一对一的传输,只负责发送,不负责数据的安全性(丢失,破坏)
26 |
27 | + **NET INTF(网络接口层)**
28 |
29 | 网络接口层根据对方的信息,发送。通过ARP协议,获知下一跳的物理地址,然后往该物理地址发送数据
30 |
31 | > 网络接口层:处理网线/电磁波等不同物理介质的一层,根据当前使用的不同的物理介质,将数据转换成不同的信号发送出去 物理地址:网卡的地址
32 |
33 | 
34 |
35 | **3.网络协议(熟练)**
36 |
37 | + **应用层协议**
38 |
39 | > ①DNS:域名解析系统
40 | >
41 | > ②FTP:文件传输协议
42 | >
43 | > ③telnet:远程登录
44 |
45 | + **TCP层协议**
46 |
47 | > ①TCP:面向连接、流的、安全可靠的
48 | >
49 | > ②UDP:面向无连接、数据包的、不安全的
50 |
51 | + **IP层协议**
52 |
53 | > ①IP:主要是网间寻址
54 | >
55 | > ②IGMP:组播协议 网红直播
56 | >
57 | > ③ICMP:网络控制管理协议,负责管理网络和获取网络统计信息(ping)
58 |
59 | + **网络接口层协议**
60 |
61 | > ①ARP:通过对方IP地址获取对方的物理地址
62 | >
63 | > ②RAPR:通过物理地址获得对方的IP地址
--------------------------------------------------------------------------------
/6错题集/错题集1.md:
--------------------------------------------------------------------------------
1 | # 错题集第一章
2 |
3 | 1.sizeof即是关键字也是运算符
4 |
5 | 2.%取余运算符只能用于整形;A%B——A、B都必须为int类型。
6 |
7 | 3.
8 |
9 | ①BSS(Block Started by Symbol):通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域。特点是:可读写的,在程序执行之前BSS段会自动清0。所以,未初始的全局变量在程序执行之前已经成0了。
10 |
11 | ②variable:变量
12 |
13 | ③data段:存放初值不为0的非const全局变量或静态局部变量
14 |
15 | ④rodata段:(局部只读变量位于栈区):
16 |
17 | ⑤text段:代码段,main函数的汇编指令存放在.text段。
18 |
19 | 4.基本ATPCS规定了在子程序调用时的一些基本规则
20 |
21 | > ①子程序间通过**寄存器R0一R3来传递参数**,这时,寄存器R0~R3可以记作A1-A4。被调用的子程序在返回前无需恢复寄存器R0~R3的内容。
22 | > ②在子程序中,使用寄存器R4~R11来保存局部变量.这时,寄存器 R4 ~ R11可以记作V1 ~ V8。如果在子程序中使用到了寄存器V1~V8中的某些寄存器,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值;对于子程序中没有用到的寄存器则不必进行这些操作。在Thumb程序中,通常只能使用寄存器R4~R7来保存局部变量。
23 | > ③寄存器R12用作过程调用时的临时寄存器(用于保存SP,在函数返回时使用该寄存器出栈), 记作ip。在子程序间的连接代码段中常有这种使用规则。
24 | > ④寄存器R13用作数据栈指针,记作sp。在子程序中寄存器R13不能用作其他用途。寄存器sp在进入子程序时的值和退出子程序时的值必须相等。
25 | > ⑤寄存器R14称为连接寄存器,记作lr。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,寄存器R14则可以用作其他用途。
26 | > ⑥寄存器R15是程序计数器,记作pc。它不能用作其他用途。
27 |
28 | 5.通用寄存器中,R0-R7是未分组寄存器,R8-R14是分组寄存器
29 |
30 | 6.I2C总线的输出端口是开漏式
31 |
32 | 7.C语言中浮点类型数据包括 符号位 指数位 尾数部分
33 |
34 | 8.进程的三种工作状态分别是 运行态 就绪态 阻塞态
35 |
36 | 9.
37 |
38 | > ①波特率指信号每秒的变化次数。比特率指每秒可传输的二进制位数。在无调制的情况下,波特率精确等于比特率。采用调相技术时,波特率不等于比特率。
39 | >
40 | > ②数字信道传送数字信号的速率称为数据传输速率或比特率。
41 | >
42 | > ③传输率就是指每秒传输多少位,传输率也常叫波特率。
43 | >
44 | > ④波特率是指线路状态更改的次数。只有每个信号符合所传输数据的一位时,才等于每秒位数。
45 |
46 | 10.指针数组是指针int* p1[] 数组指针:是数组int(*p1)[]
--------------------------------------------------------------------------------
/6错题集/错题集2.md:
--------------------------------------------------------------------------------
1 | # 错题集第二章
2 |
3 | #### 1.单片机IO口开漏输出和推挽输出有什么区别?
4 |
5 | > **开漏输出:**开漏输出只能输出低电平,如果要输出高电平必须通过上拉电阻才能实现。就类似于三极管的集电极输出。
6 | >
7 | > **推挽输出:**推挽输出既可以输出低电平,也可以输出高电平,可以直接驱动功耗不大的数字器件。导通损耗小、效率高、既提高电路的负载能力,又提高开关速度。
8 |
9 | 拓展:STM32的8种GPIO输入输出模式
10 |
11 | > **①浮空输入**:浮空输入模式下,I/O端口的电平信号直接进入输入数据寄存器。也就是说,I/O的电平状态是不确定的,完全由外部输入决定;如果在该引脚悬空(在无信号输入)的情况下,读取该端口的**电平是不确定的。**
12 | >
13 | > **②上拉输入**:上拉输入模式下,I/O端口的电平信号直接进入输入数据寄存器。但是在I/O端口悬空(在无信号输入)的情况下,输入端的电平可以**保持在高电平**;并且在I/O端口输入为低电平的时候,输入端的电平也还是低电平。
14 | >
15 | > **③下拉输入**:下拉输入模式下,I/O端口的电平信号直接进入输入数据寄存器。但是在I/O端口悬空(在无信号输入)的情况下,输入端的电平可以**保持在低电平**;并且在I/O端口输入为高电平的时候,输入端的电平也还是高电平。
16 | >
17 | > **④模拟输入**:模拟输入模式下,I/O端口的模拟信号(电压信号,而非电平信号)直接模拟输入到片上外设模块,比如ADC模块等等。
18 | >
19 | > **⑤开漏输出:**开漏输出模式下,通过设置位设置/清除寄存器或者输出数据寄存器的值,途经N-MOS管,最终输出到I/O端口。这里要注意N-MOS管,**当设置输出的值为高电平的时候,N-MOS管处于关闭状态,此时I/O端口的电平就不会由输出的高低电平决定,而是由I/O端口外部的上拉或者下拉决定;当设置输出的值为低电平的时候,N-MOS管处于开启状态,此时I/O端口的电平就是低电平。**同时,I/O端口的电平也可以通过输入电路进行读取;注意,I/O端口的电平不一定是输出的电平。
20 | >
21 | > **⑥开漏复用输出:**输出的高低电平的来源,不是让CPU直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的。
22 | >
23 | > **⑦推挽输出:**推挽输出模式下,通过设置位设置/清除寄存器或者输出数据寄存器的值,途经P-MOS管和N-MOS管,最终输出到I/O端口。这里要注意P-MOS管和N-MOS管,当设置输出的值为高电平的时候,P-MOS管处于开启状态,N-MOS管处于关闭状态,此时I/O端口的电平就由P-MOS管决定:高电平;当设置输出的值为低电平的时候,P-MOS管处于关闭状态,N-MOS管处于开启状态,此时I/O端口的电平就由N-MOS管决定:低电平。同时,I/O端口的电平也可以通过输入电路进行读取;注意,此时I/O端口的电平一定是输出的电平。
24 |
25 | #### 2.单片机里定时器有什么作用和优势?
26 |
27 | > ①作用:计数,延时,输入捕获,输出比较(输出PWM),产生中断
28 | >
29 | > ②优势:不需要CPU参与,精确
30 |
31 | #### 3.同步通信和异步通信的区别
32 |
33 | > 同步通信需要保持相同的时钟频率,发送端发送一帧,接收端接收一帧
34 | >
35 | > 异步通信不需要知道数据什么时候会到达,不需要保持相同的时钟频率,通常以起始位和结束位来判断
36 |
37 | #### 4.什么是中断?
38 |
39 | > 在CPU正常执行程序时,因外部或者内部事件或程序使得CPU暂停工作,CPU去处理中断子程序中的任务,待中断子程序中的任务处理完之后,在继续执行程序
40 | >
41 | > 中断处理过程:
42 | >
43 | > ①保护现场,将当前位置的PC地址压栈
44 | >
45 | > ②跳转到中断服务程序中,执行中断服务函数
46 | >
47 | > ③回复现场,将栈顶的值回送给PC
48 | >
49 | > ④跳转到被中断的位置开始执行下一个指令
50 |
51 |
--------------------------------------------------------------------------------
/6错题集/面试题1.md:
--------------------------------------------------------------------------------
1 | # 面试题
2 |
3 | #### 1.简述SPI IIC UART接口的区别和各自收发数据的方法
4 |
5 | SPI:全双工,四根线 优点:高速 缺点:没有指定流控制器,没有应答机制确认是否收到数据
6 |
7 | MISO:主设备数据输入,从设备数据输出
8 |
9 | MOSI:主设备数据输出,从设备数据出入
10 |
11 | SCLK:时钟信号,由主设备产生
12 |
13 | CS:从设备使能信号,由主设备控制
14 |
15 | IIC:半双工,两根线 优点:简单,支持多主控 缺点:速度比SPI慢,数据帧大小限制8位
16 |
17 | SDA:数据线
18 |
19 | SCL:时钟线
20 |
21 | UART:全双工,两根线(不包括GND) 优点:不需要时钟,传输距离远,有检错机制 缺点:需要波特率支持
22 |
23 | RX:接收
24 |
25 | TX:发送
26 |
27 | #### 2.从以下几种无线通信方式中选取一种了解的尽可能详细的描述一下:BLE,WiFi ,NFC,NB-IoT,eMTC
28 |
29 | BLE:蓝牙技术,是一种近距离无线通信方式,协议为802.15.4,频段为公共通用2.4GHz ISM频段
30 |
31 | WIFI:近距离无线通信技术,正式名称802.11b,工作频率2.4GHz,最高速率可达11Mbps,覆盖范围100m
32 |
33 | NFC:短距离无线通信技术,采用双向识别和连接,使用13.56MHz频率
34 |
35 | Zigbee:低速短距离无线通信技术,低耗电,低成本,支持节点多,支持拓扑结构,使用2.4GHz频段
36 |
37 | #### 3.试描述MQTT、CoAP、HTTP三种协议的区别
38 |
39 | MTQQ:基于TCP协议
40 |
41 | CoAP:基础UDP协议
42 |
43 | HTTP:基于TCP协议
44 |
45 | #### 4.试描述TCP建立和断开连接时的三次握手和四次挥手
46 |
47 | 第一次握手:服务器启动监听,客户端向服务器发送SYN命令,服务器收到后会给客户端发送收到的SYN(x+1)和ACK(y)
48 |
49 | 第二次握手:服务器进入accept,客户端收到指令后,向服务器发送SYN(x+1)和ACK(y+1)
50 |
51 | 第三次握手:服务器收到命令,校验成功后建立连接
52 |
53 | 第一次挥手:客户端向服务器发送请求断开指令(FIN),表示自己想要断开
54 |
55 | 第二次挥手:服务器收到命令后,给客户端回复(ACK)收到指令里的随机值+1,由于TCP是安全可靠的,现在不会马上断开连接,而是等数据处理完之后,这时客户端关闭发送功能,保留接收功能
56 |
57 | 第三次挥手:服务器处理完数据之后,服务器向客户端发送请求断开指令(FIN),表示自己要断开了
58 |
59 | 第四挥手:客户端收到指令后,向服务器发送响应(ACK),表示服务器可以断开
60 |
61 | #### 5.一个无序的长度为n的数组,找到最小的值,请用C语言编码实现以下函数
62 |
63 | int get_min(int* array, int n) {
64 |
65 | }
66 |
67 | #### 6.工厂生产摩拜单车包含两道工序,工序A每5min生产一辆摩拜单车,生产完成后送到工序B检测,每1min检测一辆,检测失败的需要返回工序A重新生产;试用多线程(多任务)的机制实现上述的生产工序,实现产能的最大化。
68 |
69 | #### 7.用变量a给出以下定义:
70 |
71 | 一个指向指针的指针,它指向的指针是指向一个整型数 int **a;
72 |
73 | 一个有10个指针的数组,该指针是指向一个整型数的 int *a[10];
74 |
75 | 一个指向有10个整型数数组的指针 int (*a)[10];
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 嵌入式
2 |
3 | **嵌入式面试笔试题整理**
4 |
5 |
6 |
--------------------------------------------------------------------------------