嵌入式应用开发(基础知识点)

一、shell基本命令

notes:简单命令结构框架而不解释详细命令的使用,具体的shell 命令用法可以自行问度娘

软件包管理

dpkg
apt

shell特殊字符使用

notes:

一行多个命令,用;隔开:ls;pwd;cd ..
一行写不下,用‘\’隔开,用于shell脚本
通配符:* ? []
管道:‘|’,前一个命令的输出作为后一个命令的输入
输入/输出重定向:>file 新建模式
			   >>file 追加模式
			   2>file 错误信息输出到文件
命令置换:ls `pwd` 
notes:esc下面的~键 

shell基本系统维护命令

man 联机帮助
passwd su 
echo 标准输出 
data clear 
df 查看文件系统信息 
du 查看文件所占磁盘情况

linux用户管理

adduser 
usermod 用户属性管理
deluser addgroup 

进程管理

ps -elf ps -aux 显示进程状态 pid 进程号
top 动态监视进程状态 
pstree 树形显示进程
kill 结束进程

文件系统类型及相关命令

linux文件系统:磁盘文件系统 网络文件系统 专有/虚拟文件系统

file 查看文件类型
ln 文件链接 
		硬链接文件:通过inode链接,不能跨越文件系统(备份)
		软链接:绝对路径链接(快捷方式)
文件压缩和归档:
tar
gzip

shell 脚本编程

#变量

COUNT=1 #变量赋值不允许有空格
echo $COUNT #$是取变量值

DATA=`date` #如果是命令必须要进行命令置换
echo $DATA


unset DATA
echo $DATA #unset 取消变量,也就是不在使用


#位置变量
echo "0-------------$0"
echo "1-------------$1"
echo "2-------------$2"
echo "3-------------$3"
echo "4-------------$4"
echo "4-------------$5"
echo "5-------------$6"
echo "7-------------$7"
echo "8-------------$8"
echo "9-------------$9"
echo "10-------------${10}"
echo "11-------------${11}"

echo "#-------------$#"
echo "\$*-------------$*"
echo "@-------------$@"
echo "?-------------$?"
echo "\$\$-------------$$"

#环境变量
echo $PATH #搜索路径
echo $HOME #用户主目录
echo $IFS #默认为空格 tab或换行符
echo $PS1PS2 #默认为提示符($)
echo $TERM #终端类型

#功能性语句
#说明性语句----->#注释
#功能性语句----->read expr test等
#结构性语句----->循环语句,判断语句


read -p "input two bumber" v1 v2
echo "$v1 $v2"


#整数运算
num=`expr $v1 + $v2` #注意加空格
echo $num

num=`expr $v1 \* $v2` #注意转义字符
echo $num


#测试命令 test
#字符测试
test "hell0" = "hell" #0表示对,1表示错,注意空格
echo $?

test -n "hello" #判断字符串是否不为0
echo $?

test -z "hello"
echo $?


#整数测试
test 1 -eq 1
echo $?

test 2 -lt 2
echo $?

test 2 -gt 1
echo $?


#文件测试

test -d test.sh
echo $?


test -f test.sh
echo $?


#条件分支判断语句

F=$1


if test -f $F
then
        echo "$F is a file"
else
        echo "$F is not a file"
fi



read -p "input a number" val
if test $val -eq 0
then
        echo "val = 0"
elif [ $val -gt 0 ] #[]在分支语句中也可以表示test,每次if后都需要添加then
then
        echo "val >0"
else
        echo "val < 0"
fi



#多路分支语句
read -p " input a grade" val
if [ $val -lt 0 ] || [ $val -gt 100 ]
then
        echo "input error"
        exit 0
fi

grade=`expr $val / 10 `
echo "grade = $grade"
case $grade in
        8|9|10)#情况1
                echo "A"
                ;;
        6|7)#情况2
                echo "B"
                ;;
        *)#*相当于其他情况,default
                echo "C"
                ;;
esac



 1 
  2 
  3 for val in 1 2 3 4 5
  4 #for ((val=1;val<5;val=val + 1))
  5 do  
  6         echo "val=$val"
  7 done
  8 
  9 
 10 for file in `ls`
 11 do  
 12         echo "-----$file"
 13 done
 14 
 15 num=0
 16 #while [ $num -lt 10 ]
 17 while(($num<10))
 18 do      
 19         if [ $num -gt 3 ]
 20         then    
 21                 #break
 22                 continue
 23         fi
 24         
 25         echo "num=$num"
 26         num=`expr $num + 1`
 27 done
~                                     
##函数
fun()
{
        local A=$1 #local表示局部作用域,只能在函数内部使用,
                   #否则就是全局作用域,在函数外也可以使用
        B=$2
        C=$3
        echo "hello world"
        echo "A=$A"
        echo "B=$B"
        echo "C=$C"
}

fun 1 2 3
echo "A=$A"
echo "B=$B"
echo "C=$C"

gcc编译器,gdb调试工具

编译流程图
在这里插入图片描述

#gcc基本命令参数
gcc -g 程序可调式
gcc -E test.c -o test.i #预编译处理文件
gcc -S test.i -o test.s #汇编文件
gcc -c test.s -o test.o #目标文件
gcc test.o -o test		#可执行文件
-l 链接库
-L
常用:
gcc test.c -o test -l pthread

#gdb 调试工具的使用
#notes:使用gdb之前,必须要用gcc编译器加上-g选项
gdb test 调试test程序
(gdb)l ----->显示10行代码
(gdb)b 12 ----->12行打上断电
(gdb)info b ----->查看断点
(gdb)r ----->运行代码
(gdb)n ----->单步调试
(gdb)p a ----->查看变量a的值
(gdb)s ----->进入到函数中
(gdb)c ----->恢复运行(一直运行,遇到断点或者程序运行结束)
(gdb)q ----->退出

Makefile文件编写


#notes:vsp 命令 set mouse=a crtl+w 切屏 ls -R 查看文件包含关系
#makefile格式
target:dependly_file
<Tab>command


#example
sunq:kang.o yul.o
	gcc kang.o yul.o -o sunq
kang.o:kang.c kang.h
	gcc -Wall -O -g -c kang.c -o kang.o
yul.o:yul.c
	gcc -Wall -O -g -c yul.c -o yul.o 

#example
test:f1.o f2.o main.o
        gcc f1.o f2.o main.o -o test
f1.o:f1.c
        gcc -c f1.c -o f1.o
f2.o:f2.c
        gcc -c f2.c -o f2.o
main.o:
        gcc -c main.c -o main.o
.PHONY:clean #存在同名文件,则需要添加虚拟文件路径
clean:#make clean 也就是执行下面语句,清除.o中间文件
        rm -rf *.o


创建变量:
VAR=var #递归方式创建,不利于拓展
VAR: =var#简单方式创建,可以随意修改,当前赋值是啥就是啥
VAR?=bar#如果var定义过,则什么都不执行,如果没有定义,则将bar的值赋值给VAR
OBJS+=test.o#加一个变量值,现在OBJS就是kang.o yul.o test.o

自动变量:
$@....目标文件
$^....所有依赖文件
$<...第一个依赖文件
参考博客
https://blog.csdn.net/dlf1769/article/details/78997967

#makefile 变量使用
OBJS=kang.o yul.o
CC=gcc
CFLAGs=-Wall -O -g 
sunq:$(OBJS)
	$(CC) $(CFLAGS) $(OBJS) -o sunq
kang.o:kang.c kang.h
	$(CC) $(CFLAGS) -c kang.c -o kang.o
yul.o:yul.c
	$(CC) $(CFLAGS) -c yul.c -o yul.o


#sample
test:f1.o f2.o main.o
        gcc $^ main.o -o $@
f1.o:f1.c
        gcc -c $< -o f1.o
f2.o:f2.c
        gcc -c f2.c -o f2.o
main.o:
        gcc -c main.c -o main.o
.PHONY:clean #存在同名文件,则需要添加虚拟文件路径
clean:#make clean 也就是执行下面语句,清除.o中间文件
        rm -rf *.o
	

Make的直接使用
make -c 
make -f

#隐含规则
gcc命令中,生成.o文件需要依赖.c文件,如果.c文件都存在,那么生成.o的语句可以不用写
#sample2
CC=gcc
CFLAGS=-Wall -O -g
OBJS=f1.o f2.o main.o
  

f1:f1.o f2.o main.o#生成的目标文件名应是后面的.o文件名的其中之一 也就是执行gcc test.c -o test 系统会自动帮我们链接.c文件

.PHONY:clean
clean:
        rm -rf *.o

#sample1
CC=gcc
CFLAGS=-Wall -O -g
OBJS=f1.o f2.o main.o
  

test:f1.o f2.o main.o
        $(CC) $(CFLAGS) $(OBJS) -o test 
f1.o:f1.c
f2.o:f2.c
main.o:main.c

.PHONY:clean
clean:
        rm -rf *.o



虚文件路径VPATH,多个源文件处于不同文件夹,需要使用虚文件包含
当前目录结构
f1  include  main  Makefile  src1  src2

./include:
head.h

./main:
main.c

./src1:
f1.c

./src2:
f2.c
#sample
CC=gcc
CFLAGS=-Wall -I include #包含文件搜索目录
OBJS=src1/f1.o src2/f2.o main/main.o
VPATH=src1:src2:main

f1:f1.o f2.o main.o
        $(CC) $(CFLAGS) $^ -o $@ 
.PHONY:clean
clean:
        find ./ -name "*.o" -exec rm {} \;

make嵌套,嵌套模版,需要记忆

二、文件IO

notes:此章节在文件I/O和标准I/o章节不在详细介绍,只是一些具体函数的使用,如果想要具体查看该小节的详细内容可以参考我的文件i/o博客。链接:

章节概括

@标准I/O主要是介绍标准c库函数对文件的使用
标准I/O接口函数:
文件打开与关闭:fopen/fclose
文件读写(单个字符读写):fgetc/fputc getc/putc getchar/putchar
文件读写(行输入/行输出):gets/puts fgets/fputs
二进制文件读写:fread/fwrite

文件流的刷新和定位(每一个文件都存在文件指针,文件指针随着读取而移动,当文件读完,
文件指针就指向文件末尾,此时想要在文件开头写入字段,就需要刷新文件流指针和定位):
文件流刷新:将缓冲区的数据刷新到文件中
文件定位:long fseek(FILE *stream, long offset, int whence);

格式化输入/输出:scanf/printf sprintf/scanf
@文件I/O主要是介绍系统调用(Linux系统API)对文件的使用:
文件打开与关闭:open/close
读写:read/write
定位:lseek

目录操作和库的使用

打开/关闭目录

打开目录:DIR *opendir(const char *name);
读取目录:struct dirent *readdir(DIR *dirp);
关闭目录:int closedir(DIR *dirp);
#include<dirent.h>
#include<stdio.h>


int main(int argc,char*argv[]){

        DIR* fd;
        struct dirent* fp;
        fd=opendir("/home");
        if(fd==NULL){
                perror("opendir:");
                return -1;

        }
        while((fp=readdir(fd))!=NULL){
                printf("%s\n",fp->d_name);

        }
        closedir(fd);
        return 0;
}

修改文件访问权限

int chmod(const char *path, mode_t mode);
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);

struct stat {
       dev_t     st_dev;         /* ID of device containing file */
       ino_t     st_ino;         /* Inode number */
       mode_t    st_mode;        /* File type and mode */
       nlink_t   st_nlink;       /* Number of hard links */
       uid_t     st_uid;         /* User ID of owner */
       gid_t     st_gid;         /* Group ID of owner */
       dev_t     st_rdev;        /* Device ID (if special file) */
       off_t     st_size;        /* Total size, in bytes */
       blksize_t st_blksize;     /* Block size for filesystem I/O */
       blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

       /* Since Linux 2.6, the kernel supports nanosecond
       precision for the following timestamp fields.
       For the details before Linux 2.6, see NOTES. */

       struct timespec st_atim;  /* Time of last access */
       struct timespec st_mtim;  /* Time of last modification */
       struct timespec st_ctim;  /* Time of last status change */

       #define st_atime st_atim.tv_sec      /* Backward compatibility */
       #define st_mtime st_mtim.tv_sec
       #define st_ctime st_ctim.tv_sec
           };

动态库和静态库的使用

静态库:编译时将静态库的代码复制到可执行的文件中,运行时不需要静态库,占用空间大,升级需要重新编译
动态库(共享库):编译时只记录用到库中的哪个符号,不复制共享库中代码,程序运行时需要重新加载库
静态库制作:
1.编写库函数文件 hello.c
2.编译生成.o文件(gcc -c hello.c -o hello.o)
静态库文件命名规则:libxxxx.a
3.ar -rsv libhello.a hello.o
#链接静态库 gcc -o 目标文件 源文件.c -L路径 -l静态库
4.gcc -o test test.c -L. -lhello

动态库制作:
1.编写库函数文件 hello.c
2.编译生成不随位置改变的.o文件 gcc -c -fPIC hello.c
动态库命名规则:libxxx.so 
3.gcc -shared -o libhello.so hello.c
#链接动态库
4.gcc -o test test.c -L. -lhello
报错:
./test: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory

进行加载动态库:
1.直接将动态库文件放到/usr/lib文件夹中
2.export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./(动态库路径),但是可执行文件换位置就又会报错
将其放入到~/.bashrc文件中
source ~/.bashrc生效
3.ldd test 查看可执行文件所使用的库文件

root@iZbp1928604gkfxejwopqbZ:/home/file_io# ldd test
	linux-vdso.so.1 (0x00007fff5651f000)
	libhello.so (0x00007ff1976a8000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff1974ad000)
	/lib64/ld-linux-x86-64.so.2 (0x00007ff1976b4000)

附图一张(仅供参考)
在这里插入图片描述

标准I/O

输入/输出数据流
流的类型:二进制流和文本流
标准I/O流:
stdin:标准输入流 0
stdout:标准输出流 1
stderr:标准错误流 2

系统调用和库函数

在这里插入图片描述
系统调用:操作系统提供的接口(API)
C库函数:现存的操作系统多种多样,每一个操作系统提供的系统调用都不一样,因此,为了提高代码的移植性,使用统一的C库函数调用底层的系统调用。

缓冲

全缓冲:系统分配的缓冲区满或无空间,输出
行缓冲:遇到换行符‘\n’,就可以输出
无缓冲:数据直接写入文件当中

文件的打开与关闭

文件打开:
FILE *fopen(const char *pathname, const char *mode);
成功返回FILE*指针;失败返回NULL
mode:
r 	只读方式打开!不存在返回NULL
r+	读写方式打开!不存在返回NULL
w	只写方式打开!不存在则创建,存在则清空内容
w+	读写方式打开!不存在则创建,存在则清空内容
a	只写方式打开!不存在则创建,存在则将数据追加
a+	读写方式打开!其他同“a”模式一样
文件关闭:
int fclose(FILE *stream);
success return EOF(0)
failed return errno
标准错误输出:
void perror(const char *s);
char *strerror(int errnum);

三、进程 线程间通信

进程

进程基本概念

#进程概念
进程与程序做区分:程序是存储硬盘上的文件,是静态的;
而进程则是运行一个程序所需要的资源总称,是动态的;

在这里插入图片描述

代码段:存放用户代码
数据段:存放初始化的全局变量和静态变量
BSS段:未初始化的全局变量
堆:用户使用malloc分配的动态内存空间
栈:系统分配的内存空间,局部变量,返回值,入参
进程控制块:进程属性(PID),进程状态,优先级,文件描述符表

进程类型:
交互进程:终端交互 前后台都可以运行
批处理进程:与终端无关
守护进程:与终端无关,后台运行
进程状态:
运行态 等待态 停止态 僵尸态

Linux下进程相关命令

ps -elf |grep test 查看进程快照
top []查看进程动态信息
ls /proc

nice -n [] []指定哪个进程以什么优先级运行
nice -n 10 /bin/bash

renice [] [pid] 改变正在运行进程的优先级
renice 5 15637

查看后台进程
jobs
fg [] 恢复到前台运行
bg [] 挂起进程到后台运行

父子进程

创建子进程函数
#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);
返回值:子进程pid值为0,父进程返回子进程pid,出错<0;
RETURN VALUE
On success, the PID of the child process is returned in the parent, and 0 is returned in the child.  
On failure, -1 is returned in  the  parent,  no child process is created, and errno is set appropriately.
父子进程关系:

在这里插入图片描述

//创建子进程
#include<stdio.h>
#include<unistd.h>

int main(int argc,char*argv[]){
        pid_t pid;
        pid=fork();
        if(pid>0){
                printf("this is father!\n");
        }else if(pid==0){
                printf("this is child\n");
        }else{
                perror("fork");
        }
        printf("public part!\n");

}

结果:注意,子进程和父进程执行的顺序是没有规律的,全靠系统调度

在这里插入图片描述

父进程先结束:
子进程变成孤儿进程,由init进程管理,且变为后台进程
子进程先结束:
父进程没有及时回收就会变成僵尸进程
#include<stdio.h>
#include<unistd.h>

int main(int argc,char*argv[]){
        pid_t pid;
        pid=fork();
        if(pid>0){
                while(1){
                printf("this is father!\n");
                sleep(1);
                }
        }else if(pid==0){
                while(1){
                printf("this is child\n");
                sleep(1);
                }
        }else{
                perror("fork");
        }
        //printf("public part!\n");
}

验证:

在这里插入图片描述
杀死父进程,子进程由init进程管理,且成为后台进程,使用ctrl+c 杀不死,必须使用kill命令
在这里插入图片描述
杀死子进程,子进程变成僵尸进程
在这里插入图片描述

一个父进程创建多个子进程

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

int main(int argc,char*argv[]){
        pid_t pid;
        for(int i=0;i<5;i++){
                pid=fork();
                if(pid>0){
                        printf("father!\n");
                        sleep(1);
                }else if(pid==0){
                        printf("child!\n");
                        //sleep(1);
                        break;
//如果不在子进程加break,那么子进程每次都会从循环开始,创建子进程,
//因此加上break就可以达到上述题目效果
                }else{
                        perror("fork:");
                }
        }
        while(1){
                sleep(1);
        }
}

进程结束

#include <stdlib.h>
void exit(int status);
       
#include <unistd.h>
void _exit(int status);
       
两个函数区别在于:exit结束进程会刷新流到缓冲,而_exit()不会
status表示以什么状态退出
具体列子可以查看下节

进程回收

#include <sys/wait.h>

pid_t wait(int *stat_loc);
等待任意一个进程退出就回收进程,返回回收进程的pid,并将其状态存放于stat_loc中

 pid_t waitpid(pid_t pid, int *stat_loc, int options);
根据输入的pid号回收指定进程(也可以是任意一个进程)
WIFEXITED(stat_val)
Evaluates to a non-zero value if status was returned for a child process that terminated normally.

WEXITSTATUS(stat_val)
If the value of WIFEXITED(stat_val) is non-zero, this macro evaluates to the low-order 8 bits of the status argument that the child process passed to _exit() or exit(), or the value  the child process returned from main().

WIFSIGNALED(stat_val)
Evaluates to a non-zero value if status was returned for a child process that terminated due to the receipt of a signal that was not caught (see <signal.h>).

WTERMSIG(stat_val)
If the value of WIFSIGNALED(stat_val) is non-zero, this macro evaluates to the number of the signal that caused the termination of the child process.

WIFSTOPPED(stat_val)
Evaluates to a non-zero value if status was returned for a child process that is currently stopped.

WSTOPSIG(stat_val)
If the value of WIFSTOPPED(stat_val) is non-zero, this macro evaluates to the number of the signal that caused the child process to stop.

WIFCONTINUED(stat_val)
Evaluates to a non-zero value if status was returned for a child process that has continued from a job control stop.

		
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>

int main(int argc,char*argv[]){
        pid_t pid;
        pid=fork();
        int status;
        int rpid;
        if(pid>0){
                printf("this is father process!\n");
                //rpid=wait(&status);status中包含许多参数,因此还需要宏进行进一步解析
                //printf("pid=%d status=%d\n",rpid,WEXITSTATUS(status));
                rpid=waitpid(-1,&status,0);//-1表示任意进程,
                printf("pid=%d status=%d\n",rpid,WEXITSTATUS(status));
        
        }else if(pid==0){
                
                printf("this is child process!\n");
                sleep(1);
                exit(2);
        
        }else{
                perror("fork:");
        
        }

        while(1){
                sleep(1);
        }


}

结果:

在这里插入图片描述

未产生僵尸进程,只有父进程
在这里插入图片描述

exec和守护进程

exec函数簇

进程调用exec函数簇,进程当前的程序会被指定程序替换

父子进程执行不同程序:
1.父进程创建子进程
2.子进程调用exec函数,父进程不受影响

相关函数

int execl(const char *pathname, const char *arg, ...
                       /* (char  *) NULL */);//最后一个参数必须为NULL
int execlp(const char *file, const char *arg, ...
                       /* (char  *) NULL */);//最后一个参数必须为NULL
                       
int execv(const char *pathname, char *const argv[]);                       
int execvp(const char *file, char *const argv[]);                       

int system(const char *command);
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main(int argc,char**argv){
        if(execl("/bin/ls","ls","-a","-l","./",NULL)<0){
                perror("execl:");
        }
        return 0;



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

int main(int argc,char**argv){
        if(execlp("ls","ls","-a","-l","./",NULL)<0){
                perror("execl:");
        }
        return 0;



}

execl和execlp函数唯一不同点在于execl函数输入的程序的绝对路径,而后者是相对路径

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

int main(int argc,char**argv){
        char *arg[]={"ls","-a","-l","./",NULL};
        if(execv("/bin/ls",arg)<0){
                perror("exec:");
        }
        return 0;



}

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

int main(int argc,char**argv){
        char *arg[]={"ls","-a","-l","./",NULL};
        if(execvp("ls",arg)<0){
                perror("exec:");
        }
        return 0;
}

execv和execvp函数唯一不同点在于execv函数输入的程序的绝对路径,而后者是相对路径,但是都需要保证最后一个参数为NULL

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

int main(int argc,char**argv){
        pid_t pid;
        printf("before fork!\n");
        pid=fork();
        if(pid==0){//子进程用execl函数,父进程与子进程执行不同程序
        if(execl("/home/thread/wait_t","0 param","1 param","2 param",NULL)<0){
                //0参数一般不使用,但是需要写,第1个输入参数,第2个输入参数,传递给程序wait_t
                perror("execl:");
        }
        }
        printf("after execl!\n");
        return 0;



}


守护进程:后台运行,独立于终端
会话/终端。进程组概念

守护进程创建过程:
1.父进程创建子进程,父进程退出
2.重新设置会话
setsid(设置为会话组组长) getpid(获取进程号)getsid(获取会话id) getpgid(获取会话组id)
3.更改文件目录(可选,防止程序文件被删除)
chdir("/")
4.更改文件权限掩码(可选)
umask
5.关闭文件标准描述符
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/stat.h>

int main(int argc,char**argv){
        pid_t pid;
        pid=fork();
        if(pid<0){
                perror("fork:");
                return 0;
        }else if(pid>0){
                exit(0);
        }
        printf("i am deamon!\n");

        //重新设置会话
        printf("before setsid:sid=%d,pid=%d,pgid=%d\n",getsid(getpid()),getpid(),getpgid(getpid()));
        if(setsid()<0){
                perror("setsid:");
                exit(0);
        }
        printf("after setsid:sid=%d,pid=%d,pgid=%d\n",getsid(getpid()),getpid(),getpgid(getpid()));
        //更改文件目录
        chdir("/");
        //更改文件掩码
        if(umask(0)<0){
                perror("umask:");
                exit(0);
        }
        //关闭文件描述符
        close(0);close(1);close(2);
        sleep(100);


        return 0;


}

gdb调试多进程

进入gdb调试
gcc -o -g fork_t fork_t.c
gdb fork_t  
start 从main函数开始运行

在这里插入图片描述

set follow-fork-mode child 选择跟踪子进程,父进程会自动运行

在这里插入图片描述

set detach-on-fork off 分离进程关闭,这样子进程和父进程都不会自动运行

在这里插入图片描述

info inferiors 查看所有进程
inferior 1 切换进程调式

在这里插入图片描述
在这里插入图片描述
补充:killall -9 fork_t 杀死多个同名进程

线程

线程创建及参数传递

线程概念:同一进程中线程共用同一片内存空间
线程公有资源:
静态变量,文件描述符,pid,pgid
私有资源:
.......(后期补充)
函数介绍:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);
							线程id,线程属性结构体,线程回调函数,线程输入参数
							返回值:错误返回EOF,成功>0
							
pthread_t pthread_self(void);	求线程id
				
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

int testThread(void*arg){
        printf("this is thread tid=%lu,pid=%d,input parmeter=%d\n",pthread_self(),getpid(),*(int *)arg);
//输入参数解释:传入的参数为void*的指针,表示任何数据类型指针,因此现将它强制转换为int *型指针,然后*
//printf("this is thread tid=%lu,pid=%d,input parmeter=%d\n",pthread_self(),getpid(),(int)arg);
        pthread_exit(0);

}

int main(int argc,char**argv){
        pthread_t tid;
        int ret;
        int arg=5;
        ret=pthread_create(&tid,NULL,(void*)testThread,(void*)&arg);
        //ret=pthread_create(&tid,NULL,(void*)testThread,(void*)arg);
//第二种方式就是值传递,int型类型没有超出指针的范围,那么就把值5当做地址传递出去,这样做会有warming        
        if(ret<0){
                perror("pthread_create:");
                exit(0);
        }
        printf("this is main thread,tid=%lu!\n",tid);
        sleep(1);//添加休眠函数,避免主线程结束了,子线程函数灭有开始运行就结束
        return 0;

}


编译错误原因分析:
pthread_create_t.c:(.text+0x84): undefined reference to `pthread_create'
collect2: error: ld returned 1 exit status       
							-------------未连接动态库函数,pthread_create是空实现
解决:gcc -o pthread_create_t pthread_create_t.c -lpthread
//创建多个线程传递参数使用第二种值传递方式
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

int testThread(void*arg){
        printf("this is thread tid=%lu,pid=%d,input parmeter=%d\n",pthread_self(),getpid(),(int)arg);
        pthread_exit(0);

}

int main(int argc,char**argv){
        pthread_t tid;
        int ret;
        int arg=5;
        int i;
        for(i=0;i<arg;i++){
        //ret=pthread_create(&tid,NULL,(void*)testThread,(void*)&arg);
//第一种地址传递,需要每次取地址,但是主线程运行速度较快,i直接来到5,
//所以每次取得地址为5这个位置,因此需要使用sleep函数来延迟主线程运行,以便子线程得以运行 
        ret=pthread_create(&tid,NULL,(void*)testThread,(void*)i);
        if(ret<0){
                perror("pthread_create:");
                exit(0);
        }
        printf("this is main thread,tid=%lu!\n",tid);
//      sleep(1);
        }
        sleep(1);
        return 0;

}

线程回收

线程回收意义:
如同进程一样,如果线程执行完毕后,主线程没有进行回收,那么这个线程会变成僵尸线程,占用系统用的内存资源,浪费内存空间,因此需要进行回收
线程回收函数:
int pthread_join(pthread_t thread, void **retval);//阻塞函数
线程分离:(线程执行完自动回收)
第一种:int pthread_detach(pthread_t thread);
第二种:设置线程属性
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
int* testThread(void* arg){
        printf("this is child thread,tid=%lu,input parameter=%d\n",pthread_self(),(int)arg);
        pthread_exit("child exit");

}


int main(int argc,char**argv){
        pthread_t tid[5];
        int i;
        void*retval;
        for(i=0;i<5;i++){
                pthread_create(&tid[i],NULL,(void*)testThread,(void*)i);        
        }
        //回收线程
        for(i=0;i<5;i++){
                pthread_join(tid[i],&retval);
//pthread_join是一个阻塞的函数,如果线程0没有结束,那么即使后面的线程结束了,
//该函数还会一直阻塞在这里,因此为了解决这个问题,提出了线程分离                
                printf("%d thread %s\n",i,(char*)retval);
        
        }
        while(1){
                sleep(1);
        }

        return 0;

}

查看线程信息命令:ps -eLf | grep pthread_join_t
动态查看进程占用内存信息:top -p pid

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
int* testThread(void* arg){
        pthread_detach(pthread_self());
        printf("this is child thread,tid=%lu,input parameter=%d\n",pthread_self(),(int)arg);
        pthread_exit("child exit");

}


int main(int argc,char**argv){
        pthread_t tid[5];
        int i;
        void*retval;
        for(i=0;i<5;i++){
                pthread_create(&tid[i],NULL,(void*)testThread,(void*)i);        
        }
        while(1){
                sleep(1);
        }

        return 0;



}

线程取消

线程取消,也就是杀死线程
int pthread_cancel(pthread_t thread);
此函数的使用必须要有取消点也就是阻塞系统调用

如果我们不知道是有程序是否有取消点,则我们可以自己设置取消点
void pthread_testcancel(void);
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>
int* testThread(void*arg){
        printf("i am child thread!\n");

        while(1){
                sleep(1);
//              pthread_testcancel();
        }
        pthread_exit("exit!\n");

}


int main(int argc,char** argv){
        pthread_t tid;
        void* retval;
        pthread_create(&tid,NULL,(void*)testThread,NULL);
        sleep(5);
        pthread_cancel(tid);
        pthread_join(tid,&retval);
        while(1){
                sleep(1);
        }

        return 0;

}

如果我们想要线程的一段程序必须执行,之后可以取消,则可以设置取消点状态
int pthread_setcancelstate(int state, int *oldstate);

设置取消的模式,遇到取消点取消还是立即取消
 int pthread_setcanceltype(int type, int *oldtype);
 


#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>
int* testThread(void*arg){
        printf("i am child thread!\n");
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
//      while(1)
        {
                sleep(5);
                pthread_testcancel();
        }
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
        while(1){
			sleep(1);
		}
        pthread_exit("exit!\n");

}


int main(int argc,char** argv){
        pthread_t tid;
        void* retval;
        pthread_create(&tid,NULL,(void*)testThread,NULL);
        sleep(1);
        pthread_cancel(tid);
        pthread_join(tid,&retval);
        while(1){
                sleep(1);
        }

        return 0;




}

线程清理

线程中申请了内存,遇到exit,线程退出,内存没有free,会造成内存泄露,因此需要线程进行清理

 void pthread_cleanup_push(void (*routine)(void *), void *arg);
 void pthread_cleanup_pop(int execute);
 这两个函数成对使用
 
routine函数执行情况:
1.遇到pthread_cancel函数执行
2.遇到pthread_exit函数执行
3.pthread_cleanup_pop非零参数也执行
//此函数只描述了遇到取消点执行routine函数情况,其余情况大家可以自行尝试
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>
void cleanup(void* arg){
        printf("this is clean fun,arg=%s\n",(char*)arg);

}

int* testThread(void*arg){
        printf("i am child thread!\n");
        pthread_cleanup_push(cleanup,"abc");
        while(1)
        {       
                sleep(5);
        }
        pthread_exit("exit!\n");
        pthread_cleanup_pop(0);

}


int main(int argc,char** argv){
        pthread_t tid;
        void* retval;
        pthread_create(&tid,NULL,(void*)testThread,NULL);
        sleep(1);
        pthread_cancel(tid);
        pthread_join(tid,&retval);
        while(1){
                sleep(1);
        }

        return 0;




}

线程互斥

互斥锁
临界资源:只允许一个程序访问的资源
临界区:
互斥锁:
创建互斥锁:
动态方式:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);
静态方式:
pthread_mutex_t mutex=PTHREAD_MUTEX_INITALIZER

销毁互斥锁:
 int pthread_mutex_destroy(pthread_mutex_t *mutex);

锁的使用:
上锁:
       int pthread_mutex_lock(pthread_mutex_t *mutex);
       int pthread_mutex_trylock(pthread_mutex_t *mutex);
解锁:
       int pthread_mutex_unlock(pthread_mutex_t *mutex);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
FILE* fp;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//静态创建锁
void* func1(void*arg){
        pthread_detach(pthread_self());
        printf("this is func1 thread!\n");
        char str[]="func1 write file!\n";
        int i=0;
        char c;
        while(1){
                pthread_mutex_lock(&mutex);//上锁
                for(i=0;i<strlen(str);i++){
                        c=str[i];
                        fputc(c,fp);
                        usleep(10);

                }
                pthread_mutex_unlock(&mutex);//解锁

                usleep(1);

        }
        pthread_exit(0);

}
void* func2(void*arg){
        pthread_detach(pthread_self());
        printf("this is func1 thread!\n");
        char str[]="func2 read file!\n";
        int i=0;
        char c;
        while(1){
                pthread_mutex_lock(&mutex);
                for(i=0;i<strlen(str);i++){
                        c=str[i];
                        fputc(c,fp);
                        usleep(10);

                }
                pthread_mutex_unlock(&mutex);
                usleep(1);


        }
        pthread_exit(0);
}


int main(int argc,char** argv){
        pthread_t tid1,tid2;
        fp=fopen("1.txt","a+");
        if(fp==NULL){

                perror("fopen:");
                return 0;
        }

        pthread_create(&tid1,NULL,func1,NULL);
        pthread_create(&tid2,NULL,func2,NULL);

        while(1){
                sleep(1);

        }
        fclose(fp);
        return 0;




}
读写锁
读写锁:
特征:
写锁:没有写者,没有读者,才能获得写锁
读锁:没有写者。可以获得读锁

写锁只能有一个,而读锁可以有多个
相关函数:
创建读写锁:
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
           const pthread_rwlockattr_t *restrict attr);
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
获得读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
获得写锁:
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
解锁:
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
FILE* fp;
pthread_rwlock_t rwlock;

void* func1(void*arg){
        pthread_detach(pthread_self());
        printf("this is func1 thread!\n");
        char str[]="func1 write file!\n";
        int i=0;
        char c;
        while(1){
                pthread_rwlock_wrlock(&rwlock);
                for(i=0;i<strlen(str);i++){
                        c=str[i];
                        fputc(c,fp);
                        usleep(10);

                }
                pthread_rwlock_unlock(&rwlock);

                usleep(1000);

        }
        pthread_exit(0);

}
void* func2(void*arg){
        pthread_detach(pthread_self());
        printf("this is func1 thread!\n");
        char str[]="func2 read file!\n";
        int i=0;
        char c;
        while(1){
                pthread_rwlock_wrlock(&rwlock);
                for(i=0;i<strlen(str);i++){
                        c=str[i];
                        fputc(c,fp);
                        usleep(10);

                }
                pthread_rwlock_unlock(&rwlock);
                usleep(1000);

        }
        pthread_exit(0);


}
void*read_func(void*arg){
        pthread_detach(pthread_self());
        char buf[32]={0};
        while(1){
                rewind(fp);
                pthread_rwlock_rdlock(&rwlock);
                while(fgets(buf,32,fp)!=NULL){
                        printf("%d thread read=%s",(int)arg,buf);
                        usleep(1);
                }
                pthread_rwlock_unlock(&rwlock);
                usleep(1000);

        }

        pthread_exit(0);

}

int main(int argc,char** argv){
        pthread_t tid1,tid2,tid3,tid4;
        fp=fopen("1.txt","a+");
        if(fp==NULL){

                perror("fopen:");
                return 0;
        }
        pthread_rwlock_init(&rwlock,NULL);

        pthread_create(&tid3,NULL,read_func,3);
        pthread_create(&tid4,NULL,read_func,4);
        sleep(1);
        pthread_create(&tid1,NULL,func1,NULL);
        pthread_create(&tid2,NULL,func2,NULL);

        while(1){
                sleep(1);

        }
        fclose(fp);
        return 0;
                                                      
}
死锁

在这里插入图片描述

死锁一般发生在两把锁以上的情况,在已经拥有一把锁的情况下想要访问另外一个资源,而这个资源已经被其他第二个线程获得,导致程序卡死
解决:
1.线程有先后
2.同时获得两把锁,同时释放两把锁

条件变量和线程池

条件变量

生产消费者模型:线程间同步
一个线程生产出资源之后才能被其他线程消费
相关函数:创建条件变量
       int pthread_cond_destroy(pthread_cond_t *cond);
       int pthread_cond_init(pthread_cond_t *restrict cond,
           const pthread_condattr_t *restrict attr);
       pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
生产出条件变量:
 int pthread_cond_signal(pthread_cond_t *cond);
等待条件变量:  
int pthread_cond_wait(pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex);
这个函数(阻塞的函数,只有一个线程能够接受到这个变量)相当于:
           先判断条件变量是否有 若无:mutex_unlock
           					 若有:mutex_lock
notes:条件变量和互斥量需要一起使用 

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


typedef struct TAX{
        struct TAX* next;
        int num;

}Tax;

Tax *head;

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

void*product(void*arg){
        pthread_detach(pthread_self());
        printf("this is consume thread!\n");
        Tax *tx;
        int i=1;
        while(1){
                pthread_mutex_lock(&mutex);
                tx=malloc(sizeof(Tax));
                tx->num=i++;
                tx->next=head;
                head=tx;
                printf("tax %d is arrived!\n",tx->num);
                pthread_cond_signal(&cond);
                pthread_mutex_unlock(&mutex);
                sleep(1);

        }

        pthread_exit(0);


}
void*consume(void*arg){
        pthread_detach(pthread_self());
        printf("this is consume thread!\n");
        Tax* tx; 
        while(1){
                pthread_mutex_lock(&mutex);
                tx=malloc(sizeof(Tax));
                while(head==NULL){//惊群效应问题解决,防止段错误
                        pthread_cond_wait(&cond,&mutex);
                }
                tx=head;
                head=tx->next;
                printf("passage take %d taxi\n",tx->num);
                pthread_mutex_unlock(&mutex);
                sleep(1);
        }
        pthread_exit(0);

}

int main(int argc,char**argv){
        pthread_t tid1,tid2;

        head=malloc(sizeof(Tax));
        head->num=0;
        head->next=NULL;

        pthread_create(&tid1,NULL,product,NULL);
        pthread_create(&tid2,NULL,consume,NULL);


        while(1){

                sleep(1);
        }
        return 0;



}
  
丢失信号量问题
bordcast发送条件变量产生惊群效应(多个线程争抢一个资源,造成段错误)

线程池

线程池:
简单来说就是一个放满线程的池子,也就是创建一个数据结构,里面的成员主要是线程,
使用线程池可以极大的节省线程的创建和销毁的时间

线程池使用:
任务队列+线程池
过程:
1.创建任务队列、线程池数据结构
2.线程池初始化
3.任务添加到线程池
4.工作线程实现
5.销毁线程池
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
//定义线程池结构体:任务对列和线程池结构体
//任务队列是临界资源
typedef struct Task{
        void*(*func)(void*arg);//工作函数
        void* arg;
        struct Task* next;//任务链表
}Task;
#define POOL_NUM 10
typedef struct ThreadPool{
        pthread_mutex_t mutex;
        pthread_cond_t newTask;
        pthread_t tid[POOL_NUM];
        Task*taskQueue;
        int workThread_num;
}Pool;

Pool *pool;

void*realWork(void* arg){
        printf("%d func work finshed\n",(int)arg);
}

void addTaskToPool(void*arg){
        while(1){
                pthread_mutex_lock(&pool->mutex);
                while(pool->workThread_num>=POOL_NUM){
                        pthread_mutex_unlock(&pool->mutex);
                        sleep(1);
                        pthread_mutex_lock(&pool->mutex);
                }

                pthread_mutex_unlock(&pool->mutex);
                //将新任务添加到任务队列中
                Task* newTask=malloc(sizeof(Task));
                newTask->func=realWork;
                newTask->arg=arg;
                pthread_mutex_lock(&pool->mutex);
                Task* member=pool->taskQueue;
                if(member==NULL)member=newTask;
                else{
                                member=member->next;
                        }
                        member->next=newTask;
                }
                pool->workThread_num++;
                pthread_cond_signal(&pool->newTask);
                pthread_mutex_unlock(&pool->mutex);

        }

}

void* workThread(void*arg){
        while(1){
                //等待任务的到来
                pthread_mutex_lock(&pool->mutex);
                pthread_cond_wait(&pool->newTask,&pool->mutex);
                //取出任务
                //Task* newTask=malloc(sizeof(Task));
                Task* newTask=pool->taskQueue;
                pool->taskQueue=pool->taskQueue->next;

                pthread_mutex_unlock(&pool->mutex);
                //执行任务
                newTask->func(newTask->arg);
                pool->workThread_num--;
        }


}


void pool_init(){
        //申请内存池内存空间
        pool=malloc(sizeof(Pool));
        if(pool==NULL){
                perror("malloc pool:");
                return;
        }
        pthread_mutex_init(&pool->mutex,NULL);
        pthread_cond_init(&pool->newTask,NULL);
        for(int i=0;i<POOL_NUM;i++){
                pthread_create(&pool->tid[i],NULL,workThread,NULL);
        }
        pool->workThread_num=0;
        pool->taskQueue=NULL;
        return ;

}

void* poolDestory(void){
        //清空任务队列
        Task* task;
        while(pool->taskQueue!=NULL){
                task=pool->taskQueue;
                pool->taskQueue=pool->taskQueue->next;
                free(task);
        }
        //销毁条件变量和互斥锁
        pthread_mutex_destroy(&pool->mutex);
        pthread_cond_destroy(&pool->newTask);

        //销毁线程池结构体
        free(pool);
        return;



}

int main(){
        //线程初始化
        pool_init();
        sleep(1);
        //任务添加
        int i=0;
        for(i=0;i<20;i++){
                addTaskToPool(i);
        }
        sleep(5);
        //销毁
        poolDestory();

        return 0;


}

线程的GDB调试

1.显示线程
info thread
2.切换线程
thread XXX
bt
3.为特定的线程添加断点
break location thread id
4.GDB设置线程锁
set schedur-locking on/off

进程间通讯

进程间通讯的几种方:
1.无名管道(pipe)
2.有名管道(fifo)
3.信号(signal)
4.共享内存(mmap)
5.套接字(socket)
6.信号量(PV操作)
7.消息队列

无名管道

无名管道在内核中开辟一片内存,也就是管道,通过管道来传递数据
无名管道特点:
1.只适用于具有亲缘关系的进程(父子进程,兄弟进程)
2.单工通信
3.每次创建会产生两个文件描述符,读端和写端,但是每个进程只能使用一个
4,线程不能通过管道自己读写数据

int pipe(int pipefd[2]);
pipefd[0] refers to the read end of the pipe. 
pipefd[1] refers to the write end of the pipe.
#include <stdio.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char**argv){
        int pfd[2];
        pid_t pid;
        char wbuf[32]={0};
        char rbuf[32]={0};
        int re;
        re=pipe(pfd);
        if(re<0){
                perror("pipe:");
                return 0;
        }

        pid=fork();
        if(pid<0){
                perror("fork");
                return 0;
        }else if(pid==0){
                while(1){
                strcpy(wbuf,"abcdefg");
                write(pfd[1],wbuf,strlen(wbuf));
                sleep(1);    
                
                }   
        }else{
                while(1){
                        re=read(pfd[0],rbuf,sizeof(rbuf));
                        printf("read data=%s\n",rbuf);
                        sleep(1);
                
                }   
        
        }   

        return 0;

}


利用无名管道实现:两个进程写,一个进程读
#include <stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main(int argc,char**argv){
        int pfd[2];
        pid_t pid;
        char wbuf[32]={0};
        char rbuf[64]={0};
        int re;
        int i=0;
        re=pipe(pfd);
        if(re<0){
                perror("pipe:");
                return 0;
        }
        for(i=0;i<2;i++){
                pid=fork();
                if(pid<0){
                        perror("fork");
                        return 0;
                }else if(pid==0){
                        break;
                
                }
        }
        if(i==0){
                while(1){
                        strcpy(wbuf,"this is 1 process");
                        write(pfd[1],wbuf,strlen(wbuf));
                        sleep(1);
                }
        }
        if(i==1){
                while(1){
                        strcpy(wbuf,"this is 2 process");
                        write(pfd[1],wbuf,strlen(wbuf));
                        sleep(1);
                }
        }
        if(i==2){
                while(1){
                        re=read(pfd[0],rbuf,sizeof(rbuf));
                        if(re>0){
                                printf("read data=%s\n",rbuf);
                        }
                        memset(rbuf,0,sizeof(rbuf));
                }
        }

        return 0;
}

无名管道一些注意事项:
1.读端
	读端有数据,返回值返回为读取的字节数
	读端无数据:
		管道的写端被全部关闭,则read返回为0
		管道写端未关闭,则阻塞等待数据
2.写端
	写端完全关闭,程序异常停止
	写端不完全关闭
		写端数据满,write阻塞
		写端数据未满,则write写入

有名管道

有名管道:
适用于非亲缘关系的进程,通过操作路径名(管道文件,管道文件存放于内存当中),使用文件I/O的方式实现进程间通信,不支持leek,也是单工通信
相关函数:
 int mkfifo(const char *pathname, mode_t mode);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <fcntl.h>           /* Definition of AT_* constants */
#include <sys/stat.h>
#include <fcntl.h>
#include<string.h>


//写进程
int main(int argc,char**argv){
        int ret;
        int fd;
        char buf[32]={0};
        ret=mkfifo("/myfifo",0666);
        if(ret<0){
                perror("mkfifo:");
        }
        fd=open("/myfifo",O_WRONLY);
        if(fd<0){
                perror("open:");
                return 0;
        }

        while(1){
                fgets(buf,sizeof(buf),stdin);
                write(fd,buf,strlen(buf));
        }

        return 0;
}

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <fcntl.h>           /* Definition of AT_* constants */
#include <sys/stat.h>
#include <fcntl.h>
#include<string.h>


//读进程
int main(int argc,char**argv){
        int ret;
        int fd;
        char buf[32]={0};
/*      
        ret=mkfifo("myfifo",0666);
        if(ret<0){
                perror("mkfifo:");
        }
*/
        fd=open("/myfifo",O_RDONLY);
        if(fd<0){
                perror("open:");
                return 0;
        }

        while(1){
                //fgets(buf,sizeof(buf),stdin);
                ret=read(fd,buf,strlen(buf));
                if(ret>0){
                        printf("read data=%s\n",buf);
                }
        }

        return 0;
}

共享内存

共享内存就是通过mmap函数将将文件映射到系统内存,通过访问内存实现进程间通信!
void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);
返回值:
On success, mmap() returns a pointer to the mapped area
On error, the value MAP_FAILED (that is, (void *) -1) is returned
参数描述:
 If addr is NULL, then the kernel chooses the (page-aligned) address at which to create the mapping。一般为NULL,让系统自动选择内存地址
 length:申请的文件大小
 port:文件映射的权限
 	PROT_EXEC:可执行
 	PROT_READ:可读
 	PROT_WRITE:可写
 flags:文件对于其他线程的权限
 	MAP_SHARED:用于多进程通信,共享
 	MAP_PRIVATE:单个进程,私有
 	MAP_ANONYMOUS:匿名映射,适用于亲缘关系进程
fd:文件描述符。若是匿名映射,则为-1
offset:文件内容偏移量 	
注意事项:
1.文件映射权限<=文件打开权限,否则会报:权限拒绝错误
2.文件大小必须>0,否则会报总线错误
3.文件偏移量参数必须为0或者4K的整数倍
4.文件申请的length参数必须>0
5.文件映射成功之后,可以关闭文件描述符
6.文件映射内存的申请是以页(4K)为单位的
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define RD 0
int main(int argc,char** argv){

        int fd;
        void* maddr;
        fd=open("test",O_RDWR);
        if(fd<0){
                perror("open:");
                return 0;
        }
        maddr=mmap(NULL,128,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
        if(maddr==MAP_FAILED){
                perror("mmap:");
                return 0;
        }
        close(fd);//文件映射成功之后可以关闭文件描述符
        //写进程使用
        int i=0;
        for(i=0;i<128;i++){
                memcpy((maddr+i),"a",1);
                sleep(1);
        }

#ifdef RD       
        //读进程使用
        while(1){
                printf("read data=%s\n",(char*)maddr);
                sleep(1);
        }
#endif
        munmap(maddr,128);//释放映射文件内存
        return 0;

}

信号

信号相关概念
信号机制:信号机制其实是对软件中断的一种模拟,异步通信,产生信号通知进程
信号产生的方式:按键,发送命令(kill),系统调用函数,硬件异常
进程对对信号不同的反应方式:
	缺省:按照信号默认的方式
	忽略:忽略该信号,不做任何反应
	捕捉:修改该信号的行为
常用信号(具体解释可以自行百度):kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
发送信号函数:
int kill(pid_t pid, int sig);//指定进程发送
int raise(int sig);//自己给自己发送
int pause(void);阻塞函数
定时器函数:
unsigned int alarm(unsigned int seconds);
useconds_t ualarm(useconds_t usecs, useconds_t interval);
int setitimer(int which, const struct itimerval *new_value,
                     struct itimerval *old_value);
                     
                     
struct itimerval {
    struct timeval it_interval; /* Interval for periodic timer */
     struct timeval it_value;    /* Time until next expiration */
 };

struct timeval {
    time_t      tv_sec;         /* seconds */
    suseconds_t tv_usec;        /* microseconds */
};

捕捉信号
捕捉信号函数:

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
//由于uinx系统分类不一致,所以一般建议使用sigaction函数


 int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
                     
struct sigaction {
	void     (*sa_handler)(int);
	void     (*sa_sigaction)(int, siginfo_t *, void *);
	sigset_t   sa_mask;
	int        sa_flags;
 	void     (*sa_restorer)(void);
};
#include<stdio.h>
#include<unistd.h>
#include <signal.h>
#include <sys/time.h>
typedef void (*sighandler_t)(int);
sighandler_t oldAct;

void handle(int sig){
        if(sig==SIGINT){
                printf("catch SIGINT!\n");
                //signal(SIGINT,oldAct);//捕捉一次INT信号后复原默认处理函数
        }else if(sig==SIGALRM){
                printf("timer!\n");
                //alarm(1);使用alarm循环捕捉SIGALRM
        }
}


int main(int argc,char** argv){

        //oldAct=signal(SIGINT,handle);
        struct sigaction act;
        act.sa_handler=handle;
        act.sa_flags=SA_SIGINFO;
        //      sigaction(SIGINT,&act,NULL);
        sigaction(SIGALRM,&act,NULL);
        //      alarm(1);
        /*时间结构体参数的设置*/
        struct itimerval cur_value;
        //间隔
        cur_value.it_interval.tv_sec=1;
        cur_value.it_interval.tv_usec=0;
        //延迟
        cur_value.it_value.tv_sec=5;
        cur_value.it_value.tv_usec=0;

        setitimer(ITIMER_REAL,&cur_value,NULL);
        while(1){
//                sleep(1);
                
        }
        return 0;

}

利用SIGCHLD信号,实现主进程回收(wait)子进程阻塞问题
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>

void handle(int sig){
        printf("sig=%d\n",sig);
        wait(NULL);//阻塞函数

}

int main(int argc,char** argv){
        pid_t pid;
        struct sigaction act;
        act.sa_handler=handle;
        sigemptyset(&act.sa_mask);
        act.sa_flags=SA_SIGINFO;

        pid=fork();

        if(pid<0){
                perror("fork");
                return 0;
        }else if(pid>0){
                sigaction(SIGCHLD,&act,NULL);
                while(1){
                        printf("main process\n");
                        sleep(1);
                }
                
        }else{

                sleep(5);
                printf("child proces exit!\n");
                exit(0);
        }
}

信号阻塞
信号阻塞:有些时候我们我们想要接受到信号,但不立即运行该信号的回调函数(此时系统正在处理更加紧急的任务),但是不能忽略该信号。而是过一段时间之后在开始执行,这就是信号的阻塞

信号状态:
信号递达(delivery):接受到信号并执行其回调函数
信号未决(peding):信号产生到递达之间的状态

信号集:
信号的集合,也就是存放信号的一种数据结构
未决信号集:
存放系统未决信号状态的信号集,该数据集无法改变
信号屏蔽字:
可以通过函数改变,决定屏蔽哪个信号(修改bit位(0表示不屏蔽,1表示屏蔽)来确定屏蔽哪个信号)
相关函数介绍:
int sigemptyset(sigset_t *set);//清空信号集,不屏蔽所有信号
int sigfillset(sigset_t *set);//填充信号集,屏蔽所有信号
int sigaddset(sigset_t *set, int signum);//将信号添加到信号屏蔽字
int sigdelset(sigset_t *set, int signum);//将信号从信号屏蔽字中删除
int sigismember(const sigset_t *set, int signum);//判断信号是否存在在信号屏蔽字中

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);//真正的信号屏蔽函数
#include<stdio.h>
#include<signal.h>
#include<unistd.h>


void handle(int sig){

        printf("get sig=%d\n",sig);
}

int main(int argc,char** argv){
        struct sigaction act;
        act.sa_handler=handle;
        sigemptyset(&act.sa_mask);
        act.sa_flags=0;

        sigset_t set;
        sigemptyset(&set);
        sigaddset(&set,SIGINT);
        sigprocmask(SIG_BLOCK,&set,NULL);
        
        sigaction(SIGINT,&act,NULL);
        sleep(5);//睡眠5秒后不屏蔽
        sigprocmask(SIG_UNBLOCK,&set,NULL);

        while(1){
                sleep(1);
        
        }

}

信号驱动任务执行
适用情况:接受到信号开始执行程序,执行完之后阻塞继续等待信号
int pause(void);等待信号阻塞函数

int sigsuspend(const sigset_t *mask);
将屏蔽的信号用新的信号集代替,并阻塞等待信号的到来
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
void handler(int sig){
        printf("get sig =%d\n",sig);
}

void mytask(){
        printf("task start!\n");
        sleep(5);
        printf("task end\n");

}
int main(int argc,char** argv){
        struct sigaction act;
        act.sa_handler=handler;
        sigemptyset(&act.sa_mask);
        act.sa_flags=0;
        
        //添加信号屏蔽函数,保证在任务执行期间不被INT信号打断
        sigset_t set;
        sigemptyset(&set);
        sigaddset(&set,SIGINT);

        sigset_t mask;
        sigemptyset(&mask);

        sigaction(SIGINT,&act,NULL);
        pause();
        
        while(1){
                sigprocmask(SIG_BLOCK,&set,NULL);
                mytask();
/*下面两句代码存在的情况是在任务执行期间确实不会被int信号中断,但是任务执行完毕之后,对于在任务执行中接收到的信号不会再次启动任务*/
        //      sigprocmask(SIG_UNBLOCK,&set,NULL);
        //      pause();
        //      为了保证接受到的信号量都会响应,使用sigsuspend函数
                sigsuspend(&mask);
        }


}

消息队列及systemV通信机制

该章节使用的ipc通信机制已经落后,一般不再采用,因此不过多详细介绍

消息队列

信号量

信号量表示资源的数量,通过P/V操作来实现
P操作(申请)if(信号量的值>0{
	申请资源任务继续运行
	信号量值-1
}else {
	申请资源任务阻塞
}
V操作(释放资源):
信号量值+1
if(有任务等待资源){
	唤醒等待任务,继续运行
}

信号灯种类:
有名信号量
无名信号量(用于linux线程同步)
systemV信号量

有名信号量相关函数:
创建
sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);
name:信号量名字
oflag:打开方式,常用O_CREAT
mode:文件权限,0666
value:信号量的值。二元信号量值为1
信号量文件位置:/dev/shm
销毁:
int sem_close(sem_t *sem);
int sem_unlink(const char *name);
P操作:
int sem_wait(sem_t *sem);
v操作:
int sem_post(sem_t *sem);

无名信号量:
创建:int sem_init(sem_t *sem, int pshared, unsigned int value);
销毁:int sem_destroy(sem_t *sem);
P操作:int sem_wait(sem_t *sem);
v操作:int sem_post(sem_t *sem);

四、网络编程

网络采取分层结构:

1.每一层实现不同的功能,对数据进行透明传输
2.网络每一层向上层提供服务,同时享受下一层的服务
模型图:
在这里插入图片描述

各层典型协议:

1.网络物理接口层:
MAC地址:48位地址,全球唯一,网络身份的标识
ARP/RARP:
ARP:IP地址------->MAC地址
RARP:MAC地址---------->IP地址

PPP协议:拨号协议(GPS/3G/4G)
2.网络层协议
IP (IPv4 · IPv6) (Internet Protocol) 网络之间互连的协议
ICMP (Internet Control Message Protocol )Internet 控制报文协议。它是TCP/IP 协议族的一个子协议,用于在IP 主机、路由器之间传递控制消息。举例:ping命令
3.传输层
TCP (Transmission Control Protocol )传输控制协议提供可靠的面向连接的服务,传输数据前须先建立连接,结束后释放。可靠的全双工信道。可靠、有序、无丢失、不重复。
UDP (User Datagram Protocol )用户数据报协议发送数据前无需建立连接,不使用拥塞控制,不保证可靠交付,最大努力交付。
4.应用层
FTP (File Transfer Protocol )文件传输协议<端口号21>减少或消除不同操作系统下处理文件的不兼容性。
HTTP (Hypertext Transfer Protocol )超文本传输协议 <端口号 80>, 面向事务的应用层协议。
SSH (Secure Shell )安全外壳协议

嵌入式相关:
NTP网络时钟管理
SNMP:简单网络管理(网络设备集中管理)
RTP/RTSP:传输音视屏协议(安防监控)

网络的分装和拆包

在这里插入图片描述

网络编程预备知识

1.socket

是一个编程接口
是一种特殊的文件描述符(可以使用read,write等文件io函数),一种网络资源
socket类型

流式套接字(SOCK_STREAM):唯一对应TCP
 提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。

数据报套接字(SOCK_DGRAM):唯一对应UDP
 提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。

原始套接字(SOCK_RAW):对应多种协议,使用ping可以直接穿透传输层
 可以对较低层次协议如IP、ICMP直接访问。
在这里插入图片描述

2.IP地址

Internet中的主机要与别的机器通信必须具有一个IP地址
IP地址为32位****(IPv4)或者128位(IPv6)
每个数据包都必须携带目的IP地址和源IP地址,路由器依靠此信息为数据包选择路由
特殊IP:
局域网:192.XXX.XXX.XXX 10.XXX.XXX.XXX
广播:XXX.XXX.XXX.255 255.255.255.255(全网广播)
组播:224.XXX.XXX.XXX ~239.XXX.XXX.XXX

3.端口号

ip地址确认哪台机器,端口号确认哪个进程或者线程(数据传输给哪个任务)
保留端口:FTP 21, SSH 22 ,HTTP 80, HTTPS 469
1025-5000端口不建议使用
可以使用:5000~65535
TCP 和UDP端口是相互独立的

4.字节序

1.cpu访问多字节数据时存在字节序的问题(string字符串则不存在这个问题)
大端字节序:数据高位对应cpu内存地址高位
小端字节序:数据低位对应vpu内存地址高位
在这里插入图片描述
2.字节序转换相关函数
(1)主机字节序到网络字节序
  u_long htonl (u_long hostlong);
  u_short htons (u_short short);
(2)网络字节序到主机字节序
  u_long ntohl (u_long hostlong);
  u_short ntohs (u_short short);
3.IP地址转换
(1)将strptr所指的字符串转换成32位的网络字节序二进制值
  int_addr_t inet_addr(const char *strptr);
(2)将IPV4/IPV6的地址转换成binary格式
  int inet_pton(int af, const char *src, void *dst);
逆过程:inet_ntop()
参数:
  af:AF_INET(IPV4)AF_INET6(IPV6)
  src:IPV4点分十进制
  dst:转换后的结果

TCP编程

在这里插入图片描述

socket函数

 #include <sys/types.h>          /* See NOTES */
 #include <sys/socket.h>
 int socket(int domain, int type, int protocol);
 参数:
1.domain:
	AF_INET      IPv4 Internet protocols                    ip(7)
	AF_INET6     IPv6 Internet protocols                    ipv6(7)	
	AF_UNIX      Local communication                        unix(7)
    AF_LOCAL     Synonym for AF_UNIX
    AF_NETLINK   Kernel user interface device               netlink(7)
    AF_PACKET    Low-level packet interface                 packet(7)
    
2.type
SOCK_STREAM:流式套接字,唯一对应TCP
SOCK_DGRAM:数据报套接字,唯一对应UDP
SOCK_RAM:原始套接字

3.protocol:一般填0,使用原始套接字需要填充
返回值:
RETURN VALUE
On success, a file descriptor for the new socket is returned.   On  error, -1 is returned, and errno is set appropriately.

bind函数

SYNOPSIS
       #include <sys/types.h>          
       #include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:
sockfd:通过socket拿到的文件描述符
addr:结构体变量的地址
struct sockaddr {
sa_family_t sa_family;
char        sa_data[14];
};

具体要我们填充的数据结构体
 struct sockaddr_in {
	sa_family_t    sin_family;&emsp;&emsp; /* address family: AF_INET */
	in_port_t      sin_port;  &emsp;&emsp; /* port in network byte order */
	struct in_addr sin_addr;  &emsp;&emsp; /* internet address */
} 

struct in_addr {
	uint32_t       s_addr;     /* address in network byte order */
 };
addrlen:地址长度

listen()函数

SYNOPSIS
       #include <sys/types.h>        
       #include <sys/socket.h>
	int listen(int sockfd, int backlog);
	
参数:
backlog:表示三次握手阶段最多允许几个客户端连接,一般设置为5
return:
-1表示失败,0表示成功

accept()函数

**SYNOPSIS**
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:
addr:存放保存连接客户端的地址
addlen:存放保存连接客户端的地址长度
返回值:
 On  success,  these system calls return a nonnegative integer that is a file descriptor for the accepted socket.  On error, -1 is returned, errno is set appropriately, and addrlen is left unchanged.

connect()函数

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);

与bind相类似

client.c

 1 #include "net.h"
  2 
  3 int main(){
  4     int fd=-1;
  5     fd=socket(AF_INET,SOCK_STREAM,0);
  6     if(fd<0){
  7         perror("socket");
  8         exit(1);
  9     }
 10 
 11     struct sockaddr_in sin;
 12     sin.sin_family=AF_INET;
 13     sin.sin_port=htons(SERVE_POET);
 14 #if 0
 15     sin.sin_addr.s_addr=inet_addr(SERVE_ADDR);
 16 #else
 17     if((inet_pton(AF_INET,SERVE_ADDR,&sin.sin_addr.s_addr)!=1)){
 18         perror("inet_pton");
 19         exit(1);
 20     }
 21 #endif
 22 
 23     if((connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
 24         perror("connect");
 25         exit(1);
 26     }
 27     char buf[BUFSIZE];
 28     while(1){
 29         bzero(buf,strlen(buf));
 30         printf(">");
 31         if((fgets(buf,32,stdin)==NULL)){
 32             perror("fgets");
 33             continue;
 34         }
 35         if((strncasecmp(STR_QUIT,buf,strlen(STR_QUIT)))==0){
 35         if((strncasecmp(STR_QUIT,buf,strlen(STR_QUIT)))==0){
 36             break;
 37         }
 38         write(fd,buf,strlen(buf));
 39     }
 40 
 41     close(fd);
 42 
 43 
 44 }

server.c

 1 #include "net.h"
  2 int main(){
  3 
  4     /*1.创建socket套接字(文件描述符)*/
  5     int fd=-1;
  6     if((fd=socket(AF_INET,SOCK_STREAM,0))<0){
  7         perror("socket");
  8         exit(-1);
  9     }
 10     /*2.绑定bind*/
 11     /*2.1填充结构体数据*/
 12     struct sockaddr_in sin;
 13     bzero(&sin,sizeof(sin));
 14     sin.sin_family=AF_INET;
 15     sin.sin_port=htons(SERVE_POET);
 16 #if 1
 17    // sin.sin_addr.s_addr=inet_addr(SERVE_ADDR);
 18    /*优化1:能绑定任意服务器*/
 19    sin.sin_addr.s_addr=htonl(INADDR_ANY);
 20 #else
 21 
 22     if((inet_pton(AF_INET,SERVE_ADDR,(void*)&sin.sin_addr.s_addr)!=1)){
 23             perror("inet_pton");
 24             exit(1);
 25             }
 26 #endif
 27             /*2.2 bind*/
 28             if(( bind(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
 29                 perror("bind");
 30                 exit(1);
 31 
 32                 }
 33                 /*3.利用listen函数将主动套接字变为被动套结字*/
 34                 if(listen(fd,BACKLOG)<0){
 35                 perror("listen");
36                 exit(1);
 37                 }
 38                 /*4.阻塞等待客户端连接*/
 39 #if 0
 40                 int newfd=-1;
 41                 newfd=accept(fd,NULL,NULL);
 42                 if(newfd<0){
 43                 perror("accept");
 44                 exit(1);
 45                 }
 46 #else
 47                 //优化2:获取连接客户端信息
 48                 int newfd=-1;
 49                 struct sockaddr_in cin;
 50                 socklen_t addrlen=sizeof(cin);
 51                 char clientaddr[16];
 52                 newfd=accept(fd,(struct sockaddr*)&cin,&addrlen);//无法实现多个客户端连接,因此需要多线程来解决
 53                 if(newfd<0){
 54                     perror("accept");
 55                     exit(1);
 56                 }
 57                 /*网络字序转换*/
 58                 if(!(inet_ntop(AF_INET,(void*)&cin.sin_addr.s_addr,clientaddr,sizeof(cin)))){
 59                     perror("inet_ntop");
 60                     exit(1);
 61                 }
 62                 printf("client addr:%s port:%d\n",clientaddr,ntohs(cin.sin_port));
 63 
 64 
 65 #endif
 66 
 67                 /*5.读写*/
 68                 char buf[BUFSIZE];
 69                 int ret=-1;
 70                 while(1){
 71                     bzero(buf,sizeof(buf));
 72                     do{
 73                         ret=read(newfd,buf,BUFSIZE-1);
 74 
 75                     }while(ret<0 && EINTR==errno);
 76 
 77                     if(ret<0){
 78                         perror("read");
 79                         exit(1);
 80                     }
 81 
 82                     printf("receive %s",buf);
 83 
 84                     if(!strncasecmp(STR_QUIT,buf,strlen(STR_QUIT))){
 85                         printf("Client is exiting\n!");
 86                         break;
 87                     }
 88                 }
 89                 close(newfd);
 90                 close(fd);
 91                 return 0;
 92 }

net.h

  1 #ifndef _NET_H_
  2 #define _NET_H_
  3 #include<stdio.h>
  4 #include<stdlib.h>
  5 #include<unistd.h>
  6 #include<string.h>
  7 #include<strings.h>
  8 #include <sys/types.h>          /* See NOTES */
  9 #include <sys/socket.h>
 10 #include <netinet/in.h>
 11 #include <netinet/ip.h> /* superset of previous */
 12 #include <arpa/inet.h>
 13 #include<errno.h>
 14 
 15 
 16 
 17 #define SERVE_POET 5001
 18 #define SERVE_ADDR "127.0.0.1"
 19 #define BACKLOG 5
 20 #define BUFSIZE 128
 21 #define STR_QUIT "quit"
 22 
 23 
 24 
 25 
 26 
 27 #endif
 28 

TCP并发服务器多线程/多进程

多线程服务器
server.c

1 #include<pthread.h>
  2 #include "net.h"
  3 void cli_data_handler(void* arg);
  4 int main(){
  5 
  6     /*1.创建socket套接字(文件描述符)*/
  7     int fd=-1;
  8     if((fd=socket(AF_INET,SOCK_STREAM,0))<0){
  9         perror("socket");
 10         exit(-1);
 11     }
 12     /*2.绑定bind*/
 13     /*2.1填充结构体数据*/
 14     struct sockaddr_in sin;
 15     bzero(&sin,sizeof(sin));
 16     sin.sin_family=AF_INET;
 17     sin.sin_port=htons(SERVE_POET);
 18 #if 1
 19     // sin.sin_addr.s_addr=inet_addr(SERVE_ADDR);
 20     /*优化1:能绑定任意服务器*/
 21     sin.sin_addr.s_addr=htonl(INADDR_ANY);
 22 #else
 23 
 24     if((inet_pton(AF_INET,SERVE_ADDR,(void*)&sin.sin_addr.s_addr)!=1)){
 25         perror("inet_pton");
 26         exit(1);
 27     }
 28 #endif
 29     /*2.2 bind*/
 30     if(( bind(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
 31         perror("bind");
 32         exit(1);
 33 
 34     }
 35     /*3.利用listen函数将主动套接字变为被动套结字*/
36     if(listen(fd,BACKLOG)<0){
 37         perror("listen");
 38         exit(1);
 39     }
 40 
 41     int newfd=-1;
 42     while(1){
 43         pthread_t pid;
 44         struct sockaddr_in cin;
 45         socklen_t addrlen=sizeof(cin);
 46         char cli_addr[16];
 47         newfd=accept(fd,(struct sockaddr*)&cin,&addrlen);
 48         if(newfd<0){
 49             perror("accept");
 50             exit(1);
 51         }
 52 
 53         if(!inet_ntop(AF_INET,(void*)&cin.sin_addr.s_addr,cli_addr,addrlen)){
 54             perror("inet_ntop");
 55             exit(1);
 56         }
 57         printf("client addr=%s, port=%d\n",cli_addr,(int)ntohs(cin.sin_port));
 58         pthread_create(&pid,NULL,(void*)cli_data_handler,(void*)&newfd);
 59 
 60     }
 61     close(fd);
 62     return 0;
 63 }
 64 
 65 void cli_data_handler(void* arg){
 66     int newfd=*(int*)arg;
 67     printf("newfd=%d\n",newfd);
 68     int ret=-1;
 69     char buf[BUFSIZ];
 70     while(1){
 71         do{
 72             bzero(buf,BUFSIZE);
 73             ret=read(newfd,buf,BUFSIZE);
 74 
 75         }while(ret<0 && EINTR==errno);
 76         if(ret<0){
 77             perror("read");
 78             exit(1);
 79         }
 80         printf("%s",buf);
 81 
 82         if(!strncasecmp(STR_QUIT,buf,strlen(STR_QUIT))){
 83             printf("client is exiting\n");
 84             break;
 85         }
 86 
 87     }
 88     close(newfd);
 89 }

client.c

1 #include "net.h"
  2 void usage(char* s){
  3     printf("\n%s  server_ip,server_port",s);
  4     printf("\n server_ip:seriver ip address");
  5     printf("\n server_port:server port(>5000)\n\n");
  6 
  7 }
  8 int main(int argc,void* argv[]){
  9     int fd=-1;
 10     if(argc<3){
 11         usage(argv[0]);
 12         exit(1);
 13     }
 14     short port;
 15     fd=socket(AF_INET,SOCK_STREAM,0);
 16     if(fd<0){
 17         perror("socket");
 18         exit(1);
 19     }
 20 
 21     port=atoi(argv[2]);
 22     if(port<5000){
 23         usage(argv[0]);
 24         exit(1);
 25     }
 26 
 27     struct sockaddr_in sin;
 28     sin.sin_family=AF_INET;
 29     sin.sin_port=htons(port);
 30 #if 0
 31     sin.sin_addr.s_addr=inet_addr(SERVE_ADDR);
 32 #else
 33     if((inet_pton(AF_INET,argv[1],&sin.sin_addr.s_addr)!=1)){
 34         perror("inet_pton");
 35         exit(1);
 36     }
 37 #endif
 38 
 39     if((connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
 40         perror("connect");
 41         exit(1);
 42     }
 43     char buf[BUFSIZE];
 44     while(1){
 45         bzero(buf,strlen(buf));
 46         printf(">");
 47         if((fgets(buf,32,stdin)==NULL)){
 48             perror("fgets");
 49             continue;
 50         }
 51         if((strncasecmp(STR_QUIT,buf,strlen(STR_QUIT)))==0){
 52             break;
 53         }
 54         write(fd,buf,strlen(buf));
 55     }
 56 
 57     close(fd);
 58 
 59 
 60 }

TCP多进程
server.c

 1 #include<signal.h>
  2 #include"net.h"
  3 #include <sys/wait.h>
  4 void waitact(int sig){
  5     if(sig==SIGCHLD){
  6         waitpid(-1,NULL,WNOHANG);
  7     }
  8 
  9 }
 10 int main(){
 11     int fd=-1;
 12     fd=socket(AF_INET,SOCK_STREAM,0);
 13     if(fd<0){
 14         perror("socket");
 15         exit(0);
 16     }
 17     //信号机制。回收进程
 18     sigaction(SIGCHLD,(void*)&waitact,NULL);
 19 
 20     struct sockaddr_in sin;
 21     sin.sin_port=htons(SERVE_POET);
 22     sin.sin_family=AF_INET;
 23 #if 0
 24     if((inet_pton(AF_INET,SERVE_ADDR,&sin.sin_addr.s_addr))<0){
 25         perror("inet_pton");
 26         exit(0);
 27     }
 28 #else
 29     sin.sin_addr.s_addr=htonl(INADDR_ANY);
 30 #endif
 31     if((bind(fd,(struct sockaddr*)&sin,sizeof(sin)))<0){
 32         perror("bind");
 33         exit(1);
 34     }
 35 
36     if((listen(fd,BACKLOG))<0){
 37         perror("lsiten");
 38         exit(1);
 39     }
 40     int newfd=-1;
 41     struct sockaddr_in  cin;
 42     socklen_t addrlen=sizeof(cin);
 43     char clientaddr[16];
 44     while(1){
 45         bzero(clientaddr,sizeof(clientaddr));
 46         newfd=accept(fd,(struct sockaddr*)&cin.sin_addr.s_addr,&addrlen);
 47         if(newfd<0){
 48             perror("accept");
 49             exit(1);
 50         }
 51         pid_t pid;
 52         pid=fork();
 53         if(pid<0){
 54             perror("fork");
 55             exit(1);
 56         }else if(pid==0){
 57             close(fd);
 58             if((inet_ntop(AF_INET,(struct sockaddr*)&sin.sin_addr.s_addr,clientaddr,addrlen))<0){
 59                 perror("inet_ntop");
 60                 exit(1);
 61             }
 62             printf("client addr=%s,port=%d\n",clientaddr,ntohs(sin.sin_port));
 63             char buf[BUFSIZE];
 64             int ret=-1;
 65             while(1){
 66                 do{
 67                     bzero(buf,BUFSIZE);
 68                     ret=read(newfd,buf,BUFSIZE);
 69                 }while(ret<0 && EINTR==errno);
 70                 if(ret<0){
 71                     perror("read");
 72                     exit(1);
 73                 }
 74                 printf("receive data=%s",buf);
 75                 if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
 76                     printf("client is exiting!\n");
 77                     break;
 78                 }
 79             }
 80             close(newfd);
 81         }else{//主线程
 82             close(newfd);
 83         }
 84     }
 85     close(fd);
 86 
 87 
 88 
 89 
 90 }

UDP编程

在这里插入图片描述

network拓展API:

send()/write():

SYNOPSIS
       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t send(int sockfd, const void *buf, size_t len, int flags);

       ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
参数:flag:
 为0.则和write()函数一样
 MSG_DONTWAIT:不阻塞
 MSG_OOB:(out-of-band data)外带函数,tcp类型
recv()/read()

SYNOPSIS
       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t recv(int sockfd, void *buf, size_t len, int flags);

       ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);

参数: falgs
为0则和read一样
 MSG_DONTWAIT:不阻塞
 MSG_OOB:(out-of-band data)外带函数,tcp类型
 MSG_PEEK:读取数据都是从头开始,而不移动读取位置

server.c

1 #include "net.h"
  2 
  3 int main(){
  4     int fd=-1;
  5     fd=socket(AF_INET,SOCK_DGRAM,0);
  6     if(fd<0){
  7         perror("socket");
  8         exit(0);
  9 
 10     }
 11 
 12     int b_reuse = 1;
 13     setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));
 14 
 15 
 16     struct sockaddr_in sin;
 17     sin.sin_family=AF_INET;
 18     sin.sin_port=htons(SERVE_POET);
 19     sin.sin_addr.s_addr=htonl(INADDR_ANY);
 20 
 21     if(bind(fd,(struct sockaddr*)&sin,sizeof(sin))<0){
 22         perror("bind");
 23         exit(0);
 24     }
 25 
 26     struct sockaddr_in cin;
 27     char buf[BUFSIZE];
 28     socklen_t addrlen=sizeof(cin);
 29     char cli_addr[16];
 30     printf("UDPserver is starting ......\n");
 31     while(1){
 32         bzero(buf,BUFSIZE);
 33         if(recvfrom(fd,buf,BUFSIZE-1,0,(struct sockaddr *)&cin,&addrlen)<0){
 34             perror("recvfrom");
 35             continue;
 36         }
 37 
 38         if(inet_ntop(AF_INET,(struct sockaddr*)&cin.sin_addr.s_addr,cli_addr,addrlen)<0){
 39             perror("inet_ntop");
 40             continue;
 41         }
 42         printf("client(%s:%d):receive data=%s\n",cli_addr,ntohs(cin.sin_port),buf);
 43 
 44         if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
 45             printf("client(%s:%d) is exit!\n",cli_addr,ntohs(cin.sin_port));
 46         }
 47 
 48     }
 49     close(fd);
 50     return 0;
 51 
 52 }
                

client .c

1 #include "net.h"
  2 
  3 void usage(char* s){
  4     printf("\n%s:server_ip,server_port:\n",s);
  5     printf("server_ip:server ipaddress\n");
  6     printf("server_port:sever port(>5000)\n");
  7 
  8 }
  9 int main(int argc,void* argv[]){
 10     int fd=-1;
 11     fd=socket(AF_INET,SOCK_DGRAM,0);
 12     if(fd<0){
 13         perror("socket");
 14         exit(1);
 15     }
 16     if(argc<3){
 17         usage(argv[0]);
 18         exit(0);
 19     }
 20     int port;
 21     port=atoi(argv[2]);
 22     if(port<5000){
 23         usage(argv[0]);
 24         exit(1);
 25     }
 26 
 27     char buf[BUFSIZE];
 28     struct sockaddr_in sin;
 29     sin.sin_port=htons(port);
 30     sin.sin_family=AF_INET;
 31     if(inet_pton(AF_INET,argv[1],(void*)&sin.sin_addr.s_addr)<0){
 32         perror("inet_pton");
 33         exit(1);
 34     }
 35 
 36     while(1){
 37         bzero(buf,sizeof(buf));
 38         printf(">");
 39         if(fgets(buf,BUFSIZE,stdin)==NULL){
 40             perror("fgets");
 41             continue;
 42         }
 43 
 44         if(sendto(fd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin))<0){
 45             perror("sendto");
 46             continue;
 47         }
 48         if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
 49             printf("client is exited!\n");
 50             break;
 51         }
 52 
 53     }
 54     close(fd);
 55 
 56 
 57 
 58     return 0;
 59 }

多路复用(select函数使用,解决阻塞的问题)

步骤:

1.fd_se集合,将关心的文件描述符加入到fd_set集合中
2.select函数阻塞监控
3.1个或者多个文件描述符有数据exit
4.判断哪个文件描述符有数据
5.依次对数据进行处理

相关函数介绍

1、 fd_set
       void FD_CLR(int fd, fd_set *set);//将fd从集合中清除
       int  FD_ISSET(int fd, fd_set *set);//判断fd是否在集合中
       void FD_SET(int fd, fd_set *set);//将fd加入到集合中
       void FD_ZERO(fd_set *set);//集合初始化
       
2.select
       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

nfds:maxfd+1
readfds:读集合
writerfds:写集合,一般为NULL
expectfds:异常处理集合(带外数据),一般为NULL
timerout:阻塞超时时间

notes:select函数前后,集合性质发生了变化,select是将关心的fd加入集合,select是有数据的fd保留了下来,因此需要用FD_ISSET判断进行相应的操作。

client.c

 1 #include"net.h"
  2 void usage(char*s){
  3     printf("\n%s: server_addr,server_port(>5000)\n",s);
  4 }
  5 int main(int argc,void *argv[]){
  6     int fd=-1;
  7     fd=socket(AF_INET,SOCK_STREAM,0);
  8     if(fd<0){
  9         perror("socket");
 10         exit(1);
 11     }
 12     if(argc<3){
 13         usage(argv[0]);
 14         exit(1);
 15     }
 16     int port=atoi(argv[2]);
 17     if(port<5000){
 18         usage(argv[0]);
 19         exit(1);
 20     }
 21 
 22     struct sockaddr_in sin;
 23     sin.sin_family=AF_INET;
 24     sin.sin_port=htons(port);
 25     if(inet_pton(AF_INET,argv[1],&sin.sin_addr.s_addr)<0){
 26         perror("inet_pton");
 27         exit(1);
 28     }
 29 
 30     if(connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0){
 31         perror("connect");
 32         exit(1);
 33     }
 34 
 35     fd_set rset;
36     int maxfd=-1;
 37     struct timeval timeout;
 38     timeout.tv_sec=5;
 39     timeout.tv_usec=0;
 40     int ret=-1;
 41     char buf[BUFSIZE];
 42     while(1){
 43         bzero(buf,BUFSIZE);
 44         FD_ZERO(&rset);
 45         FD_SET(0,&rset);
 46         FD_SET(fd,&rset);
 47         maxfd=fd;
 48         select(maxfd+1,&rset,NULL,NULL,&timeout);
 49 
 50         if(FD_ISSET(0,&rset)){//从标准输入当中读取字符
 51             do{
 52                 ret=read(0,buf,BUFSIZE-1);
 53             }while(ret<0 && EINTR==errno);
 54 
 55             if(ret<0){
 56                 perror("read");
 57                 continue;
 58             }
 59             if(!ret)continue;//没有字符输入
 60 
 61             write(fd,buf,BUFSIZE);
 62             if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
 63                 printf("client is exited!\n");
 64                 break;
 65             }
 66         }
 67         if(FD_ISSET(fd,&rset)){
 68             bzero(buf,BUFSIZE);
 69             do{
 70                 ret=read(fd,buf,BUFSIZE);
 71 
 72             }while(ret<0 && EINTR==errno);
 73             if(ret<0){
 74                 perror("read");
 75                 continue;
 76             }
 77             if(!ret)break;//服务器关闭
 78             printf("sever data:%s\n",buf);
 79             if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
 80                 printf("server is closed\n");
 81                 break;
 82             }
 83 
 84         }
 85 
 86     }
 87     close(fd);
 88 
 89 
 90 }

TCP IP协议

以太网头

在这里插入图片描述

IP头

在这里插入图片描述

tcp头

在这里插入图片描述

tcp握手过程分析

在这里插入图片描述

网络编程扩展

网络信息检索

域名解析相关函数
SYNOPSIS
#include <netdb.h>
extern int h_errno;
struct hostent *gethostbyname(const char *name);//域名解析

#include <sys/socket.h>       /* for AF_INET */
struct hostent *gethostbyaddr(const void *addr,
                                     socklen_t len, int type);

void herror(const char *s);//报错提示
const char *hstrerror(int err);

 /* System V/POSIX extension */
struct hostent *gethostent(void);
struct hostent {
     char  *h_name;            /* official name of host */
     char **h_aliases;         /* alias list */
     int    h_addrtype;        /* host address type */
     int    h_length;          /* length of address */
     char **h_addr_list;       /* list of addresses */
}
#define h_addr h_addr_list[0] /* for backward compatibility */


RETURN VALUE
       The gethostbyname() and gethostbyaddr() functions return the hostent structure or a null pointer if an error occurs.
       On error, the h_errno variable holds an error number.  When non-NULL, the return value may point at static data, see the notes below.
 1 #include<netdb.h>
  2 #include "net.h"
  3 void usage(char* s){
  4     printf("\n%s  server_name,server_port",s);
  5     printf("\n server_name:seriver ip address");
  6     printf("\n server_port:server port(>5000)\n\n");
  7 
  8 }
  9 int main(int argc,void* argv[]){
 10     int fd=-1;
 11     if(argc<3){
 12         usage(argv[0]);
 13         exit(1);
 14     }
 15     short port;
 16     fd=socket(AF_INET,SOCK_STREAM,0);
 17     if(fd<0){
 18         perror("socket");
 19         exit(1);
 20     }
 21     
 22     port=atoi(argv[2]);
 23     if(port<5000){
 24         usage(argv[0]);
 25         exit(1);
 26     }
 27     //域名处理
 28     struct hostent *hs;
 29     if((hs=gethostbyname(argv[1]))==NULL){
 30         herror("gethostname");
 31         exit(1);
 32     }
 33     struct sockaddr_in sin;
 34     sin.sin_family=AF_INET;
 35     sin.sin_port=htons(port);
 36 #if 1
 37     sin.sin_addr.s_addr=*(uint32_t *)hs->h_addr;//域名转换
 38 #else
 39     if((inet_pton(AF_INET,argv[1],&sin.sin_addr.s_addr)!=1)){
 40         perror("inet_pton");
 41         exit(1);
 42     }
 43 #endif
 44 
 45     if((connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
 46         perror("connect");
 47         exit(1);
 48     }
 49     char buf[BUFSIZE];
 50     while(1){
 51         bzero(buf,strlen(buf));
 52         printf(">");
 53         if((fgets(buf,32,stdin)==NULL)){
 54             perror("fgets");
 55             continue;
 56         }
 57         if((strncasecmp(STR_QUIT,buf,strlen(STR_QUIT)))==0){
 58             break;
 59         }
 60         write(fd,buf,strlen(buf));
 61     }
 62 
 63     close(fd);
 64 
 65 
 66 }

网络属性设置

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int getsockopt(int sockfd, int level, int optname,
                      void *optval, socklen_t *optlen);
       int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);

level指定控制套接字的层次.可以取三种值: 
1)SOL_SOCKET:通用套接字选项. (应用层)
2)IPPROTO_IP:IP选项. (网络层)
3)IPPROTO_TCP:TCP选项. (传输层)
optname指定控制的方式(选项的名称),我们下面详细解释 

在这里插入图片描述

举例:
允许地址快速重用
int reuse=1;
setsockopt(fd,SOL_SOCKET,SO_RESUSERADDR,&reuse,sizeof(int))

网络超时优化

方法一:
设置socket的属性 SO_RCVTIMEO

参考代码如下
    struct timeval  tv;

     tv.tv_sec = 5;   //  设置5秒时间
     tv.tv_usec = 0;

     setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,  &tv,  
                       sizeof(tv));   //  设置接收超时
      recv() / recvfrom()    //   从socket读取数据
方法二:
用select检测socket是否’ready’

参考代码如下
   struct fd_set rdfs;
   struct timeval  tv = {5 , 0};   // 设置5秒时间

   FD_ZERO(&rdfs);
   FD_SET(sockfd, &rdfs);

   if (select(sockfd+1, &rdfs, NULL, NULL, &tv) > 0)   // socket就绪
   {
         recv() /  recvfrom()    //  从socket读取数据
   }
方法三:
设置定时器(timer), 捕捉SIGALRM信号

参考代码如下
    void  handler(int signo)     {   return;  }

      struct sigaction  act;
      sigaction(SIGALRM, NULL, &act);
      act.sa_handler = handler;
      act.sa_flags &= ~SA_RESTART;
      sigaction(SIGALRM, &act, NULL);
      alarm(5);
      if (recv(,,,) < 0) ……

广播发送

sender:UDP客户端
receiver:UDP服务端
广播发送:只需要修改客户端即可
加上如下代码:
	int so_br=1;
	setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&so_br,sizeof(int));

多播

组播接收
1.创建用户数据报套接字
2.加入多播组
3.绑定本机IP地址和端口
4.绑定的端口必须和发送方指定的端口相同
5.等待接收数据

加入多播组
struct  ip_mreq  mreq;
bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(“235.10.10.3”);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);

setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,  sizeof(mreq));
struct ip_mreq
{
     struct  in_addr  imr_multiaddr;
     struct  in_addr  imr_interface;
};

UINX域套接字(本地进程通信)

1.进程间通信方式:
管道,消息队列,共享内存,信号,unix域套接字
易用性:消息队列>unix套接字>管道>共享内存
效率性:共享内存>unix套接字>管道>消息队列
2.异步通信
信号
2.同步和互斥
信号量

unix域套接字
和普通tcp和udp套接字流程是一样的,唯一区别就是套接字类型不同,适用于本地通信
socket(AF_UNIX/AF_LOACL,SOCK_STRAM,0);
bind地址结构的需要改变:
          struct sockaddr_un {
               sa_family_t sun_family;               /* AF_UNIX */
               char        sun_path[108];            /* Pathname */
           };
还需要利用**access()函数**判断文件是否存在?

五、数据库开发

六、ARM体系结构

ARM体系结构导学

操作系统:向上提供接口(API),向下管理硬件
Linux系统组成:
1.进程管理:进程创建调度
2.内存管理:内存的申请释放
3.文件系统:访问磁盘文件管理
4.设备管理:硬件设备及驱动管理
5.网络协议:网络协议栈通信
计算机基础:
冯诺依曼计算机体系:
CPU(控制器,运算器),输入设备,输出设备,存储器
三级存储结构:cache(高速缓存),主存储器(内存),辅助存储器(硬盘),CPU只能访问cache和内存,不能直接访问硬盘
地址空间:CPU通过地址总线访问内存的空间,地址空间是有限的,取决于地址总线的数量,如:32位操作系统,他的地址空间一般为2^32=4G,因此32位操作系统内存不能超过4G,
CPU工作原理:
取指:
指令计数器存取想要执行指令的地址,将其发送给内存,内存将其地址中的内容返回给cpu的指令寄存器IR
译码:
译码器将指令解析成具体的运算
执行:
控制器控制相应的运算单元运算,运算结果存入寄存器中

在这里插入图片描述

ARM处理器架构

RISC(精简指令集)指令集和CISC(复杂指令集)指令集

ARM寄存器

ARM处理器拥有8种工作模式:
用户模式(USR):正常程序执行模式,不能直接切换到其他模式
系统模式(SYS):运行操作系统的特权任务,与用户模式类似,但具有可以直接切换到其他模式等特权
快中断模式(FIQ):支持高速数据传输及通道处理,FIQ异常响应时进入此模式
中断模式(IRQ):用于通用中断处理,IRQ异常响应时进入此模式
管理模式(SVC):操作系统保护模式,系统复位和软件中断响应时进入此模式(由系统调用执行软中断SWI命令触发)
中止模式(ABT):用于支持虚拟内存和/或存储器保护,在ARM7TDMI没有大用处
未定义模式(UND):支持硬件协处理器的软件仿真,未定义指令异常响应时进入此模式

ARM寄存器分为通用寄存器、专用寄存器、控制寄存器

R15,PC,程序计数器,存放执行指令的地址
R14,LR,链接寄存器
执行跳转指令(如:从main函数执行func函数,跳转到func函数实现),LR自动保存跳转指令下一条指令的地址,
使得执行完func函数后,将LR的值赋值给PC,重新返回main函数执行
产生异常(中断),LR自动保存跳转指令下一条指令的地址,异常处理结束后将LR的值赋值给PC,跳转到异常开始的地方继续执行
R13,栈指针,指向栈底的位置

CPSR,控制寄存器,32位,注意前四位和后7位代表的含义

ARM异常处理

异常概念:异常指的是处理器在正常执行程序的过程中遇到的不正常事件。异常发生时,处理器会暂停当前程序转而去处理异常事件,异常事件处理完成之后再回到之前被打断的地方继续执行程序。

异常源
在这里插入图片描述
异常模式
在这里插入图片描述

七、系统移植

开发板启动过程

在这里插入图片描述

1.开发板上电,pc=0,从IROM开始运行,执行BL0(bootload)程序,软硬件初始化,读取拨码开关的值,选择启动方式(以SD卡启动为例),将SD卡中uboot程序加载到RAM中
2.uboot程序运行,将linux程序,dtb(设备树),rootfs(根文件系统)加载到RAM中
3.运行linux程序,初始化环境,挂载rootfs

交叉编译环境配置

1.网络环境配置 开发板与ubuntu使用有线网线连接
2.tftp服务器配置 Ubuntu与开发板使用tftp协议进行文件传输
3.nfs服务器配置  nfs网络文件系统,能够在不同的计算机之间使用网络进行文件共享,开发板能够访问Ubuntu的文件

uboot

bootload:
操作系统运行之前的一段代码,用于将软硬件环境初始化到一个合适的状态,为操作系统的加载和运行做准备
bootload基本功能:
初始化软硬件环境
引导加载linux内核
给linux内核传参
执行用户命令
notes:bootLoad是引导加载程序的统称,在嵌入式linux经常使用的bootload是uboot

SD卡制作uboot启动盘:

uboot使用:
将开发板用串口线与电脑连接,打开secureCRT软件,即可进行交互操作:
常用命令:
bootcmd 上电启动命令
setenv 设置环境变量
printenv 答应环境变量
saveenv 保存环境变量
go 跳转到内存哪里开始运行

linux安装与加载

从tftp服务器将linux内核加载到内存
1.配置Ubuntu的tftp服务器及ip
2.将liunx内核(uimage),dtb设备数文件(exynos -4412,dtb),rootfs根文件(ramdisk)放到tftp服务器目录,修改权限为777
du -mh 查看文件大小
3.连接开发板,上电,进入uboot交互模式,设置自启动环境变量
setenv bootcmd tftp 0x41000000 uimage\;tftp 0x42000000 exynos4412.dtb\;tftp 0x43000000 ramdisk.img\;bootm 0x41000000 0x43000000 0x42000000
saveenv
解释:地址为什么从0x41000000开始?
查看芯片手册得知,exynos4412他的拓展内存是从0x40000000开始,且大小为1G,0x40000000到0x41000000之间需要存放uboot文件,因此从41000000开始
bootm 专用于启动linux系统命令


EMMC加载linux内核(产品交付常用模式):
1.将内存中的linux内核文件,设备树文件,根文件系统文件写入到外存(EMMC)中
使用tftp命令从tftp服务器中下载程序到内存,使用EMMC write 将文件从内存写入都EMMC中
tftp->内存->EMMC 
2.设置启动环境变量
setenv bootcmd mmc read 
3.saveenv

tftp加载linux内核,nfs挂载头rootfs(开发常用模式):

EMMC加载uboot(u-boot-fs4412.bin):
从tftp服务器下载uboot加载到内存,使用mmc命令打开引导分区,之后将镜像文件写入EMMC中

交叉编译

编译的原理:将高级语言转换成cpu能够识别的机器语言(二进制)
由于cpu架构的不同(X86,ARM架构),他们底层的指令(二进制命令)是不一样的,因此为了移植的方便
,只需要更改编译器就可以,而不需要修改代码

gcc编译的四个过程:
预处理:将源文件中宏定义和头文件展开,生成.
编译:编译为汇编代码,输出汇编文件
汇编:将汇编文件(.s)输出位目标文件(.o)
链接:将目标文件与附加目标文件(动态库 静态库 标准C库)链接起来,生成可执行文件

交叉工具链:arm-gcc适用于ARM架构的编译器

ELF文件:linux系统下可执行文件的格式

  • 28
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值