1. 说明
硬件环境:
迅为imx6q
开发板, ARM Cortex-A9的处理器
7.0寸电容触摸屏
软件环境:
使用的是迅为官方提供的u-boot.imx , uImage, Qt5.7的rootfs根文件系统。
交叉编译工具链:arm-linux-gcc-4.8.3
问题:
在使用迅为提供的Qt5.7的系统时,发现板子的动态库不支持c++ 的东西,只支持Qt,于是就开始了找原因的过程。
下面是在迅为提供的Qt5.7的系统上执行c++的报错问题:
找不到动态库 GLIBCXX_3.4.11
通过执行 strings /lib/libstdc++.so.6 |grep GLIBCXX 后:
发现确实找不到 GLIBCXX_3.4.11。
解决方法:
从网上下载比较新的 libstdc++.so.6.0.17 库,
然后执行如下步骤:
将 libstdc++.so.6.0.17拷贝到开发板 /lib/ 目录下
删除原来的libstdc++.so.6, 执行 rm -rf libstdc++.so.6
建立新的指向libstdc++.so.6.0.17的软链接, 即:
ln -s libstdc++.so.6.0.17 libstdc++.so.6
至此,板子的环境问题就全部解决
2. 板子测试一个c++11的多线程的Demo
#include
#include
#include
#include
#include
//#include
#include
void led_proc1();
void led_proc2();
#define MAX 100
int main(void)
{
std::thread t1(led_proc1);
std::thread t2(led_proc2);
t1.join();
t2.join();
//std::cout << "thread john finish" << std::endl;
printf("thread john finish");
return 0;
}
void led_proc1()
{
int fd,LedOnOfftimes;
char gpio[MAX],cmd[MAX];
char *leds = "/dev/leds_ctl";
LedOnOffTimes = MAX;
printf("leds light on and off 5 times rn");
if((fd = open(leds, O_RDWR|O_NOCTTY|O_NDELAY))<0)
printf("open %s failedn",leds);
else
{
printf("open %s successrn",leds);
while(LedOnOffTimes--)
{
printf("ioctl leds %d timesn",LedOnOffTimes);
ioctl(fd,0,0); //parameter 2 is cmd ,cmd = 1 leds on
//ioctl(fd,0,1);
sleep(1);
ioctl(fd,1,0);
//ioctl(fd,1,1);
sleep(1);
}
}
close(fd);
}
void led_proc2()
{
int temp = 100;
while(1)
{
//std::cout << "temp= " << temp-- << std::endl;
printf("temp= %drn",temp--);
sleep(2);
}
}
交叉编译命令如下:(注意,编译c++的编译器版本务必在4.8以上才支持)
arm-none-linux-gnueabi-g++ -lpthread led_cpp_thread.cpp -o ledThread3 -std=c++11
编译注意事项:
因为用到了多线程,编译时要加上 -lpthread 命令
因为用到c++11, 编译时要用到 -std=c++11 命令
3. 板子上测试posix标准的多线程代码
从《Unix环境高级编程》的第11章 线程 章节里,可以知道如何使用posix接口创建一个多线程。
3.1 创建线程:
包含头文件: #include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_rtn) (void*),void *arg);
*thread: 线程id (指向线程标识符的指针)
*attr: 线程属性(通常为空)
*start_rtn: 线程要执行的函数(线程运行函数的起始地址)
*arg: start_rtn的参数
注意事项:
返回值为0才算成功,其它表示错误
编译时需要加上 -lpthread库
线程函数都是 void* 的返回类型
示例如下:
pthread_t tid_1; //存放线程ID
int err = pthread_create(&tid_1, NULL, led_proc_1, NULL);
if(err !=0){
printf("can't create thread led_proc1");
}
pthread_join(tid_1, NULL); //线程阻塞,防止main的进程退出
3.2 测试示例Demo
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void* led_proc1(void *args);
void* led_proc2(void *args);
#define MAX 100
pthread_t ntid1,ntid2;
void print_tids(const char* s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("%s pid %u tid %u (0x%x)n", s, (unsigned int)pid, (unsigned int)tid, (unsigned int)tid);
}
int main(void)
{
int err = pthread_create(&ntid1, NULL, led_proc1, NULL);
if(err !=0)
{
//err_quit("can't create thread %sn", strerror(err));
printf("can't create thread led_proc1");
}
err = pthread_create(&ntid2, NULL, led_proc2, NULL);
if(err !=0)
{
//err_quit("can't create thread %sn", strerror(err));
printf("can't create thread led_proc2");
}
print_tids("main thread: ");
pthread_join(ntid1, NULL);
pthread_join(ntid2, NULL);
printf("exiting...");
sleep(2);
exit(0);
}
void* led_proc1(void *args)
{
int fd,LedOnOffTimes;
char *leds = "/dev/leds_ctl";
LedOnOffTimes = MAX;
printf("leds light on and off 5 times rn");
if((fd = open(leds, O_RDWR|O_NOCTTY|O_NDELAY))<0)
{
printf("open %s failedn",leds);
}
else
{
printf("open %s successrn",leds);
while(LedOnOffTimes--)
{
ioctl(fd,0,0); //parameter 2 is cmd ,cmd = 1 leds on
//ioctl(fd,0,1);
sleep(1);
ioctl(fd,1,0);
//ioctl(fd,1,1);
sleep(1);
}
}
close(fd);
}
void* led_proc2(void *args)
{
int temp = 1000;
while(1)
{
printf("temp= %drn",temp--);
sleep(2);
}
}
4. 创建进程以及进程间通讯
4.1 fork创建父子进程
从《Unix环境高级编程》里fork介绍如下:
下面是一个使用fork() 函数创建进程的示例:
#include
#include
#include
void father_process();
void son_process();
void main()
{
pid_t pid;
pid = fork();
if(pid>0) //pid>0的是父进程
{
printf("this is father processn");
father_process();
}
else if(pid==0) //pid=0的是子进程
{
printf("this is son processn");
son_process();
}
}
void father_process() //父进程
{
int count = 0;
for (;;)
{
printf("father: %dn", count++);
sleep(1);
}
}
void son_process() //子进程
{
int count = 0;
for (;;)
{
printf("son: %dn", count++);
sleep(1);
}
}
看这个程序的时候,头脑中必须首先了解一个概念:在语句pid = fork()之前,只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了,这两个进程的代码部分完全相同,将要执行的下一条语句都是if(pid>0) ……。
这2个进程中,原先就存在的那个被称作“父进程”,新出现的那个被称作“子进程”。父子进程的区别除了进程标志符(process ID)不同外,变量pid的值也不相同,pid存放的是fork的返回值。fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
在父进程中,fork返回新创建子进程的进程ID,进程ID>0;
在子进程中,fork返回0;
如果出现错误,fork返回一个负值;
4.2 进程间通讯之管道
管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。
其特征如下:
1. 其本质是一个伪文件(实为内核缓冲区)
2. 由两个文件描述符引用,一个表示读端,一个表示写端。
3. 规定数据从管道的写端流入管道,从读端流出。
管道的局限性:
① 数据自己读不能自己写。
② 数据一旦被读走,便不在管道中存在,不可反复读取。
③ 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
④ 只能在有公共祖先的进程间使用管道。
创建管道:
简单示例如下:
#include
int fd[2]; //fd[0] 是输入端 fd[1] 是输出端
if(pipe(fd)== -1)
{
perror("pipe err");
}
下面是一段父子进程间使用管道进行数据读写的Demo
#include
#include
#include
#include
#include
void sys_error(const char *str)
{
perror(str);
exit(1);
}
void main()
{
char buf[100];
char *p = "test for pipen";
int fd[2]; //fd[0] 是标准输入 fd[1] 是标准输出
if(pipe(fd)==-1) //创建管道
{
sys_error("pipe");
}
pid_t pid = fork();
if(pid > 0)
{
printf("this is father processn");
close(fd[1]); //读之前先关闭fd[1],防止子进程没法打开这个fd[1]
int len = read(fd[0], buf, sizeof(buf)); //阻塞型I/O
write(STDOUT_FILENO, buf, len); //STDOUT_FILENO 表示标准输出描述符
close(fd[0]);
//father_process();
}
else if(pid == 0)
{
printf("this is son processn");
close(fd[0]);
write(fd[1], p, strlen(p));
close(fd[1]);
//son_process();
}
else
{
sys_error("fork err");
}
}
进程通讯之套接字
当无血缘关系的进程间通讯时,就需要用到socket 了。
server.c 代码如下:
/*
Faker @2020-12-10
*/
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, const char* argv[])
{
int lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
if(lfd == -1)
{
perror("socket error");
exit(1);
}
unlink("server.sock");
//bind
struct sockaddr_un serv;
serv.sun_family = AF_LOCAL;
strcpy(serv.sun_path, "server.sock");
int ret = bind(lfd, (struct sockaddr*)&serv, sizeof(serv));
if(ret == -1)
{
perror("bind err");
}
//Listen
ret = listen(lfd, 36);
if(ret == -1)
{
perror("listen error");
exit(1);
}
//wait connect request
struct sockaddr_un client;
socklen_t len = sizeof(client);
int cfd = accept(lfd, (struct sockaddr*)&client, &len);
if(cfd == -1)
{
perror("accept error");
exit(1);
}
printf("client bind file: %sn", client.sun_path);
//Comm
while(1)
{
char buf[1024] = {0};
int recvlen = recv(cfd, buf, sizeof(buf),0);
if(recvlen == -1)
{
perror("recv error");
exit(1);
}
else if(recvlen == 0)
{
printf("clent disconnect .....n");
close(cfd);
break;
}
else
{
printf("recv buf: %sn", buf);
send(cfd, buf, recvlen, 0);
}
}
close(cfd);
close(lfd);
return 0;
}
client.c 代码如下:
/*
Faker @2020-12-10
client.c
*/
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, const char* argv[])
{
int fd = socket(AF_LOCAL, SOCK_STREAM, 0);
if(fd == -1)
{
perror("socket error");
exit(1);
}
ulink("client.sock");
//bind a socket file for client
struct sockaddr_un client;
client.sun_family = AF_LOCAL;
strcpy(client.sun_path, "client.sock");
int ret = bind(fd, (struct sockaddr*)&client, sizeof(client));
if(ret == -1)
{
perror("bind err");
exit(1);
}
//initial server info
struct sockaddr_un serv;
serv.sun_family = AF_LOCAL;
strcpy(serv.sun_path, "server.sock");
//connect server
connect(fd, (struct sockaddr*)&serv, sizeof(serv));
//Comm
while(1)
{
char buf[1024] = {0};
fgets(buf, sizeof(buf), stdin);
send(fd, buf,strlen(buf)+1, 0 );
//recv data
recv(fd, buf, sizeof(buf), 0);
printf("recv buf: %sn", buf);
}
close(fd);
return 0;
}
如果提示 bind err: Address already in use 解决方法:
在代码中使用 ulink("client.sock"); 和 ulink("server.sock"); 删除掉这2文件即可。
原作者:少年丶趁年轻