赞
踩
先输入普通用户登陆密码
Enter new UNIX password:设置超级用户密码
Retype new UNIX password:确认超级用户密码
1、确保虚拟机Linux和主机Windows ip互通
2、安装 openssh-server 工具包
[ sudo apt-get install openssd-server ]
3、确认ssh服务正在运行
[ sudo /etc/init.d/ssh status ]
若服务没有开启,启动/重启ssh服务即可
[ sudo /etc/init.d/ssh start/restart ]
4、使用SecureCRT工具访问Linux终端
连接时主机填写Linux系统IPv4地址,用户名填写普通用户名
/home : 默认普通用户的主目录
/root : 超级用户/管理员用户的主目录
/bin : 二进制文件,通常是系统使用的指令
/etc : 存放所有的系统管理所需要的配置文件以及其子目录
/lib : 系统会用到的库文件
/mnt : 该目录用于临时挂载其他文件系统
<1> 建立windows和Linux的共享目录
1.打开正在使用的客户机设置选项;
2.选择"总是启用" -> 共享文件夹
3.添加一个共享目录,运行向导
4.确认 退出
<2> 使用WinSCP软件,通过SFTP协议传输文件
apt-get命令是Debian Linux发行版中的APT软件包管理工具
用法: apt-get [选项] 命令
apt-get [选项] install|remove 软件包1 [软件包2 ...]
apt-get [选项] source 软件包1 [软件包2 ...]
常用选项:
update - 取回更新的软件包列表信息
upgrade - 进行一次升级
install - 安装新的软件包(注:软件包名称是 libc6 而非 libc6.deb)
remove - 卸载软件包
purge - 卸载并清除软件包的配置
autoremove - 卸载所有自动安装且不再使用的软件包
dist-upgrade - 发布版升级,见 apt-get(8)
dselect-upgrade - 根据 dselect 的选择来进行升级
build-dep - 为源码包配置所需的编译依赖关系
clean - 删除所有已下载的包文件
autoclean - 删除已下载的旧包文件
check - 核对以确认系统的依赖关系的完整性
source - 下载源码包文件
download - 下载指定的二进制包到当前目录
changelog - 下载指定软件包,并显示其changelog
arm-linux-gnueabihf-readelf -d 可执行文件 可查看运行当前可执行文件 所需要的库文件
ls:显示当前目录下的所有文件
-l:显示详细的文件信息 (详细信息的内容 ????)
-a:显示隐藏文件
-R:递归的显示目录下的以及子目录下的文件
dirName:显示指定目录下的文件
-l显示的文件:
文件类型 个数 所在组 修改日期
权限 所有者 大小 字体颜色
“-”表示普通文件;
“d”表示目录;
“l”表示链接文件;
“p”表示管理文件;
“b”表示块设备文件;
“c”表示字符设备文件;
“s”表示套接字文件
在大多数的linux shell窗口中,还能用颜色来区分不同 文件的属 性:
灰白色表示普通文件;
亮绿色表示可执行文件;
亮红色表示压缩文件;
灰蓝色表示目录;
亮蓝色表示链接文件;
亮黄色表示设备文件
cd:跳转到指定目录下(相对路径和绝对路径)
"-" -> 上一次cd指令访问的目录
"~" -> 当前正在使用的用户的主目录
su:切换用户(Ubuntu发行版的Linux)
cp:复制文件
cp [目标] [路径] [参数]
参数: -r 复制的目标是文件夹(目录)
-i 产生询问交互
-a 保留链接文件的属性
-f 直接覆盖目标而不给出提示
cat:以字符形式显示文件的内容
hexdump:以十六进制形式显示文件的内容
rm:删除文件
rm [参数] [目标]
参数: -r 指定删除的对象是目录
-f 删除目标而不给出提示
pwd:显示当前工作路径
touch:创建一个新文件
mkdir:创建一个新目录
参数: -p 允许一次创建一条路径
-m 在创建目录时直接设置其权限 mkdir dir_name -m 777
mv:重命名文件名 / 剪切(移动)文件
mv [目标] [路径] -> 将目标文件移动到指定的路径中
tar:压缩/解压缩
解压缩:tar [参数] [压缩包名] -C [解压至某个路径]
压缩 :tar [参数] [压缩包名] [需要压缩的文件(多个)]
参数 : x -> 解压缩
c -> 压缩
f -> 该参数放在参数段的末尾
v -> 显示过程
z -> 操作tar.gz格式的压缩包
j -> 操作tar.bz格式的压缩包
find:查找文件
find -name "xxxx" 查找关键字段指定的文件
find -name "std*.h" | wc -l 收集检索结果数量
history:查看历史指令
file:查看文件信息
ifconfig:查看网络设备信息
chmod:修改文件权限
chmod [权限参数] [文件名]
echo:输出指定信息
ps:显示进程信息
a:显示当前终端下的所有进程信息,包括其他用户的进程。
u:使用以用户为主的格式输出进程信息。
x:显示当前用户在所有终端下的进程。
-e:显示系统内的所有进程信息。
-l:使用长(long)格式显示进程信息。
-f:使用完整的(full)格式显示进程信息。
top以全屏交互式的界面显示进程排名,及时跟踪包括CPU、内存等 系统资源占用情况,默认情况下每三秒刷新一次,其作用基本类 似于Windows系统中的任务管理器。
man:查看用户指令和系统应用编程接口
man [章节页码] [查询的关键字]
df:显示磁盘使用率
df -Th
【vi文本编辑器】
vi filename -> open target file
安装vim编辑器( sudo apt-get install vim )
进入vi编辑器,会有三种模式
【初始模式(默认)】
i -> 以光标当前所在位置 进入编辑模式
a -> 光标向后进一个字符 进入编辑模式
o -> 光标先向下新起一行 进入编辑模式
x -> 删除光标当前所在的字符
dd -> 删除光标所在的一行字符(删除x行 :x dd)
yy -> 复制一行
p -> 从光标所在的位置开始,粘贴的复制的内容
h -> 光标向左移动 ←
j -> 光标向下移动 ↓
k -> 光标向上移动 ↑
l -> 光标向右移动 →
u -> 退回到上一步
: -> 进入底行命令模式 给出指令
/ -> 进入底行命令模式 查找文本字段
ctrl+F -> 向下翻一页
ctrl+B -> 向上翻一页
【编辑模式】
ESC -> 退回到初始模式
【底行命令模式】
:q -> 退出(没有任何修改操作)
:w -> 写入(保存修改的动作)
:q! -> 不保存退出(有修改操作)
:x -> 保存退出
:set number -> 显示行号
:set ts=4 -> 制表符按4个字符缩进
/xxxx -> 查找字段xxxx [ N向后查找 M向前查找 ]
【vi编辑器行列跳转】
初始模式下跳转:gg 跳转至一行一列
G 跳转至最后一行最后一列
ngg或nG (n代表行号)跳转至n 行
0或| 跳转至当前行行首
$ 跳转至当前行行尾
n|或0n| (0代表数字0,n代表列号,|代表符号|) 跳转至当前行n列
Ctrl+g或Ctrl+G 查看光标所在行占总行数百分比
底行命令模式下跳转:n 按下回车跳转至n行
使用格式vim+n+文件名 使光标进入文件是直接跳转至n行
设置vi环境
用户主目录下的".vimrc"中设置
set number
set ts=4
编写完成之后用source 指令运行
Ubuntu发行的Linux操作系统 安装工具包指令:sudo apt-get install [tools name]
Ubuntu切换终端界面 Ctrl + Alt + F1~F7(F7是桌面化应用)
mount命令 用于挂载Linux系统外的文件
umount命令用于卸除文件系统。
umount可卸除目前挂在Linux目录中的文件系统。
df,linux命令,指的是检查文件系统的磁盘空间占用情况。
格式:df [选项]
说明:df命令可显示所有文件系统对I节点和磁盘块的使用情况。
命令中各个选项的含义:
df -a:显示所有文件系统的磁盘使用情况,包括0块(block)的文件系统。
df -h:以容易理解的格式输出文件系统大小,例如124KB、345MB、46GB。
df -i:显示i节点信息,而不是磁盘块。
df -t:显示各指定类型的文件系统的磁盘空间使用情况。
df -x:列出不是某一指定类型文件系统的磁盘空间使用情况。
df -T:显示文件系统类型。
df 以512字节为单位
df –k 以1024字节为单位.
uname命令是一个用于显示系统信息的工具
基本用法:
在终端中输入"uname"即可显示系统的内核名称。
可以结合不同的参数使用,获取更详细的系统信息。
常见参数:
“-s”:显示操作系统名称。
“-n”:显示网络节点主机名。
“-r”:显示内核版本。
“-m”:显示硬件架构。
“-v”:显示操作系统版本。
“-a”:显示所有可用的系统信息。
mv(英文全拼:move file)命令用来为文件或目录改名、或将文件或目录移入其它位置
env命令用于显示系统中已存在的环境变量,以及在定义的环境中执行指令。该命令只使用”-“作为参数选项时,隐藏了选项”-i”的功能。若没有设置任何选项和参数时,则直接显示当前的环境变量。
-i | 开始一个新的空的环境 |
-u | 从当前环境中删除指定的变量 |
readelf指令
作用:readelf命令用来显示目标文件的头信息,节表信息、符号表信息、重定位表信息等
命令格式
readelf <option> <elffile...>
-a, --all 列出文件的所有信息
-h, --file-header 列出文件头信息
-S, --section-header 列出节表信息
-s, --symbols 列出符号表信息
-r, --relocs 列出重定位表信息
-d, --dynamic 列出动态段信息
-V, --version-info 列出版本段信息
-m,--arch-specific, --disassemble,--hex-dump等选项还可以解析 ELF 文件的内部结 构
调整printk打印权限
echo 4 > /proc/sys/kernel/printk
查看vmware虚拟机版本
可以使用命令行工具vmware-toolbox-cmd来查看vmware虚拟机版本,具体操作如下:
1. 首先,在终端中输入命令:vmware-toolbox-cmd -v
2. 然后,系统会显示vmware虚拟机的版本号
gcc(选项)(参数)
GCC 编译器常用选项
-o 指定输出文件名 gcc a.c -o a
-E 输出预处理后的代码文件 gcc a.c -o a.i -E
-S 输出编译后的汇编代码文件 gcc a.c -o a.s -S
-c 输出链接后的可重定位文件 gcc a.c -o a.o -c
-g 在编译结果中加入调试信息 gcc a.c -o a -g
-I 指定头文件路径 gcc a.c -o a -I./include (大写字母 I)
-L 指定库文件路径 gcc a.c -o a -L./lib(l 小写自母 可以设置 链接库的名字)
-O 指定优化等级 ① gcc a.c -o a -O2
-static 使用静态链接 ② gcc a.c -o a -lxxx -static
-Wall 打开所有的警告
例
gcc a.c -o a -Wall
注:
① 可用的优化等级有 4 个,分别是 O0、O1、O2 和 O3。优化等级越高,编译速度越慢,相对而言程序运行速度越快,调试难度越大。其中 O0 表示关闭所有的优化项目。
② 链接库文件 xxx 时,如果系统中同时存在其对应的静态库和动态库,使用此选项可以使得程序链接静态库,使程序编译之后不依赖于该库文件
%d -> 有符号的十进制整型值
%u -> 无符号的十进制整型值
%ld -> 有符号的十进制长整型值
%lu -> 无符号的十进制长整型值
浮点数的精度
在运行进程指令后加‘&’符号可使进程在后台运行
进程间通信方式:1. 管道
其中消息队列、共享内存、信号量是标准进程间通信
无名管道 pipe()
无名管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘
关系的进程间使用,进程的亲缘关系一般指的是父子关系。无名管道一般用于两个
不同进程之间的通信。
当一个进程创建了一个管道,并调用fork创建自己的一个子进程后,父进程关闭
读管道端,子进程关闭写管道端,这样提供了两个进程之间数据流动的一种方式。
int pipe(int pipefd[2]);
函数功能:创建一个管道
函数参数:指向无名管道文件描述符组的首地址
返 回 值:成功返回0,失败返回-1
ssize_t read (int fd, void *buf, size_t count); 读文件,利用文件描述符
ssize_t write(int fd, const void *buf, size_t count); 写文件,利用文件描述符
===============================================================================
pid_t fork(void);
函数功能:创建一个子进程
函数参数:无参数
返 回 值:成功时,父进程的fork()会返回创建出来的子进程的PID,子进程的fork()会返回0
失败时,父进程的fork()会返回-1,子进程不会被创建
===============================================================================
pid_t getpid(void); pid -> process id 进程获取自己的进程号
pid_t getppid(void); ppid-> parent process id 进程获取自己父进程的进程号
===============================================================================
如果一个管道的所有写端都已经关闭,则读管道将会得到一个文件结束符,read将返回0;
如果一个管道的所有读端都已经关闭,则写管道将会收到一个 SIGPIPE 信号
int mkfifo(const char *pathname, mode_t mode);
函数功能:创建一个管道文件(有名管道)
函数参数:pathname 文件路径名
mode specifies the FIFO's permissions.(8进制常量数值)
返 回 值:成功返回0,失败返回-1
===============================================================================
int open(const char *pathname, int flags);
函数功能:打开文件,获取一个文件描述符
函数参数:pathname 文件路径名
flags 打开方式( O_RDONLY, O_WRONLY, or O_RDWR. )
返 回 值:成功返回一个文件描述符,失败返回-1
使用指令ipcs可以查看当前系统已经拥有的消息队列
消息队列是消息的链表,存放在内核中并由消息队列标识符标识,消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等特点。
消息队列是UNIX下不同进程之间可实现共享资源的一种机制,UNIX允许不同进程将格式化的数据流以消息队列形式发送给任意进程,对消息队列具有操作权限的进程都可以使用msgget完成对消息队列的控制,通过使用消息类型,进程可以按任何顺序读信息,或为消息安排优先级顺序。
优点:可以实现任意进程间的通信,并通过系统调用函数来实现消息发送和接收之间的同步,无需考虑同步问题,方便。
缺点:信息的复制需要额外消耗CPU的时间,不适宜于信息量大或操作频繁的场合。
使用ipcrm指令删除消息队列
===============================================================================
key_t ftok(const char *pathname, int proj_id);
函数功能:生成/获取一个键值
函数参数:pathname which must refer to an existing, accessible file
proj_id 1-255
返 回 值:成功返回键值,失败返回-1
===============================================================================
int msgget(key_t key, int msgflg);
函数功能:获取消息队列标识符
函数参数:key 键值
msgflg 权限
返 回 值:成功返回消息队列标识符(非负整型值),失败返回-1
===============================================================================
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg );
函数功能:向消息队列发送消息
函数参数:msqid 消息队列标识符
msgp 指向消息结构的指针
msgsz 消息大小(不是结构大小)
msgflg 标志位(一般写0)
返 回 值:成功返回0,失败返回-1
===============================================================================
ssize_t msgrcv(int msqid, void *msgp , size_t msgsz, long msgtyp ,int msgflg);
函数功能:从消息队列接收消息
函数参数:msqid 消息队列标识符
msgp 指向消息结构的指针
msgsz 消息大小(不是结构大小)
msgtyp 消息类型(接收的消息类型)
msgflg 标志位(一般写0)
返 回 值:成功返回结构中消息的字节数,失败返回-1
===============================================================================The msgp argument is a pointer to a caller-defined structure of the following general form:
消息结构
struct msgbuf {
long mtype; // 消息类型 大于0的数值(不能更改的成员)
char data[1024]; // 消息数据 允许更改该成员
};
void *shmat(int shmid, const void *shmaddr, int shmflg); 接入共享内存shared memory attach
int shmdt(const void *shmaddr); 断开共享内存 shared memory detach
int shmget(key_t key, size_t size, int shmflg);
函数功能:获取共享内存段标识符
函数参数:key 键值
size 创建共享内存的大小(需要为PAGE_SIZE的倍数)
shmflg IPC_CREAT 创建 | 权限数
返 回 值:成功返回一段共享内存标识符,失败返回-1
共享内存
1、获取共享内存段标识符( 键值 -> 创建/获取shmid );
2、获取共享内存段的首地址;
int sem_init(sem_t *sem,int pshared,unsigned int value);
sem_init() 初始化一个定位在 sem 的匿名信号量。value 参数指定信号量的初始值。 pshared 参数指明信号量是由进程内线程共享,还是由进程之间共享。
如果 pshared 的值为 0,那么信号量将被进程内的线程共享,并且应该放置在这个进 程的所有线程都可见的地址上(如全局变量,或者堆上动态分配的变量)。
如果 pshared 是非零值,那么信号量将在进程之间共享,并且应该定位共享内存区域(见 shm_open(3)、mmap(2) 和 shmget(2))。因为通过 fork(2) 创建的孩子继承其父亲 的内存映射,因此它也可以见到这个信号量。所有可以访问共享内存区域的进程都 可以用 sem_post(3)、sem_wait(3) 等等操作信号量。初始化一个已经初始的信号量 其结果未定义。
返回值:sem_init() 成功时返回 0;错误时,返回 -1,并把 errno 设置为合适的值。
Linux 中线程相关的函数实现在静态库libpthread.a中,pthread库不是Linux系统的默认库因此编译时需要手动链接
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
函数功能:创建一个线程
函数参数:pthread_t 线程的标识符
attr 线程的属性(写NULL,创建时使用线程的默认属性)
start_routine 线程处理函数的函数指针
arg 传递给线程处理函数的参数(如果没有参 数需要传递,写NULL)
返 回 值:成功返回0,失败返回非0.
pthread_t -> long unsigned int
线程结束执行的方式有3种
1、线程将指定函数体中的代码执行完后自行结束
2、线程执行过程中,被同一进程中的其它线程强制终止
3、线程执行过程中,遇到 pthread_exit() 函数结束执行
“被其它线程强制终止”,是指,一个线程可以借助 pthread_cancel() 函数向另一个线程发送“终止执行”的信号,结束该线程。但pthread_cancel() 函数能够成功结束另一个线程,需要目标线程产生了系统调用动作,或是使用pthread_testcancel()设置了测试点。
int pthread_cancel(pthread_t thread);
函数功能:发起请求,取消一个线程的运行
函数参数:thread 目标线程
返 回 值:成功返回0,失败返回非0
pthread_t pthread_self(void);
函数功能:返回在线程处理函数中调用该函数的线程的标识符
函数参数:无参数
返 回 值:线程的标识符
释放线程是指在一个线程结束运行后,释放其使用的资源
<1>使用线程回收函数pthread_join(),阻塞的等待目标线程结束,并回收其资源,另外,线程回收函数还可以获取子线程退出时的返回值;
<2>使用pthread_detach()函数,将一个线程属性设置为“分离状态”,一个被分离的线程,系统会在其结束后自动释放其资源。 (可结合的 | 分离的 ) (joinable | detached)
int pthread_join(pthread_t thread, void **retval);
函数功能:阻塞的等待目标线程结束后,回收其资源(同时也可以获取其返回值)
函数参数:thread 目标线程标识符
retval 指向线程的返回值(如果不关心线程的返回值可以写NULL)
返 回 值:成功返回0,失败返回非0
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//条件变 量初始化
等待条件信号
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步
发送条件信号
int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_broadcast();
条件成立,解除线程的阻塞状态,线程继续执行
int pthread_cond_signal(pthread_cond_t* cond);
int pthread_cond_broadcast(pthread_cond_t* cond);
两个函数都能解除线程的“被阻塞”状态,区别在于:
pthread_cond_signal()
函数至少解除一个线程的“被阻塞”状态,如果等待队列中包含多个线程,优先解除哪个线程将由操作系统的线程调度程序决定;
pthread_cond_broadcast()
函数可以解除等待队列中所有线程的“被阻塞”状态
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //互斥锁 变量初始化
int socket(int domain, int type, int protocol); //和下边函数一样
int socket(int protofamily, int type, int protocol);//返回sockfd //
函数功能:创建套接字
函数参数:domain 协议族
type 套接字类型
protocol 如果已经通过前两个参数确定套接字类型,这个参数可以写0
protofamily:即协议域,又称为协议族(family)。常用的协议族有,AF_INET(IPV4)、 AF_INET6(IPV6)、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE 等等。协议族决定了socket的地址类型,在通信中必须采用对应的地 址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的) 的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、 SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等流式socket ( SOCK_STREAM)是一种面向连接的socket,针对于面向连接的TCP服务 应用。数据报式socket (SOCK DGRAM是一种无连接的socket,对应于 无连接的UDP服务应用。
protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、 IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP 传输协议、UDP 传输协议、STCP传输协议、TIPC传输协议
返 回 值:成功返回初始化好的套接字,失败返回-1
///
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函数功能:将一个地址族中的特定地址赋给socket
函数的三个参数分别为:
sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind() 函数就是将给这个描述字绑定一个名字。
addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地 址结构根据地址创建socket时的地址协议族的不同而不同
addrlen:对用地址长度
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
函数功能:连接至服务端
函数参数:sockfd 客户端套接字
addr 指向协议地址结构的指针
addrlen 协议地址的大小(字节)
返 回 值:
通用地址结构
struct sockaddr{
u_short sa_family; // 地址族 , AF_xxx 2 bytes
char sa_data[14]; // 14 字节协议地址 14bytes
};
Internet 协议地址结构 (需要使用的结构)
struct sockaddr_in{
u_short sin_family; // 地址族,AF_INET, 2 bytes
u_short sin_port; // 端口, 2 bytes
struct in_addr sin_addr; // IPV4 地址, 4 bytes
char sin_zero[8]; // unused作为填充 8 bytes
};
IPv4 地址结构
struct in_addr{
in_addr_t s_addr; //u32 network address
};
===============================================================================
uint32_t htonl(uint32_t hostlong); 主机字节序 转 网络字节序 -> 长整型
uint16_t htons(uint16_t hostshort); -> 短整型
uint32_t ntohl(uint32_t netlong); 网络字节序 转 主机字节序 -> 长整型
uint16_t ntohs(uint16_t netshort); -> 短整型
===============================================================================
in_addr_t inet_addr(const char *cp); 将点分十进制字符串形式的IPv4地址转换成无符 号32位网络字节序数值
/
如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。
int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的 最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将 socket变为被动类型的,等待客户的连接请求。
connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址, 第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务 器的连接。
//
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就向TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); //返回连接connect_fd
参数sockfd
参数sockfd就是上面解释中的监听套接字,这个套接字用来监听一个端口,当有一 个客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接 字关联。当然客户不知道套接字这些细节,它只知道一个地址和一个端口号。
参数addr
这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址,当然这 个地址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如 果对客户的地址不感兴趣,那么可以把这个值设置为NULL。
参数len
如同大家所认为的,它也是结果的参数,用来接受上述addr的结构的大小的,它 指明addr结构所占有的字节个数。同样的,它也可以被设置为NULL。
如果accept成功返回,则服务器与客户已经正确建立连接了,此时服务器通过accept返回的套接字来完成与客户的通信。
注意:
accept默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字。
此时我们需要区分两种套接字,
监听套接字: 监听套接字正如accept的参数sockfd,它是监听套接字,在调用listen 函数之后,是服务器开始调用socket()函数生成的,称为监听socket描述字(监听套接字)
连接套接字:一个套接字会从主动连接的套接字变身为一个监听套接字;而accept 函数返回的是已连接socket描述字(一个连接套接字),它代表着一个网络已经存在的点 点连接。
一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期 内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字, 当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。
自然要问的是:为什么要有两种套接字?原因很简单,如果使用一个描述字的话, 那么它的功能太多,使得使用很不直观,同时在内核确实产生了一个这样的新的描述字。
连接套接字socketfd_new 并没有占用新的端口与客户端通信,依然使用的是与监听套接字socketfd一样的端口号
///
网络I/O操作有下面几组:
///
#include <unistd.h>
int close(int fd);
功能:终止连接
close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。
注意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。
1) SIGHUP:当用户退出 shell 时,由该 shell 启动的所有进程将收到这个信号,默认动作为终 止进程。
2)SIGINT:当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序 发出此信号。默认动作为终止进程。
3)SIGQUIT:当用户按下<ctrl+\>组合键时产生该信号,用户终端向正在运行中的由该终端启 动的程序发出些信号。默认动作为终止进程。
4)SIGILL:CPU 检测到某进程执行了非法指令。默认动作为终止进程并产生 core 文件
5)SIGTRAP:该信号由断点指令或其他 trap 指令产生。默认动作为终止里程 并产生 core 文 件。
6 ) SIGABRT:调用 abort 函数时产生该信号。默认动作为终止进程并产生 core 文件。
7)SIGBUS:非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生 core 文 件。
8)SIGFPE:在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为 0 等所有的算法错误。默认动作为终止进程并产生 core 文件。
9)SIGKILL:无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它 向系统管理员提供了可以杀死任何进程的方法。
10)SIGUSE1:用户定义 的信号。即程序员可以在程序中定义并使用该信号。默认动作为终 止进程。
11)SIGSEGV:指示进程进行了无效内存访问。默认动作为终止进程并产生 e core 文件。
12 ) SIGUSR2 :号 这是另外一个用户自定义信号 , 程序员可以在程序中义 定义 并使 用该信号。 默认动作为终止进程。
13 ) SIGPIPE :n Broken e pipe 向一个没有读端的管道写数据。默认动作为终止进程。
14) SIGALRM:间 定时器超时,超时的时间 由系统调用 m alarm 设置。默认动作为终止进 程。
15 ) SIGTERM :程序结束信号,与 L SIGKILL 不同的是,该信号可以被阻塞和终止。 。 通 常用来要示程序正常退出。 执行 l shell 命令 l Kill 时 ,缺省产生这个信号。默认动 作为终止进程。
16 ) SIGCHLD :子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号。
17 ) SIGCONT :停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为终止进程。
18 ) SIGTTIN :后台进程读终端控制台。默认动作为暂停进程。
19 ) SIGTSTP :停止进程的运行。按下 <ctrl+z> 组合键时发出这个信号。默认动作为暂停 进程。
21 ) SIGTTOU: 该信号类似于 SIGTTIN , 在后台进程要向终端输出数据时发生。默认动作 为暂停进程。
22 ) SIGURG : 套接字上有紧急数据时 , 向当前正在运行的进程发出些信号 , 报告有 紧急数据到达 。 如网络带外数据到达 , 默认动作为忽略该信号。
23 ) SIGXFSZ : 进程执行时间超过了分配给该进程的 U CPU 间 时间 , 系统产生该信 号并发送给该进程。默认动作为终止进程。
24 ) SIGXFSZ :超过文件的最大长度设置。默认动作为终止进程。
25 ) SIGVTALRM :虚拟时钟超时时产生该信号。类似于 SIGALRM ,但是该信号只计算该 进程占用 U CPU 的使用时间。默认动作为终止进程 。
26 ) SGIPROF :类似于 SIGVTALRM ,它不公包括该进程占用 U CPU 时间还包括执行系 统调用时间。默认动作为终止进程。
27 ) SIGWINCH :窗口变化大小时发出。默认动作为忽略该信号。
28 ) SIGIO : 此信号向进程指示发出了一个异步 O IO 事件 。 默认动作为忽略。
29 ) SIGPWR :关机。默认动作为终止进程。
30 ) SIGSYS :无效的系统调用。默认动作为终止进程并产生 e core 文件。
31 ) SIGRTMIN ~ ( 64 ) SIGRTMAX :X LINUX 的实时信号 , 它们没有固定的含义(可 以由用户自定义) ) 。所有的实时信号的默认动作都为终止进程。
函数声明:sighandler_t signal(int signum, sighandler_t handler);
函数功能:捕获信号(注册信号处理函数)
函数参数:signum 信号种类
handler 信号处理函数(函数指针)
SIG_IGN -> signal ignore 忽略信号
SIG_DFL -> siganl default 执行默认动作
Linux中的库文件通常会携带后缀名 (xxx代表库名)
静态库 libxxx.a (archive)
动态库 libxxx.so (shared object)
————————————————————————————————————
静态库:
制作静态库需要目标文件 :ar crv [静态库名称] [目标文件们]
使用静态库时,使用链接参数-l后直接跟上库的名字
,使用编译参数-L指定库文件所在的位置(路径)
————————————————————————————————————
动态库:
制作动态库需要源文件:gcc -fPIC -shared [源文件名称] -o [动态库名称]
使用动态库时,同样需要跟上连接参数,如同静态库一样
但是在运行时,必须找到有效的动态库,否则无法运行
Linux下C使用的大部分库文件都在:/usr/lib/x86_64-linux-gnu下
ar指令:
ar命令用于建立或修改备存文件,或是从备存文件中抽取文件
c 建立备存文件
r 将文件插入备存文件中
v 程序执行时显示详细的信息
Using-directives:指示使用某命名空间,这也是最常见的使用方法
语法:
attr(optional) using namespace nested-name-specifier(optional) namespace-name;
1
attr: 应用到using的任意数量的属性,
nested-name-specifier: 名称和作用域解析运算符::的序列,以作用域解析运算符结尾。 单个::引用全局命名空间。
namespace-name: 命名空间的名称。查找此名称时,只关注名称空间声明,也就是说只 查找命名空间的名字。
using-declaration: 使用using声明命名空间中的名称
将在别处定义的名称引入此using-declaration出现的声明区域中。这个需要注意定义冲突的问题
Using-declaration:声明类中的名称
C语言
预处理指令
C语言中的预处理指令是一种特殊的指令,它们在程序编译之前被处理,用于定义常量、宏、条件编译等
https://baijiahao.baidu.com/s?id=1765660278528160019&wfr=spider&for=pc
1.#define指令
#define指令用于定义常量和宏
2.#include指令
#include指令用于包含头文件
3.#ifdef和#ifndef指令
#ifdef和#ifndef指令用于条件编译
4.#if和#elif指令
#if和#elif指令也用于条件编译
5. #undef指令
#undef指令用于取消已定义的宏
static的特点/作用
● static的本质是延长变量或函数的生命周期,同时限制其作用域。
● static声明的全局变量、函数,仅当前文件内可用,其他文件不能引用。
● static在函数内部声明的内部静态变量,只需初始化一次。
● 而且变量存储在全局数据段(静态存储区)中,而不是栈中,其生命周期持续到程序退出
● 其他文件中可以定义相同名字的函数,不会发生冲突
● 静态函数不能被其他文件所用。
预处理 gcc -E hello.c -o helle.i
编译 gcc -S hello.i -o hello.s
汇编 gcc -c hello.s -o hello.o
链接 gcc hello.o -o hello
F2 跳转到函数定义(和Ctrl+鼠标左键一样的效果)
Shift+F2 声明和定义之间切换
F4 头文件和源文件之间切换
Ctrl+B 编译工程
Ctrl+R 运行工程
Ctrl+I 自动对齐
Ctrl+/ 注释行,取消注释行
Ctrl+L 跳到某一行
Ctrl+F 查找替换当前选中的内容,按下Ctrl+F,会列出所有和你选的内容一样的语句
F5 开始调试
Shift+F5 停止调试
F9 设置和取消断点
F10 单步前进
F11 单步进入函数
Shift + F11 单步跳出函数
Mount -t nfs xxx.xxx.xxx.xxx:/xxxxxxxx/nfsroot /mnt -o nolock,nfsvers=3,vers=3
挂载U盘
mount -t vfat /dev/sda1 /mnt/
挂载nfs网络文件系统
<1>为Ubuntu安装NFS服务: sudo apt-get install nfs-kernel-server
sudo apt-get install nfs-common
(安装完成后检查nfs服务是否启用 /etc/init.d/nfs-kernel-server status)
<2>指定一个目录作为共享目录(可以新建一个 例如 mkdir /home/meson/nfsroot),将其权限设置为最宽松(777)
<3>对该目录进行配置,使其能够挂载在开发板
a.使用超级用户权限修改"/etc/exports"文件,在文件末尾添加以下内容
格式: <nfs共享目录绝对路径> *(rw,sync,no_root_squash)
举例: /home/meson/nfsroot *(rw,sync,no_root_squash)
b.保存退出
c.输入指令"sync"进行同步
d.重启nfs服务 "/etc/init.d/nfs-kernel-server restart" (每次修改exports文件之后都最好重启nfs服务)
e.进入开发板进行挂载 ( ARM-linux-IP 和 Ubuntu-linux-IP 需互通 )
开发板中输入指令:mount -t nfs <ip>:<dir_1> <dir_2> -o nolock
指令功能:将<ip>指定的系统中的dir_1路径中的内容挂载到本地的dir_2路径下
举例:"mount -t nfs 192.168.XXX.XXX:/home/meson/nfsroot /mnt -o nolock"
无报错即为成功,随后可以在开发板的/mnt中访问虚拟机中的/home/meson/nfsroot目录,重启失效,需重新挂载
IMX6ULL开发板启动模式:
USB 串行启动(烧写模式): 0 x x x x x x x
EMMC 启动 (正常使用): 1 0 1 0 0 1 1 0
更改启动模式之前,开发板需要断电
烧写文件系统:
使用工具mfgtools_for_6ULL
在该目录下"mfgtools_for_6ULL\Profiles\Linux\OS Firmware\files\linux\"
需要有 BootLoader,内核文件,设备树文件,文件系统镜像文件
BootLoader -> 资料\6.EMMC核心板资料\EMMC核心板Uboot\u-boot.imx
文件系统镜像文件 -> 资料\2.文件系统镜像\02_Buildroot文件系统/rootfs.tar.bz2
内核文件 -> 资料\6.EMMC核心板资料\EMMC核心板Linux内核镜像\zImage
设备树文件 -> 资料\6.EMMC核心板资料\EMMC核心板Linux内核镜像\*.dtb
将4类文件全部放入烧工具"mfgtools_for_6ULL\Profiles\Linux\OS Firmware\files\linux\"目录下后,可以正常使用MfgTool2.exe,运行后连接开发板即可烧写
使用串口线连接计算机和开发板
烧写工具工作后
"No Device Connected" -> 无设备连接
"符合 HID 标准的供应商定义设备" -> 设备连接成功 ("USB Mass Storage Device")
连接成功后,点击"Start"按钮,烧写工具会默认将Uboot,内核,设备树文件,文件系统镜像烧写
全部完成后,确认烧写无误,点击"Stop",然后点击"Exit"退出
=======================================================================================
访问开发板终端:
1.为计算机安装CH340串口驱动
2.确认连接的串口编号(可以在设备管理器中查看 COM??)
3.使用终端连接工具进行连接
使用串口协议 端口 :COM??
波特率 :115200
数据位 :8
奇偶校验:无
停止位 :1
流控 :无
buildroot文件系统默认的用户名:root
密码:topeet
=======================================================================================
调整屏幕参数(手册章节6.2(使用资料"其他"中的内核与设备树文件以兼容屏幕))
进入开发板uboot
setenv lcdtype 1024x600
saveenv
reset
挂载网络文件系统(NFS)
1.使"开发板中Linux-OS的IP"和"虚拟机Linux-OS的IP"以及"计算机主机IP"处于同一网段(可以互相ping通)
<1>使用网线连接开发板和计算机,配置双方的IP地址
windows中以太网IP直接手动配置即可
开发板中配置IP 修改/etc/network/interfaces
auto eth0
iface eth0 inet static
address 192.168.xxx.xxx
netmask 255.255.255.0
gateway 192.168.xxx.1
重启网络服务 - /etc/init.d/S40network restart (如果不生效,重启系统)
(windows IP 和 开发板的IP 互相ping测试)
<2>为虚拟机添加一个网络适配器,给Linux操作系统新增一个网络设备 - 桥接至主机的以太网
手动给新的虚拟网络配置IP
重启网络服务使其生效 - /etc/init.d/networking restart (如果不生效,重启系统)
安装交叉编译器
1.将交叉编译工具压缩包解压;
2.找到交叉编译工具二进制文件的路径,并将该路径中的二进制文件添加为系统指令
运行指令arm-linux-gnueabihf-gcc -v 查看交叉编译工具版本号
自行了解gnueabi 和 gnueabihf的区别
以libjpeg-turbo为例
如何使用第三方软件包
1.配置 -> 指定"目标平台"和"交叉编译工具"以及"安装的位置"
2.编译 -> 根据源码编译出目标文件
3.编译安装 -> 生成头文件、库文件以及其他相关文件
[配置]
--prefix -> 指定安装的路径
--host -> 运行平台
CC -> 交叉编译工具链
指令:
进入源码包顶层目录
mkdir tmp_install
./configure --prefix=$PWD/tmp_install/ --host=arm-linux-gnueabihf CC=arm-linux-gnueabihf-gcc
[编译]
make 或 make -j8
[编译安装]
make install
默认情况下的Ubuntu和Windows之间是无法实现跨系统复制粘贴的,这样的话不利于开发。
解决方法
打开Ubuntu命令终端
卸载已有的工具:sudo apt-get autoremove open-vm-tools
安装open-vm-tools-desktop:sudo apt-get install open-vm-tools-desktop
重启Ubuntu系统:reboot
在往source insight中添加内核库文件时需要在options -> preferences -> typing -> pre file type -> default 中添加 *.c;*.h;*.S;Kconfig;Makefile;*.lds;*.dts;*.dtsi;*.dtb
module_init(); //驱动入口函数
module_exit(); //驱动出口函数
MODULE_LICENSE(“GPL”); //声明模块具有开源许可证
file operations; //文件操作合集
Struct miscdevice //表示杂项设备结构体
int misc_register(struct miscdevice *misc) //杂项设备注册函数
int misc_deregister(struct miscdevice *misc) //杂项设备注销函数
#define MAJOR(dev) 在dev_t里获取我们的主设备号
#define MINOR(dev) 在dev_t里获取我们的次设备号
#define MKDEV(ma,mi) 将我们的主设备号和此设备号组成dev_t类型
register_chrdev_region(); //静态分配设备号
alloc_chrdev_region(); //动态分配设备号
unregister_chrdev_region(); //注销设备号
cdev_init(); //初始化cdev成员,并建立cdev和file_operation之间的联系
cdev_add();//向Linux系统添加字符型设备
cdev_del();//删除字符型设备
class_create();//创建类
class_destroy();//删除类
device_create();//自动创建设备,在/dev 下生成设备节点
device_destroy();//注销设备
Ioremap //将物理地址转换成虚拟地址
Iounmap//释放掉ioremap映射的地址
struct timer_list name; //定时器相关参数结构体,定义在#include <linux/timer.h>里
init_timer(struct timer_list*): 定时器初始化函数;
b、add_timer(struct timer_list*): 往系统添加定时器;
c、mod_timer(struct timer_list *, unsigned long jiffier_timerout):修改定时器的超时时间为jiffies_timerout;
d、timer_pending(struct timer_list *):定时器状态查询,如果在系统的定时器列表中则返回1,否则返回0;
e、del_timer(struct timer_list*): 删除定时器。
platform_device_register();//注册platform设备
platform_device_unregister();//注销platform设备
struct platform_device name{}; //描述platform设备结构体
struct resource name{};//描述platform资源结构体
platform_driver_register();//注册platform驱动
platform_driver_unregister(); //注销platform驱动
struct platform_driver name{}; //描述platform驱动结构体
platform_get_resource();//从platform_device.c中获取硬件资源
设备树代替platform_device
如果需要编译设备树.dtb文件则需要安装一下东西:
apt-get install device-tree-compiler
查找节点的 of 函数
设备都是以节点的形式“挂”到设备树上的,因此要想获取这个设备的其他属性信息,必须先获取到这个 设备的节点。Linux 内核使用 device_node 结构体来描述一个节点。
节点的属性信息里面保存了驱动所需要的内容,因此对于属性值的提取非常重要,Linux 内核中使用结 构体 property 表示属性
这两个结构体都定义在文件 include/linux/of.h 中,获得设备树文件节点里面资源的步骤:
步骤一:查找我们要找的节点。
步骤二:获取我们需要的属性值。
与查找节点有关的 OF 函数有 3 个,我们依次来看一下。
1.of_find_node_by_path 函数
函数 inline struct device_node *of_find_node_by_path(const char *path)
path 带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这 个 节点的全路径。
返回值 成功:返回找到的节点,失败返回 NULL。
功能 通过节点名字查找指定的节点
2 of_get_parent 函数
函数 struct device_node *of_get_parent(const struct device_node *node)
node 要查找的父节点的节点。
返回值 成功:找到的节点,如果为 NULL 表示查找失败。
功能 用于获取指定节点的父节点(如果有父节点的话)。
3 of_get_next_child 函数
函数 struct device_node *of_get_next_child(const struct device_node *node struct device_node *prev)
node 父节点
prev 前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。可以设置为
NULL ,表示从第一个子节点开始。
返回值 成功:找到的下一个子节点。如果为 NULL 表示查找失败。
功能 of_get_next_child 函数用迭代的查找子节点
5.2、获取属性值的of函数
与获取属性值的 OF 函数有 5 个,我们依次来看一下。
1 of_find_property 函数
函数 property *of_find_property(const struct device_node *np,const char *name,int *lenp)
np 设备节点
name 属性名字
enp 属性值的字节数
返回值 找到的属性
功能 of_find_property 函数用于查找指定的属性
2 of_property_read_u8 函数
of_property_read_u16 函数
of_property_read_u32 函数
of_property_read_u64 函数
函数
int of_property_read_u8(const struct device_node *np,const char *propname,u8 *out_value)
int of_property_read_u16(const struct device_node *np,const char *propname,u16 *out_value) int of_property_read_u32(const struct device_node *np,const char *propname,u32 *out_value) int of_property_read_u64(const struct device_node *np,const char *propname,u64 *out_value)
np 设备节点
proname 要读取的属性名字
out_value 读取到的数组值
返回值 0 ,读取成功,负值,读取失败
功能 有些属性只有一个整型值,这四个函数就是用于读取这种只有一个整型值的属性,分别用于读取 u8 、u16 、u32 和 u64 类型属性值
3 of_property_read_u8_array 函数
of_property_read_u16_array 函数
of_property_read_u32_array 函数
of_property_read_u64_array 函数
函数
int of_property_read_u8_array(const struct device_node *np,const char *propname,u8 *out_values,size_t sz)
int of_property_read_u16_array(const struct device_node *np,const char *propname,u16 *out_values,size_t sz)
int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_values,size_t sz)
int of_property_read_u64_array(const struct device_node *np,const char *propname,u64 *out_values,size t sz)
np 设备节点
proname 要读取的属性名字
out_value 读取到的数组值,分别为 u8 、u16 、u32 和 u64。
sz 要读取的数组元素数量
返回值 0 ,读取成功,负值,读取失败
功能 这 4 个函数分别是读取属性中 u8、u16、u32 和 u64 类型的数组数据, 比如大多数的 reg 属 性都是数组数据,可以使用这 4 个函数一次读取出 reg 属性中的所有数据
4 of_property_read_string 函数
函数 int of_property_read_string(struct device_node *np,const char *propname,const char **out_string)
np 设备节点
proname 要读取的属性名字
out_string 读取到的字符串值
返回值 0 ,读取成功,负值,读取失败
功能 of_property_read_string 函数用于读取属性中字符串值
5 of_iomap 函数
函数 void __iomem *of_iomap(struct device_node *np,int index)
np 设备节点
index reg 属性中要完成内存映射的段,如果 reg 属性只有一段的话 index 就设置 0。
返回值 经过内存映射后的虚拟内存首地址,如果为 NULL 的话表示内存映射失败
功能 of_iomap 函数用于直接内存映射,以前我们会通过 ioremap 函数来完成物理地 址到虚拟地址 的映射。
3.2、GPIO函数
经过 GPIO 子系统,我们可以通过如下的方式来配置 GPIO:
1 gpio_request 函数
函数 int gpio_request(unsigned gpio, const char *label)
gpio 要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信 息,此函数会返回这个 GPIO 的标号。
Label 给 gpio 设置个名字
返回值 0 , 申请成功;其他值, 申请失败
功能 gpio_request 函数用于申请一个 GPIO 管脚
2 gpio_free 函数
函数 void gpio_free(unsigned gpio)
gpio 要释放的 gpio 标号
返回值 无
功能 如果不使用某个 GPIO 了,那么就可以调用 gpio_free 函数进行释放。
3 gpio_direction_input 函数
函数 int gpio_direction_input(unsigned gpio)
gpio 要设置为输入的 GPIO 标号
返回值 设置成功返回0;设置失败返回负值
功能 此函数用于设置某个 GPIO 为输入
4 gpio_direction_output 函数
函数 int gpio_direction_output(unsigned gpio, int value)
gpio 要设置为输出的 GPIO 标号
value GPIO 默认输出值
返回值 设置成功返回0;设置失败返回负值
功能 此函数用于设置某个 GPIO 为输出,并且设置默认输出值
5 gpio_get_value 函数
函数 int __gpio_get_value(unsigned gpio)
gpio 要获取的 GPIO 标号
返回值 成功返回 GPIO 值,失败返回负值
功能 此函数用于获取某个 GPIO 的值(0 或 1)
6 gpio_set_value 函数
函数 void __gpio_set_value(unsigned gpio, int value)
gpio 要设置的 GPIO 标号
value 要设置的值
返回值 无
功能 此函数用于设置某个 GPIO 的值
7 of_get_named_gpio 函数
函数 int of_get_named_gpio(struct device_node *np,const char *propname,int index)
np 设备节点
propname 包含要获取 GPIO 信息的属性名
index 因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO 的编号, 如果只有一个 GPIO 信息的话此参数为 0
返回值 成功返回到的 GPIO 编号,失败返回一个负数
功能 此函数获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要 使用 GPIO 编号,此函数会将设备树中类似<&gpio1 3 GPIO_ACTIVE_ LOW>的属 性信息转换为对 应的 GPIO 编号
ioctrl:http://t.csdn.cn/1ktTy
dir(访问方向(读/写)) size(数据类型大小(int/char/。。。))
type(“幻数”或“魔数”(“A/B/C/。。”)) nr(编号(1/2/3/。。。))
如:定义命令
#define _IO(‘A’,0) //不读不写 没有size type:A nr:0
#define _IOR(‘A’,1,int) //只读 size:int :4 type:A nr:1
#define _IOW(‘B’,0,int) //只写 size:int :4 type:B nr:0
#define _IOWR(’B‘,1,int) //读写 size:int :4 type:B nr:1
如:解析命令
_IOC_XXX(要解析的命令宏定义) 解析DIR 自动返回整型值(从二进制转换十进制)
XXX:(可以是DIR/SIZE/TYPE/NR)
printf("30-31 is %d \n", _IOC_DIR(CMD_TEST0));
printf("30-31 is %d \n", _IOC_DIR(CMD_TEST3));
printf("7-15 is %c \n", _IOC_TYPE(CMD_TEST0));
printf("7-15 is %c \n", _IOC_TYPE(CMD_TEST1));
printf("0-7 is %d \n", _IOC_NR(CMD_TEST2));
/*
第四个分区:30-31 代表读写的方向。(DIR)
00:表示用户程序和驱动程序没有数据传递
10:表示用户程序从驱动里面读数据
01:表示用户程序向驱动里面写数据
11:先写数据到驱动里面然后在从驱动里面把数据读出来。
*/
函数
unsigned int irq_of_parse_and_map(struct device_node *dev,int index)
dev 设备节点
index 索引号,interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息。
返回值 中断号
功能 通过 irq_of_parse_and_map 函数从 interupts 属性中提取到对应的设备号
函数
int gpio_to_irq(unsigned int gpio)
gpio 要获取的 GPIO 编号
返回值 GPIO 对应的中断号
功能 获取 GPIO 对应的中断号
函数
int request_irq( unsigned int irq,irq_handler_t handler,unsigned long flags,const char
*name,void *dev)
irq 要申请中断的中断号
Handler 中断处理函数,当中断发生以后就会执行此中断处理函数。
Flags 中断标志
Name 中断名字,设置以后可以在开发板/proc/interrupts 文件中看到对应的中断名字
Dev 如果将 flags 设置为 IRQF_SHARED 的话, dev 用来区分不同的中断,一般情况下 将dev 设 置 为设备结构体,dev 会传递给中断处理函数 irq_handler_t 的第二个 参数。
返回值 中断申请成功返回 0 ,其他负值则中断申请失败,如果返回- EBUSY 的话表示中断 已经被申请了。
函数
void free_irq(unsigned int irq,void *dev)
irq 要释放的中断
dev 如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断 只有在释放最后中断处理函数的时候才会被禁止掉。
返回值 无
功能 释放掉相应的中断
函数
irqreturn_t (*irq_handler_t) (int, void *)
第一个参数 要中断处理函数要相应的中断号
第二个参数 是一个指向 void 的指针,也就是个通用指针,需要与 request_irq 函数的 dev 参数保持 一致。用于区分共享中断的不同设备,dev 也可以指向设备数据结 构。
返回值 中断处理函数的返回值为 irqreturn_t 类型
irqreturn_t 类型定义如下所示:
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
};
typedef enum irqreturn irqreturn_t;
可以看出 irqreturn_t 是个枚举类型,一共有三种返回值。一般中断服务函数返回值使用如下形式
return IRQ_RETVAL(IRQ_HANDLED)
中断使能和禁止
void enable_irq(unsigned int irq)
void disable_irq(unsigned int irq)
enable_irq 和 disable_irq 用于使能和禁止指定的中断,irq 就是要禁止的中断号。disable_irq 函数要等到当前正在执行的中断处理函数执行完才返回,因此使用者需要保证不会产生新的中断,并且确保所有已经开始执行的中断处理程序已经全部退出。在这种情况下,可以使用另外一个中断禁止函数:
void disable_irq_nosync(unsigned int irq)
disable_irq_nosync 函数调用以后立即返回,不会等待当前中断处理程序执行完毕。
中断使用方法
1、通过 io 口获取中断号
步骤:
1.获取 gpio 口标号
2、通过 GPIO 口标号获取中断号
3、申请中断
4、编写中断处理函数
5、释放
函数
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long),unsigned long data);
T 要初始化的 tasklet
func tasklet 的处理函数
data 要传递给 func 函数的参数
返回值 没有返回值。
功能 动态初始化 tasklet
也可以使用宏 DECLARE_TASKLET 一次性完成 tasklet 的定义和初始化,DECLARE_TASKLET
定义在 include/linux/interrupt.h 文件中,定义如下:
DECLARE_TASKLET(name, func, data)
其 中 name 为要定义的 tasklet 名字,这个名字就是一个 tasklet_struct 类型的时候变量,func就是 tasklet 的处理函数,data 是传递给 func 函数的参数。
函数
void tasklet_schedule(struct tasklet_struct *t)
t 要调度的 tasklet ,也就是 DECLARE_TASKLET 宏里面的 name。
返回值 没有返回值
功能 调度 tasklet
函数
tasklet_kill(struct tasklet_struct *t)
T 要删除的 tasklet
功能 删除一个 tasklet
注意 这个函数会等待 tasklet 执行完毕,然后再将它移除。该函数可能会引起休眠,所 以要禁止在中断上下文中使用。
2、通过中断属性获取中断号
步骤:
1、在设备树中添加节点中断属性
2、通过函数 irq_of_parse_and_map 去获取中断号
3、申请中断
4、编写中断处理函数
5、释放
两者的差异在第二步
在开发板 /proc/interrupts下可查看中断名字
问题描述:在使用IMX6进行开发时发现:即使IO口设置为输出状态,设置其电平为高,硬件上测量电平也是对的,但是gpio_get_value获取的状态一直是0。
解决方法:配置SION位,使IO口在输出状态下可以正确获取状态。
这里以DTS配置为例:
&iomux {
pinctrl-name = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
...
/* 第29位配置为4,即表示设置SION位 */
MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x40017059
...
};
...
};
1、应用层通过标准的 open 调用进行 IIC 设备的操作;
2、每一个 i2c_client 对应一个实际硬件上的 IIC device(比如 EEPROM)
3、每一个 i2c_driver 描述一种 IIC 设备的驱动
4、i2c_dev 是注册的字符类型的设备
5、i2c_core 是 IIC 核心层,提供了总线、驱动、通信方式等的注册和钩子函数的设置
6、i2c_adapter 用于描述一个 SoC 的一个 IIC 控制器
7、i2c_algorithm 用于底层对接实际的控制器,产生 IIC 硬件波形的函数
8、下面对接的就是实际的 SoC 的 IIC 控制器(寄存器)和硬件
下载
sudo apt-get install xmlto
进入内核编译器
sudo make mandocs
make installmandocs
#define MX6UL_PAD_UART4_TX_DATA__GPIO1_IO28 0x00B4 0x0340 0x0000 0x5 0x0
此处的宏定义可在头文件中查找
格式<mux_reg conf_reg input_reg mux_val mux_val>
mux_reg 复用寄存器的偏移地址 0x00B4
conf_reg 寄存器偏移地址 0x0340
input_reg 寄存器偏移地址 0x0000
mux_reg 寄存器值 0x5
input_reg 寄存器值 0x0
其他知识
构造函数也可以被重载
逻辑真时非零值
回调函数:被作为参数传递的函数
递归函数:自己调用自己,可以时直接调用、也可以时间接调用
再C语言中没有引用只有指针,C语言函数名就是函数指针
静态/动态编译:https://blog.csdn.net/weixin_38907560/article/details/81478981
动态/静态库:https://blog.csdn.net/weixin_45134977/article/details/126221222
1. 安装VMware tools,winspc
2. 安装语言支持包
3. 设置root用户密码
sudo passwd root
4. 更新软件源为最新版
sudo apt-get update
5. 安装vim编辑器
sudo apt-get install vim
6. 安装32bit兼容包
sudo apt-get install lib32ncurses5
sudo apt-get install lib32stdc++6
sudo apt-get install lib32z1
7. 开启ssh服务
sudo apt-get install openssh-server //安装ssh服务端
sudo service ssh start //启动ssh
sudo service ssh status //查询ssh状态
8. 开启tftp服务(后面用于网络升级内核)
sudo apt-get install tftp-hpa tftpd-hpa //安装tftp
mkdir -p /home/suifeng/tftpboot //suifeng目录可变
chmod 777 tftpboot //设置权限
修改文件sudo vim /etc/default/tftpd-hpa,添加以下内容:
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/suifeng/tftpboot"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="-l -c -s"
sudo service tftpd-hpa restart //重启tftp服务
9. 开启nfs服务(后面用于挂载网络文件系统)
mkdir -p /home/suifeng/nfsroot
sudo apt install nfs-kernel-server //安装nfs服务
sudo vim /etc/exports //修改该文件
/home/suifeng/nfsroot *(rw,sync,no_root_squash) //添加该内容
sudo /etc/init.d/nfs-kernel-server restart //重启nfs服务
插上 u 盘后,使用 df 命令查看当前系统上文件系统挂载情况
可以看出我们的 u 盘没有挂载
接下来我们就可以进行配置 u 盘自动识别挂载
操作方法如下:
1. 在 在/etc/udev/rules 目录下新建 USB-add.rules 和 USB-rm.rules, , 负责
设备监测。
ACTION!="add",GOTO="farsight"
KERNEL=="sd[a-z][0-9]",RUN+="/opt/mountusb.sh %k"
KERNEL=="sd[a-z]",RUN+="/opt/mountusb.sh %k"
LABEL="farsight"
这个文件中ACTION后是说明是什么事件,KERNEL后是说明是什么设备比如sda1,mmcblk0p1
等,RUN 这个设备插入后去执行哪个程序%k 是传入这个程序的参数,这里%k=KERNEL 的值
也就是 sda1 等
2、 、在 在/etc/udev/rules 目录下新建 USB-rm.rules, , 负责设备状态识别跳
转
ACTION !="remove",GOTO="farsight"
SUBSYSTEM!="block",GOTO="farsight"
KERNEL=="sd[a-z][0-9]",RUN+="/opt/umountusb.sh"
LABEL="farsight"
3. 在/opt 下建立执行脚本 mountusb.sh,负责挂载。并给予
可执行权限
#!/bin/sh
mount -t vfat /dev/$1 /opt/usb
sync
4、在/opt 下建立执行脚本 umountusb.sh,负责卸载。并给
予可执行权限
#!/bin/sh
sync
umount /opt/usb
5.完成操作,可实现自动挂载。使用 df 命令查看。
接下来直接访问挂载点/opt/usb 即可
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。