• [技术干货] 数据上云 | OneMO通信核心板接入OneNET云平台操作全过程
    前言最近在做物联网实训项目,要求将传感器的数据采集上传到平台上,便于查看数据的情况。经过几番查阅,最终选择使用OneMO通信核心板和OneNET平台来完成我的作业。一顿折腾,小白选手的我,终于摸出了大致结构,复盘下来发现不算复杂,通过简单的三步即可完成数据上云的操作。本着互联网分享精神,特将这呕心熬来的“沥血“分享给大家,希望能帮助大家少掉一点头发。第一步 OneNET云平台基础搭建1、OneNET平台侧创建产品注册完OneNET后,点击进入OneNET“多协议接入”产品栏,在MQTT(旧版)界面下添加产品,任意输入产品名“MQTT_TEST”点击确认,示例如下:2、获取OneNET平台侧参数通过产品名“MQTT_TEST”进入产品概况界面,获取“产品ID”(示例为538410)如下,然后点击设备列表进入设备管理界面:获取设备管理界面的“设备注册码”如下:注:平台侧仅需创建产品,获取“产品ID”和“设备注册码”即可,不需要单独创建设备。第二步 DTU参数配置首先我们需要下载串口调试工具并完成安装,并将OneMO通信核心板通过USB连接电脑,USB接线图如下:打开串口调试工具,进入通信核心板配置模式(我是前往OneMO官网下载的DTU指令介绍),通过AT+MQTTCFG=,,配置OneNET参数,示例如下:通过AT+WKMOD=MQTT1配置MQTT透传模式,执行AT+S保存配置并自动重启,示例如下:重启之后,模组会自动在平台上创建设备并连接,示例如下:注:设备名称默认为模组IMEI号,用户可根据需求在平台侧自行修改。第三步 成果展示那照上述操作已完成平台接入,下面我给大家演示一下我的接入成果。通过串口助手输入任意数据内容(示例为字符串类型)并点击发送,示例如下:平台侧可通过数据流界面观察数据,示例如下:同时,平台侧可以在设备列表界面,通过下发指令下发任意数据给模组,示例如下:通过串口助手,可以看到模组已收到平台下发的数据123456789ABC,示例如下:到此,设备数据上云的实例已介绍完毕,利用上面的例子可以实现设备端-OneNET云平台双向透传。在写文章的过程中,查看购买订单,发现这款DTU通信核心板最近有抢购的活动,心痛、肉痛、钱包痛!友友们,我要不再买一块补个仓拉低成本?!羊毛地址:https://dl-9.cn/111CY
  • [技术干货] C语言——链表总结
    本篇文章介绍C语言链表相关知识点,涉及链表的创建、单向链表、循环链表、双向链表、单向循环链表,链表常见问题总结等,还列出了结构体数组与链表的练习题,将在下篇文章贴出完整代码。1. 链表1.1 结构体对比数组特性: 内存空间连续、只能存放相同类型的数据 结构体特性: 内存空间连续、可以存放不同类型的数据#include <stdio.h> struct MyStruct { int a; char b; }; int main() { struct MyStruct *p; struct MyStruct data={12,'A'}; data.a=123; data.b='B'; p=&data; printf("Ñbace130-38b5-4ab1-8793-02493cd42100n",p->a); printf("Ábace130-38b5-4ab1-8793-02493cd42100n",p->b); return 0; }数组学生管理系统作业:作业: 学生管理系统 需求: (每一个功能都是使用函数进行封装) 1.实现从键盘上录入学生信息。 (姓名、性别、学号、成绩、电话号码) 2.将结构体里的学生信息全部打印出来。 3.实现根据学生的姓名或者学号查找学生,查找到之后打印出学生的具体信息。 4.根据学生的成绩对学生信息进行排序。 5.根据学号删除学生信息。1.2 单向链表的创建与运用链表: 单链表、双链表 区分: 循环和不循环链表 链表的特性: 一种数据结构的运行--->结构体。学习结构体数组(编写学生管理系统): 学生的人数问题不好确定。 链表本身就是一个结构体。单向链表的创建与运用:#include <stdio.h> #include <stdlib.h> #include <string.h> //定义结构体 struct MyListStruct { int a; struct MyListStruct *next; //结构体指针。存放下一个节点的地址 }; //定义链表头 struct MyListStruct *ListHead=NULL; //函数声明 struct MyListStruct *CreateListHead(struct MyListStruct *head); void PintListInfo(struct MyListStruct *head); void AddListNode(struct MyListStruct *head,struct MyListStruct NewNode); void DeleteListNode(struct MyListStruct *head,int a); int main() { int i; struct MyListStruct temp; int a; //1. 创建链表头 ListHead=CreateListHead(ListHead); //2. 添加节点 for(i=0; i<5; i++) { printf("输入新节点数据:"); scanf("%d",&temp.a); AddListNode(ListHead,temp); } //3. 打印所有节点数据 PintListInfo(ListHead); //4. 删除节点数据 printf("输入删除的节点数据值:"); scanf("%d",&a); DeleteListNode(ListHead,a); //5. 打印所有节点数据 PintListInfo(ListHead); return 0; } /* 函数功能: 创建链表头 返回值 : 链表头指针 */ struct MyListStruct *CreateListHead(struct MyListStruct *head) { if(head==NULL) { head=malloc(sizeof(struct MyListStruct)); head->next=NULL; } return head; //返回链表头 } /* 函数功能: 添加链表节点 说明: 链表头一般不存放数据 */ void AddListNode(struct MyListStruct *head,struct MyListStruct NewNode) { struct MyListStruct *p=head; //保存头地址 struct MyListStruct *new_p=NULL; //新的节点 /*1. 寻找链表尾*/ while(p->next!=NULL) { p=p->next; //移动到下一个地址 } /*2. 创建新节点*/ new_p=malloc(sizeof(struct MyListStruct)); if(new_p==NULL) { printf("新节点内存申请失败!\n"); return; } /*3. 新节点赋值*/ memcpy(new_p,&NewNode,sizeof(struct MyListStruct)); //内存拷贝 new_p->next=NULL; //尾节点指向空 /*4. 新节点添加*/ p->next=new_p; } /* 函数功能: 删除链表节点 */ void DeleteListNode(struct MyListStruct *head,int a) { struct MyListStruct *p=head; //保存头地址 struct MyListStruct *tmp=NULL; /*查找数据存在的节点位置*/ while(p->next!=NULL) { tmp=p; //保存上一个节点的地址 p=p->next; if(p->a==a) //查找成功 { tmp->next=p->next; //将要删除节点的指针值赋值给删除节点的上一个节点指针域 //tmp->next=tmp->next->next; free(p); //直接释放p节点空间 //break; p=head; //重新指向链表头 } } } /* 函数功能: 遍历所有链表信息 */ void PintListInfo(struct MyListStruct *head) { int cnt=0; struct MyListStruct *p=head; printf("\n链表遍历的数据如下:\n"); while(p->next!=NULL) { p=p->next; cnt++; printf("第%d个节点的数据=Ñbace130-38b5-4ab1-8793-02493cd42100n",cnt,p->a); } }作业:1.看代码、理解链表的创建流程 2.编写出单向链表的基础运用 3.将之前的学生管理系统使用链表方式做出来 链表的功能: (1)创建链表头 (2)在链表结尾添加一个节点 (3)删除指定的一个链表节点 (4)遍历链表,打印出所有的数据。 (5)在链表的指定节点的后面添加一个节点。 (6)根据链表节点里的数据对链表进行排序。 双向链表: (1)使用逆向+顺向两种遍历方式删除链表节点,目的: 提高效率。 类似于二分法查找。2. 链表问题总结动态空间分配: #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *p=malloc(100); //申请动态空间。 if(p==NULL) { return -1; } strcpy(p,"1234567890"); printf("%s\n",p); return 0; } #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *p1=malloc(100); //申请动态空间。 printf("%p\n",p1); char *p2=malloc(100); //申请动态空间。 printf("%p\n",p2); char *p3=malloc(100); //申请动态空间。 printf("%p\n",p3); char *p4=malloc(100); //申请动态空间。 printf("%p\n",p4); return 0; } 错误解决: 1.第一个错误开始解决 2.常规错误: 函数没有声明、分号每加、括号没有加、= 3.括号没有对齐。3. 双向链表和循环链表3.1 单向循环链表:#include <stdio.h> #include <stdlib.h> #include <string.h> //定义结构体 struct MyListStruct { int a; struct MyListStruct *next; //结构体指针。存放下一个节点的地址 }; //定义链表头 struct MyListStruct *ListHead=NULL; //函数声明 struct MyListStruct *CreateListHead(struct MyListStruct *head); void PintListInfo(struct MyListStruct *head); void AddListNode(struct MyListStruct *head,struct MyListStruct NewNode); void DeleteListNode(struct MyListStruct *head,int a); int main() { int i; struct MyListStruct temp; int a; //1. 创建链表头 ListHead=CreateListHead(ListHead); //2. 添加节点 for(i=0; i<5; i++) { printf("输入新节点数据:"); scanf("%d",&temp.a); AddListNode(ListHead,temp); } //3. 打印所有节点数据 PintListInfo(ListHead); //4. 删除节点数据 printf("输入删除的节点数据值:"); scanf("%d",&a); DeleteListNode(ListHead,a); //5. 打印所有节点数据 PintListInfo(ListHead); return 0; } /* 函数功能: 创建链表头 返回值 : 链表头指针 */ struct MyListStruct *CreateListHead(struct MyListStruct *head) { if(head==NULL) { head=malloc(sizeof(struct MyListStruct)); head->next=head; } return head; //返回链表头 } /* 函数功能: 添加链表节点 说明: 链表头一般不存放数据 */ void AddListNode(struct MyListStruct *head,struct MyListStruct NewNode) { struct MyListStruct *p=head; //保存头地址 struct MyListStruct *new_p=NULL; //新的节点 /*1. 寻找链表尾*/ while(p->next!=head) { p=p->next; //移动到下一个地址 } /*2. 创建新节点*/ new_p=malloc(sizeof(struct MyListStruct)); if(new_p==NULL) { printf("新节点内存申请失败!\n"); return; } /*3. 新节点赋值*/ memcpy(new_p,&NewNode,sizeof(struct MyListStruct)); //内存拷贝 new_p->next=head; //尾节点指向头 /*4. 新节点添加*/ p->next=new_p; } /* 函数功能: 删除链表节点 */ void DeleteListNode(struct MyListStruct *head,int a) { struct MyListStruct *p=head; //保存头地址 struct MyListStruct *tmp=NULL; /*查找数据存在的节点位置*/ while(p->next!=head) { tmp=p; //保存上一个节点的地址 p=p->next; if(p->a==a) //查找成功 { tmp->next=p->next; //将要删除节点的指针值赋值给删除节点的上一个节点指针域 //tmp->next=tmp->next->next; free(p); //直接释放p节点空间 //break; p=head; //重新指向链表头 } } } /* 函数功能: 遍历所有链表信息 */ void PintListInfo(struct MyListStruct *head) { int cnt=0; struct MyListStruct *p=head; printf("\n链表遍历的数据如下:\n"); while(p->next!=head) { p=p->next; cnt++; printf("第%d个节点的数据=Ñbace130-38b5-4ab1-8793-02493cd42100n",cnt,p->a); } }3.2 双向链表#include <stdio.h> #include <stdlib.h> #include <string.h> //定义结构体 struct MyListStruct { int a; struct MyListStruct *next; //结构体指针。存放下一个节点的地址 struct MyListStruct *old; //结构体指针。存放上一个节点的地址 }; //定义链表头 struct MyListStruct *ListHead=NULL; //函数声明 struct MyListStruct *CreateListHead(struct MyListStruct *head); void PintListInfo(struct MyListStruct *head); void AddListNode(struct MyListStruct *head,struct MyListStruct NewNode); void DeleteListNode(struct MyListStruct *head,int a); void PintListInfo_old(struct MyListStruct *head); int main() { int i; struct MyListStruct temp; int a; //1. 创建链表头 ListHead=CreateListHead(ListHead); //2. 添加节点 for(i=0; i<5; i++) { printf("输入新节点数据:"); scanf("%d",&temp.a); AddListNode(ListHead,temp); } //3. 打印所有节点数据 PintListInfo(ListHead); PintListInfo_old(ListHead); //4. 删除节点数据 printf("输入删除的节点数据值:"); scanf("%d",&a); DeleteListNode(ListHead,a); //5. 打印所有节点数据 PintListInfo(ListHead); PintListInfo_old(ListHead); return 0; } /* 函数功能: 创建链表头 返回值 : 链表头指针 */ struct MyListStruct *CreateListHead(struct MyListStruct *head) { if(head==NULL) { head=malloc(sizeof(struct MyListStruct)); head->next=NULL; //尾节点指向空 head->old=head; //上一个节点指向头 } return head; //返回链表头 } /* 函数功能: 添加链表节点 说明: 链表头一般不存放数据 */ void AddListNode(struct MyListStruct *head,struct MyListStruct NewNode) { struct MyListStruct *p=head; //保存头地址 struct MyListStruct *new_p=NULL; //新的节点 /*1. 寻找链表尾*/ while(p->next!=NULL) { p=p->next; //移动到下一个地址 } /*2. 创建新节点*/ new_p=malloc(sizeof(struct MyListStruct)); if(new_p==NULL) { printf("新节点内存申请失败!\n"); return; } /*3. 新节点赋值*/ memcpy(new_p,&NewNode,sizeof(struct MyListStruct)); //内存拷贝 new_p->next=NULL; //尾节点指向NULL new_p->old=p; //保存上一个节点的地址 /*4. 新节点添加*/ p->next=new_p; } /* 函数功能: 删除链表节点 顺向删除。 */ void DeleteListNode(struct MyListStruct *head,int a) { struct MyListStruct *p=head; //保存头地址 struct MyListStruct *tmp=NULL; /*查找数据存在的节点位置*/ while(p->next!=NULL) { tmp=p; //保存上一个节点的地址 p=p->next; if(p->a==a) //查找成功 { tmp->next=p->next; //将要删除节点的指针值赋值给删除节点的上一个节点指针域 //tmp->next=tmp->next->next; p->next->old=tmp; //保存上一个节点的地址 free(p); //直接释放p节点空间 //break; p=head; //重新指向链表头 } } } /* 函数功能: 遍历所有链表信息 从头到尾遍历 */ void PintListInfo(struct MyListStruct *head) { int cnt=0; struct MyListStruct *p=head; printf("\n(顺向)链表遍历的数据如下:\n"); while(p->next!=NULL) { p=p->next; cnt++; printf("第%d个节点的数据=Ñbace130-38b5-4ab1-8793-02493cd42100n",cnt,p->a); } } /* 函数功能: 遍历所有链表信息 从尾到头遍历 */ void PintListInfo_old(struct MyListStruct *head) { int cnt=0; struct MyListStruct *p=head; printf("\n(逆向)链表遍历的数据如下:\n"); /*1. 找到链表尾*/ while(p->next!=NULL) { p=p->next; } /*2. 通过链表尾遍历链表的数据*/ while(p!=head) { cnt++; printf("第%d个节点的数据=Ñbace130-38b5-4ab1-8793-02493cd42100n",cnt,p->a); p=p->old; //访问上一个节点 } }
  • [问题求助] uart_exmple和外接设备不能正常通信
    小熊派nano有个案例B6_basic_uart,是uart的Demo。按照原程序,把TX和RX短接,然后通过调试工具查看,是正常的收发。但是把串口输出连接到外部设备,就不正常了。比如usb转485的线连接到电脑上,有串口工具查看收发程序代码/* * Copyright (c) 2020 Nanjing Xiaoxiongpai Intelligent Technology Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#include #include #include #include "ohos_init.h"#include "cmsis_os2.h"#include "wifiiot_errno.h"#include "wifiiot_gpio.h"#include "wifiiot_gpio_ex.h"#include "wifiiot_adc.h"#include "wifiiot_uart.h"#define UART_TASK_STACK_SIZE 1024 * 8#define UART_TASK_PRIO 25#define UART_BUFF_SIZE 1000static const char *data = "Hello, BearPi!\r\n";static void UART_Task(void){ uint8_t uart_buff[UART_BUFF_SIZE] = {0}; uint8_t *uart_buff_ptr = uart_buff; uint32_t ret; WifiIotUartAttribute uart_attr = { //baud_rate: 9600 .baudRate = 9600, //data_bits: 8bits .dataBits = 8, .stopBits = 1, .parity = 0, }; //Initialize uart driver ret = UartInit(WIFI_IOT_UART_IDX_1, &uart_attr, NULL); if (ret != WIFI_IOT_SUCCESS) { printf("Failed to init uart! Err code = Üece653f-8bdd-4b7d-a762-5a1f288d4c22n", ret); return; } printf("UART Test Start\n"); while (1) { printf("=======================================\r\n"); printf("*************UART_example**************\r\n"); printf("=======================================\r\n"); //通过串口1发送数据 UartWrite(WIFI_IOT_UART_IDX_1, (unsigned char *)data, strlen(data)); //通过串口1接收数据 UartRead(WIFI_IOT_UART_IDX_1, uart_buff_ptr, UART_BUFF_SIZE); printf("Uart1 read data:%s", uart_buff_ptr); usleep(1000000); }}static void UART_ExampleEntry(void){ osThreadAttr_t attr; attr.name = "UART_Task"; attr.attr_bits = 0U; attr.cb_mem = NULL; attr.cb_size = 0U; attr.stack_mem = NULL; attr.stack_size = UART_TASK_STACK_SIZE; attr.priority = UART_TASK_PRIO; if (osThreadNew((osThreadFunc_t)UART_Task, NULL, &attr) == NULL) { printf("[ADCExample] Falied to create UART_Task!\n"); }}APP_FEATURE_INIT(UART_ExampleEntry);
  • [问题求助] NB-IoT模块不能激活
    请帮忙看看什么问题,该如何调试?
  • [技术干货] 部署Linux下开发环境,完成Linux下代码开发(合集)
    一、前言linux是一款自由软件,公开源码的、可以免费获取的操作系统,且linux下许多软件也都是可以免费获取的,linux已经被广泛使用在超级计算机、服务器、PC机、手机、嵌入式系统中。 很多人其实对Linux并不了解,只知道它是个开源操作系统,有一个内核,仅此而已。很多人以为做Linux就是做运维,其实不然,运维只是其中一个方向而已,还有另一个很重要的方向是开发。 开发主要有两个方向:应用开发、底层开发。 应用开发工程师主要在Linux 环境下开发各种应用程序,就如同你们在 Windows 下开发的应用程序一样(比如QQ,PC微信等)。这些应用程序有些带有界面,也有很多是不带界面的。带界面的很多是由 QT 开发,当然也有其他的开发工具。 底层开发工程师就会跟内核、uboot、文件系统、驱动等比较底层的东西打交道。但是,很多移植类的工作芯片厂商都已经给你做好了,很多底层工程师都是做维护的工作,以及驱动开发的工作。 目前很多服务器都是部署在Linux系统上的,比如:很多物联网云平台的MQTT服务器、很多公司的网站等等。 二、文章集合2.1 Linux系统下基础命令介绍链接:https://bbs.huaweicloud.com/forum/thread-0264102134841708046-1-1.htmlLinux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。Linux操作系统诞生于1991 年10 月5 日(这是第一次正式向外公布时间)。Linux存在着许多不同的Linux版本,但它们都使用了Linux内核。Linux可安装在各种计算机硬件设备中,比如手机、平板电脑、路由器、视频游戏控制台、台式计算机、大型机和超级计算机。2.2 华为云ECS服务器部署_CentOS7.4服务器安装NFS,ffmpeg、Qt环境链接:https://bbs.huaweicloud.com/forum/thread-0216102134937528112-1-1.html在CentOS7.4服务器版本的环境下搭建NFS服务器、安装ffmpeg、安装nginx服务器、部署Qt编译环境。 (1)配置NGINX为HTTP服务器,安装rtmp模块,完成rtmp视频推流,支持缓存视频到本地目录、支持转为HLS流,通过浏览器访问直播流。 (2)部署Qt编译环境,可以编译Qt代码,运行Qt程序。 因为是服务器,我这里部署Qt环境,运行Qt程序是属于控制台版本,不用Qt的GUI模块,只是用核心模块完成一些功能设计。 (3)安装、编译 ffmpeg : 支持源码编译安装ffmpeg、在线安装ffmpeg,通过ffmpeg可以完成音视频的一些开发。比如:视频转码、视频合并、视频剪切、视频特效处理。----这里用ffmpeg并不是做播放器,是在服务器上用,主要是处理视频文件。 (4)安装NFS服务器,主要是方便与本地Linux系统进行通信,编译代码,文件共享。 (5)部署开机自动启动程序:方便设置自己的程序为开机启动。 2.3  华为云ECS服务器安装CentOS7.4镜像,部署GINX服务器、搭建物联网视频监控系统链接:https://bbs.huaweicloud.com/forum/thread-0228102135095482106-1-1.html在CentOS7.4服务器版本的环境下安装nginx服务器、配置文件服务器、流媒体服务器。(1)配置NGINX为HTTP服务器,安装rtmp模块,完成rtmp视频推流,支持缓存视频到本地目录、支持转为HLS流,通过浏览器访问直播流。(2)部署开机自动启动程序:方便设置自己的程序为开机启动。2.4 Linux系统-Makefile规则介绍、基本使用链接:https://bbs.huaweicloud.com/forum/thread-0299102303834721003-1-1.html一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,也可以执行操作系统的命令。2.5  Linux下(pthread)线程创建与使用链接:https://bbs.huaweicloud.com/forum/thread-0288102303912787002-1-1.html前面文章介绍了Linux下进程的创建、管理、使用、通信,了解了多进程并发;这篇文章介绍Linux下线程的基本使用。 线程与进程的区别 (1)进程: 是操作系统调度最小单位。 Linux下可以通过ps、top等命令查看进程的详细信息。 (2)线程: 是进程调度的最小单位,每个进程都有一个主线程。在进程里主要做事情就是线程。 (3)在全系统中,进程ID是唯一标识,对于进程的管理都是通过PID来实现的。每创建一个进程,内核去中就会创建一个结构体来存储该进程的全部信息,每一个存储进程信息的节点也都保存着自己的PID。需要管理该进程时就通过这个ID来实现(比如发送信号)。当子进程结束要回收时(子进程调用exit()退出或代码执行完),需要通过wait()系统调用来进行,未回收的消亡进程会成为僵尸进程,其进程实体已经不复存在,但会虚占PID资源,因此回收是有必要的。 对于线程而言,若要主动终止需要调用pthread_exit() ,主线程需要调用pthread_join()来回收(前提是该线程没有设置 “分离属性”)。像线发送线程信号也是通过线程ID实现 进程间的通信方式: A.共享内存 B.消息队列 C.信号量 D.有名管道 E.无名管道 F.信号 G.文件 H.socket 线程间的通信方式: A.互斥量 B.自旋锁 C.条件变量 D.读写锁 E.线程信号 F.全局变量 进程间采用的通信方式要么需要切换内核上下文,要么要与外设访问(有名管道,文件)。所以速度会比较慢。而线程采用自己特有的通信方式的话,基本都在自己的进程空间内完成,不存在切换,所以通信速度会较快。也就是说,进程间与线程间分别采用的通信方式,除了种类的区别外,还有速度上的区别。 说明: 当运行多线程的进程捕获到信号时,只会阻塞主线程,其他子线程不会影响会继续执行。 2.6  Linux系统下(pthread)线程的使用案例(分离属性、清理函数等)链接:https://bbs.huaweicloud.com/forum/thread-0254102303978648006-1-1.html这篇文章介绍Linux下线程的创建与基本使用案例,主要是案例代码为主;相关的函数详细介绍在上篇文章里已经介绍过了。2.7 Linux系统下(pthread)线程通信(读写锁) 使用介绍链接:https://bbs.huaweicloud.com/forum/thread-0235102304161444005-1-1.html 读写锁与互斥锁类似,读写锁比互斥锁有更高的并行性,读写锁特点如下: ​ 1. 读写锁有三种状态,读模式下加锁(共享)、写模式下加锁(独占)以及不加锁。 ​ 2. 一次只有一个线程可以占有写模式下的读写锁;但是多个线程可以同时占有读模式下的读写锁。 ​ 3. 读写锁在写加锁状态时,其他试图以写状态加锁的线程都会被阻塞。读写锁在读加锁状态时,如果有线程希望以写模式加锁时,必须阻塞,直到所有线程释放锁。 ​ 4. 当读写锁以读模式加锁时,如果有线程试图以写模式对其加锁,那么读写锁会阻塞随后的读模式锁请求,以避免读锁长期占用,而写锁得不到请求。  读写锁总结: 读写锁分为读锁和写锁。 如果资源被读写锁保护,多个线程可以同时获取读锁—也就是读支持多个线程同时读。 资源加了写锁之后,在写资源的时候只能被一个线程占用,其他读锁就会阻塞。 读锁和写锁也是互斥的关系。读的时候不能写,写的时候不能读。 但是读的时候可以支持多个线程同时读,写的时候只能被一个线程写,其他线程也不能读。 
  • [技术干货] Linux系统下(pthread)线程通信(读写锁) 使用介绍
    1. 读写锁介绍读写锁与互斥锁类似,读写锁比互斥锁有更高的并行性,读写锁特点如下:​ 1. 读写锁有三种状态,读模式下加锁(共享)、写模式下加锁(独占)以及不加锁。​ 2. 一次只有一个线程可以占有写模式下的读写锁;但是多个线程可以同时占有读模式下的读写锁。​ 3. 读写锁在写加锁状态时,其他试图以写状态加锁的线程都会被阻塞。读写锁在读加锁状态时,如果有线程希望以写模式加锁时,必须阻塞,直到所有线程释放锁。​ 4. 当读写锁以读模式加锁时,如果有线程试图以写模式对其加锁,那么读写锁会阻塞随后的读模式锁请求,以避免读锁长期占用,而写锁得不到请求。读写锁总结:读写锁分为读锁和写锁。如果资源被读写锁保护,多个线程可以同时获取读锁—也就是读支持多个线程同时读。资源加了写锁之后,在写资源的时候只能被一个线程占用,其他读锁就会阻塞。读锁和写锁也是互斥的关系。读的时候不能写,写的时候不能读。但是读的时候可以支持多个线程同时读,写的时候只能被一个线程写,其他线程也不能读。2. 读写锁相关函数1. 读写锁初始化 int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); 2. 读模式加锁 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); 3. 写模式加锁 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); 4. 解锁 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); 5. 销毁读写锁 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);3.案例代码: 读写锁使用模型下面代码使用读写多保护一个全局变量的读写。#include #include #include #include #include #include #include #include #include pthread_rwlock_t rwlock; int data; /* 线程工作函数 */ void *thread_work_func(void *dev) { while(1) { pthread_rwlock_rdlock(&rwlock); //读加锁 printf("data=ß6aa7a7a-1f24-44f3-b551-5daf4f62986cn",data); pthread_rwlock_unlock(&rwlock); //解锁 sleep(1); } } /* 线程工作函数 */ void *thread_work_func2(void *dev) { while(1) { pthread_rwlock_wrlock(&rwlock); //写加锁 data++; pthread_rwlock_unlock(&rwlock); //解锁 sleep(1); } } int main(int argc,char **argv) { //初始化读写锁 pthread_rwlock_init(&rwlock,NULL); /*1. 创建子线程1*/ pthread_t thread_id; if(pthread_create(&thread_id,NULL,thread_work_func,NULL)!=0) { printf("子线程1创建失败.\n"); return -1; } /*2. 创建子线程2*/ pthread_t thread_id2; if(pthread_create(&thread_id2,NULL,thread_work_func2,NULL)!=0) { printf("子线程2创建失败.\n"); return -1; } /*3. 等待线程的介绍*/ pthread_join(thread_id,NULL); pthread_join(thread_id2,NULL); //销毁读写锁 pthread_rwlock_destroy(&rwlock); return 0; }
  • [技术干货] Linux系统下(pthread)线程的使用案例(分离属性、清理函数等)
    这篇文章介绍Linux下线程的创建与基本使用案例,主要是案例代码为主;相关的函数详细介绍在上篇文章里已经介绍过了。1. 案例代码: 线程的创建下面这份代码演示如何创建线程。在编译的时候需要加上-lpthread函数原型:#include int pthread_create ( pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg );示例代码:#include #include #include #include #include #include #include #include //[wbyq@wbyq linux_c]$ gcc app.c -lpthread /* 线程工作函数 */ void *thread_work_func(void *dev) { int i; for(i=0;i<5;i++) { sleep(1); printf("子线程正在运行.%d \n",i); } //终止当前线程执行 pthread_exit(NULL); } int main(int argc,char **argv) { /*1. 创建子线程*/ pthread_t thread_id; if(pthread_create(&thread_id,NULL,thread_work_func,NULL)!=0) { printf("子线程创建失败.\n"); return -1; } /*2. 等待子线程结束-清理子线程的空间*/ pthread_join(thread_id,NULL);//--wait printf("主线程正常终止.\n"); return 0; }2. 如何接收子线程的返回值?线程运行的时候默认是结合模式,也可以设置成分离模式,如果是默认的模式,在线程执行完毕后需要回收资源,顺便可以介绍子线程结束时,返回的状态值。函数原型:int pthread_join(pthread_t thread, void **retval);示例代码:#include #include #include #include #include #include #include #include //[wbyq@wbyq linux_c]$ gcc app.c -lpthread /* 线程工作函数 */ void *thread_work_func(void *dev) { int i; for(i=0;i<5;i++) { sleep(1); printf("子线程正在运行.%d \n",i); } //终止当前线程执行 pthread_exit("1234567890"); } int main(int argc,char **argv) { /*1. 创建子线程*/ pthread_t thread_id; if(pthread_create(&thread_id,NULL,thread_work_func,NULL)!=0) { printf("子线程创建失败.\n"); return -1; } /*2. 等待子线程结束-清理子线程的空间*/ char *p; pthread_join(thread_id,&p);//--wait printf("主线程正常终止.子线的返回值:%s\n",p); return 0; }3. 设置线程的分离属性默认情况下,子线程是结合模式,需要手动等待子线程结束,清理空间;子线程也支持设置为分离属性,在子线程运行结束后,自己清理空间,下面的例子就演示如何设置子线程为分离模式。函数原型:int pthread_detach(pthread_t thread);示例代码:#include #include #include #include #include #include #include #include //[wbyq@wbyq linux_c]$ gcc app.c -lpthread /* 线程工作函数 */ void *thread_work_func(void *dev) { int i; for(i=0;i<5;i++) { sleep(1); printf("子线程正在运行.%d \n",i); } //终止当前线程执行 pthread_exit(NULL); } int main(int argc,char **argv) { /*1. 创建子线程*/ pthread_t thread_id; if(pthread_create(&thread_id,NULL,thread_work_func,NULL)!=0) { printf("子线程创建失败.\n"); return -1; } /*2. 设置线程的分离属性*/ pthread_detach(thread_id); while(1) { printf("主线程正在运行.\n"); sleep(1); } return 0; }4. 注册线程的清理函数线程清理函数,可以在线程退出时自动调用或者手动调用,用于清理一些需要释放的资源。函数原型://注册 void pthread_cleanup_push(void (*routine)(void *),void *arg); //释放 void pthread_cleanup_pop(int execute);示例代码:#include #include #include #include #include #include #include #include #include //[wbyq@wbyq linux_c]$ gcc app.c -lpthread //线程的清理工作函数 void thread_clear_work_func(void *dev) { printf("线程的清理工作函数被调用.\n"); /* 做一些资源清理工作。 比如: 释放malloc申请的空间,关闭打开的文件等等. */ } /* 线程工作函数 */ void *thread_work_func(void *dev) { int i; //注册清理函数 pthread_cleanup_push(thread_clear_work_func,NULL); for(i=0;i<5;i++) { sleep(1); printf("子线程正在运行.%d \n",i); } //终止当前线程执行 pthread_exit(NULL); //释放清理函数 pthread_cleanup_pop(1); } int main(int argc,char **argv) { /*1. 创建子线程*/ pthread_t thread_id; if(pthread_create(&thread_id,NULL,thread_work_func,NULL)!=0) { printf("子线程创建失败.\n"); return -1; } /*2. 设置线程的分离属性*/ pthread_detach(thread_id); sleep(3); //取消指定子线程结束 pthread_cancel(thread_id); while(1) { printf("主线程正在运行.\n"); sleep(1); } return 0; }5. 通过ulimit命令设置栈空间大小pthread_create 创建线程时,若不指定分配堆栈大小,系统会分配默认值,查看默认值方法如下:[root@tiny4412 ]#ulimit -s 10240上面的10240单位是KB,也就是默认的线程栈空间大小为10M也可以通过ulimit -a命令查看,其中的stack size也表示栈空间大小。[root@tiny4412 ]#ulimit -a -f: file size (blocks) unlimited -t: cpu time (seconds) unlimited -d: data seg size (kb) unlimited -s: stack size (kb) 10240 -c: core file size (blocks) 0 -m: resident set size (kb) unlimited -l: locked memory (kb) 64 -p: processes 7512 -n: file descriptors 1024 -v: address space (kb) unlimited -w: locks unlimited -e: scheduling priority 0 -r: real-time priority 0设置栈空间大小: ulimit -s <栈空间大小>[root@tiny4412 ]#ulimit -s 8192 //设置栈空间大小 [root@tiny4412 ]#ulimit -s //查看栈空间大小 8192 //大小为8M注意: 栈空间设置只能在超级管理员用户权限下设置。每个线程的栈空间都是独立的,如果栈空间溢出程序会出现段错误。如果一个进程有10个线程,那么分配的栈空间大小就是10*<每个线程栈大小>例如:int main(int argc,char **argv) { char buff[1024*1024*10]; //在栈空间定义数组,如果超出了栈空间总大小程序会奔溃。 printf("hello world!\n"); return 0; }
  • [技术干货] Linux下(pthread)线程创建与使用
    1. 前言前面文章介绍了Linux下进程的创建、管理、使用、通信,了解了多进程并发;这篇文章介绍Linux下线程的基本使用。线程与进程的区别 (1)进程: 是操作系统调度最小单位。 Linux下可以通过ps、top等命令查看进程的详细信息。 (2)线程: 是进程调度的最小单位,每个进程都有一个主线程。在进程里主要做事情就是线程。(3)在全系统中,进程ID是唯一标识,对于进程的管理都是通过PID来实现的。每创建一个进程,内核去中就会创建一个结构体来存储该进程的全部信息,每一个存储进程信息的节点也都保存着自己的PID。需要管理该进程时就通过这个ID来实现(比如发送信号)。当子进程结束要回收时(子进程调用exit()退出或代码执行完),需要通过wait()系统调用来进行,未回收的消亡进程会成为僵尸进程,其进程实体已经不复存在,但会虚占PID资源,因此回收是有必要的。对于线程而言,若要主动终止需要调用pthread_exit() ,主线程需要调用pthread_join()来回收(前提是该线程没有设置 “分离属性”)。像线发送线程信号也是通过线程ID实现进程间的通信方式: A.共享内存 B.消息队列 C.信号量 D.有名管道 E.无名管道 F.信号 G.文件 H.socket 线程间的通信方式: A.互斥量 B.自旋锁 C.条件变量 D.读写锁 E.线程信号 F.全局变量进程间采用的通信方式要么需要切换内核上下文,要么要与外设访问(有名管道,文件)。所以速度会比较慢。而线程采用自己特有的通信方式的话,基本都在自己的进程空间内完成,不存在切换,所以通信速度会较快。也就是说,进程间与线程间分别采用的通信方式,除了种类的区别外,还有速度上的区别。说明: 当运行多线程的进程捕获到信号时,只会阻塞主线程,其他子线程不会影响会继续执行。2. 线程相关函数介绍2.1 创建线程pthread_create是Unix操作系统(Unix、Linux等)的创建线程的函数。 编译时需要指定链接库:-lpthread 函数原型#include int pthread_create ( pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg );参数介绍第一个参数为指向线程标识符的指针。 第二个参数用来设置线程属性。默认可填NULL。 第三个参数是线程运行函数的起始地址。 最后一个参数是运行函数的参数。不需要参数可填NULL。 Linux下查看函数帮助:# man pthread_create返回值: 若线程创建成功,则返回0。若线程创建失败,则返回出错编号。 线程创建成功后, attr参数用于指定各种不同的线程属性。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg,如果需要向线程工作函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。示例:#include #include //线程函数1 void *pthread_func1(void *arg) { while(1) { printf("线程函数1正在运行.....\n"); sleep(2); } } //线程函数2 void *pthread_func2(void *arg) { while(1) { printf("线程函数2正在运行.....\n"); sleep(2); } } int main(int argc,char **argv) { pthread_t thread_id1; pthread_t thread_id2; /*1. 创建线程1*/ if(pthread_create(&thread_id1,NULL,pthread_func1,NULL)) { printf("线程1创建失败!\n"); return -1; } /*2. 创建线程2*/ if(pthread_create(&thread_id2,NULL,pthread_func2,NULL)) { printf("线程2创建失败!\n"); return -1; } /*3. 等待线程结束,释放线程的资源*/ pthread_join(thread_id1,NULL); pthread_join(thread_id2,NULL); return 0; } //gcc pthread_demo_code.c -lpthread2.2 退出线程线程通过调用pthread_exit函数终止执行,就如同进程在结束时调用exit函数一样。这个函数的作用是,终止调用它的线程并返回一个指向某个对象的指针。这个函数的作用是,终止调用它的线程并返回一个指向某个对象的指针,该返回值可以通过pthread_join函数的第二个参数得到。函数原型#include void pthread_exit(void *retval);参数解析 线程的需要返回的地址。 注意: 线程结束必须释放线程堆栈,就是说线程函数必须调用pthread_exit()结束,否则直到主进程函数退出才释放2.3 等待线程结束pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable(结合属性)属性。 函数原型#include int pthread_join(pthread_t thread, void **retval);参数 第一个参数: 线程标识符,即线程ID,标识唯一线程。 最后一个参数: 用户定义的指针,用来存储被等待线程返回的地址。 返回值 0代表成功。 失败,返回的则是错误号。 接收线程返回值示例://退出线程 pthread_exit ("线程已正常退出"); //接收线程的返回值 void *pth_join_ret1; pthread_join( thread1, &pth_join_ret1);2.4 线程分离属性创建一个线程默认的状态是joinable(结合属性),如果一个线程结束运行但没有调用pthread_join,则它的状态类似于进程中的Zombie Process(僵死进程),即还有一部分资源没有被回收(退出状态码),所以创建线程者应该pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似于进程的wait,waitpid)。但是调用pthread_join(pthread_id)函数后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。pthread_detach函数可以将该线程的状态设置为detached(分离状态),则该线程运行结束后会自动释放所有资源。 函数原型#include int pthread_detach(pthread_t thread);参数 线程标识符 返回值 0表示成功。错误返回错误码。 EINVAL线程并不是一个可接合线程。 ESRCH没有线程ID可以被发现。2.5 获取当前线程的标识符pthread_self函数功能是获得线程自身的ID。 函数原型#include pthread_t pthread_self(void);返回值 当前线程的标识符。 pthread_t的类型为unsigned long int,所以在打印的时候要使用%lu方式,否则显示结果出问题。2.6 自动清理线程资源线程可以安排它退出时需要调用的函数,这样的函数称为线程清理处理程序。用于程序异常退出的时候做一些善后的资源清理。 在POSIX线程API中提供了一个pthread_cleanup_push()/pthread_cleanup_pop()函数用于自动释放资源。从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用 pthread_exit()和异常终止)都将执行pthread_cleanup_push()所指定的清理函数。注意:pthread_cleanup_push函数与pthread_cleanup_pop函数需要成对调用。 函数原型void pthread_cleanup_push(void (*routine)(void *),void *arg); //注册清理函数 void pthread_cleanup_pop(int execute); //释放清理函数参数 void (*routine)(void *) :处理程序的函数入口。 void *arg :传递给处理函数的形参。 int execute:执行的状态值。 0表示不调用清理函数。1表示调用清理函数。导致清理函数调用的条件:调用pthread_exit()函数pthread_cleanup_pop的形参为1。 注意:return不会导致清理函数调用。2.7 自动清理线程示例代码#include #include #include //线程清理函数 void routine_func(void *arg) { printf("线程资源清理成功\n"); } //线程工作函数 void *start_routine(void *dev) { pthread_cleanup_push(routine_func,NULL); //终止线程 // pthread_exit(NULL); pthread_cleanup_pop(1); //1会导致清理函数被调用。0不会调用。 } int main(int argc,char *argv[]) { pthread_t thread_id; //存放线程的标识符 /*1. 创建线程*/ if(pthread_create(&thread_id,NULL,start_routine,NULL)!=0) { printf("线程创建失败!\n"); } /*2.设置线程的分离属性*/ if(pthread_detach(thread_id)!=0) { printf("分离属性设置失败!\n"); } while(1){} return 0; }2.8 线程取消函数pthread_cancel函数为线程取消函数,用来取消同一进程中的其他线程。头文件: #include 函数原型:pthread_cancel(pthread_t tid);
  • [技术干货] Linux系统-Makefile规则介绍、基本使用
    1. Makefile基本使用命令行的make命令支持解析makefile和Makefile文件。 如果我们编写的规则文件不是makefile或者Makefile文件,那么需要使用-f选项指定。[wbyq@wbyq linux_c]$ make -f <规则文件>Makefile文件内部是以目标作为单位执行规则顺序的。在Makefile文件里使用shell命令行的命令时,需要使用TAB键开头。app:app.c gcc app.c -o app<目标文件名称>:<依赖文件> gcc app.c -o app 什么是依赖文件? 就是在生成这个目标之前,需要得到的文件。2. make指定目标执行Makefile文件里可以有多个目标文件,我们在命令行执行make命令默认执行Makefile文件里的第一个目标。如果需要执行指定的目标,需要在make命令后面加上执行的目标名称即可。[wbyq@wbyq linux_c]$ make pwd /mnt/hgfs/linux-share-dir/linux_c [wbyq@wbyq linux_c]$ make app2 ls a.out app.c Makefile makefile_1234567 sum [wbyq@wbyq linux_c]$ make app3 touch 123.c touch 456.c [wbyq@wbyq linux_c]$ make app4 rm 123.c rm 456.c [wbyq@wbyq linux_c]$ make app1 app2 app3 app4 pwd /mnt/hgfs/linux-share-dir/linux_c ls a.out app.c Makefile makefile_1234567 sum touch 123.c touch 456.c rm 123.c rm 456.c [wbyq@wbyq linux_c]$3. 隐藏makefile文件执行命令方法1: make -s 方法2: 在执行的命令前面加上@符号4. 示例1: 编译一个简单的工程使用显式规则编写Makefile文件,编译一个工程。[wbyq@wbyq linux_c]$ ls app.c Makefile sum.c sum.h [wbyq@wbyq linux_c]$ make -n gcc sum.c -c gcc app.c -c gcc sum.o app.o -o app [wbyq@wbyq linux_c]$ make gcc sum.c -c gcc app.c -c gcc sum.o app.o -o app [wbyq@wbyq linux_c]$ ./app sum_val=300 [wbyq@wbyq linux_c]$ 5. 示例2: 编译一个简单的工程使用自动化编译符号优化Makefile文件,编译一个工程。6. 示例3: 编译一个简单的工程使用隐式规则编写Makefile文件,编译一个工程。[wbyq@wbyq linux_c]$ ls app.c Makefile sum.c sum.h [wbyq@wbyq linux_c]$ make -n cc -c -o app.o app.c cc -c -o sum.o sum.c gcc app.o sum.o -o app [wbyq@wbyq linux_c]$ make cc -c -o app.o app.c cc -c -o sum.o sum.c gcc app.o sum.o -o app [wbyq@wbyq linux_c]$ make make: “app”是最新的。 [wbyq@wbyq linux_c]$ make cc -c -o app.o app.c cc -c -o sum.o sum.c gcc app.o sum.o -o app7. 特殊变量的使用编写的Makefile文件: CC=gcc #指定编译器 VPATH=./main:./sum #指定隐式推导时搜索的路径 CFLAGS=-I ./sum #指定编译器编译是自动增加的选项参数 OBJ=app.o sum.o #依赖文件 app:$(OBJ) $(CC) $^ -o $@ .PHONY:clear #声明伪目标 clear: rm *.o app
  • [技术干货] 华为云ECS服务器安装CentOS7.4镜像,部署GINX服务器、搭建物联网视频监控系统
    【摘要】 在CentOS7.4服务器版本的环境下安装nginx服务器、配置文件服务器、流媒体服务器。 (1)配置NGINX为HTTP服务器,安装rtmp模块,完成rtmp视频推流,支持缓存视频到本地目录、支持转为HLS流,通过浏览器访问直播流。 (2)部署开机自动启动程序:方便设置自己的程序为开机启动。1. 环境介绍环境介绍: 采用的是华为云的ECS弹性云服务器–镜像安装的CentOS7.4 64位 -----是服务器版,非桌面版哦。在CentOS7.4服务器版本的环境下安装nginx服务器、配置文件服务器、流媒体服务器。(1)配置NGINX为HTTP服务器,安装rtmp模块,完成rtmp视频推流,支持缓存视频到本地目录、支持转为HLS流,通过浏览器访问直播流。(2)部署开机自动启动程序:方便设置自己的程序为开机启动。2. Centos中安装带rtmp模块的Nginx2.1 新安装的系统可以先安装一些工具yum install -y pcre pcre-devel openssl openssl-devel zlib zlib-devel gcc gcc-c++ yum install -y vim wget lsof git zip unzip2.2 获取Nginx二进制源码听说srtmp模块暂时只支持Nginx13-15版本,当前就在官网下载Nginx14wget http://nginx.org/download/nginx-1.14.2.tar.gz tar xvf nginx-1.14.2.tar.gz 2.2 获取nginx-rtmp-modulewget https://github.com/arut/nginx-rtmp-module/archive/refs/tags/v1.2.1.tar.gz tar xvf v1.2.1.tar.gz2.3 编译nginxcd nginx-1.14.2 ./configure --add-module=../nginx-rtmp-module-1.2.1/ --with-http_ssl_module make && make install #建立软链接 ln -s /usr/local/nginx/sbin/nginx /usr/bin特别说明:如果在配置时报错,一般就是缺东西了,安装了再配置。比如:报错 ./configure: error: the HTTP rewrite module requires the PCRE library. You can either disable the mo .... 解决: yum -y install pcre-devel yum -y install openssl openssl-devel2.4 修改Nginx的配置文件打开/usr/local/nginx/conf/nginx.conf文件,在文件最后面加入下面的配置。rtmp { server { listen 8888; application live { live on; } } }上面8888是rtmp推流和拉流的端口。修改nginx.conf之后,重启nginx服务:sudo service nginx restart重启服务之后,使用netstat -ltn命令查看TCP监听的端口,确认下Nginx的监听端口是否正常。正常情况,一个是我们自己设置的rtmp服务监听端口8888,还有一个80是Nginx默认的HTTP服务监听端口。接下来可以在浏览器里输入本机IP地址:http://127.0.0.1/,查看Nginx服务开启状态。2.5 设置开机启动wget http://raw.github.com/JasonGiedymin/nginx-init-ubuntu/master/nginx -O /etc/init.d/nginx chmod +x /etc/init.d/nginx update-rc.d nginx defaults2.6 控制nginx服务的3个命令: 启动、停止、重启service nginx start service nginx stop service nginx restart 或者 nginx -s reload (运行中生效配置文件)2.7 进行rtmp推流服务器搭建好之后,推流和拉流的地址就是: rtmp://<服务器IP地址>:8888/live/<推流存放的目录> 例如: rtmp://127.0.0.1:8888/live/xl2.8 nginx保存推流视频文件如果需要让推流上来的文件保存下来后续进行查看历史文件,可以配置nginx进行保存。在原来的/usr/local/nginx/conf/nginx.conf配置文件里rtmp模块中增加新的配置:record all; record_unique on; record_path "./video"; #视频缓存的路径 record_suffix -%Y-%m-%d-%H_%M_%S.flv;完整/usr/local/nginx/conf/nginx.conf里的rtmp模块的配置如下:#RTMP服务 rtmp { server { listen 8888; application live { live on; #开启实时 record all; record_unique on; record_path "./video"; #视频缓存的路径 record_suffix -%Y-%m-%d-%H_%M_%S.flv; } } }配置之后执行命令nginx -s reload重启服务器即可。2.9 rtmp直播流转为hls直播流什么是HLS直播流?HLS 全称是 HTTP Live Streaming,是一个由 Apple 公司提出的基于 HTTP 的媒体流传输协议,用于实时音视频流的传输。目前HLS协议被广泛的应用于视频点播和直播领域。 原理介绍 HLS 跟 DASH 协议的原理非常类似。通过将整条流切割成一个小的可以通过 HTTP 下载的媒体文件,然后提供一个配套的媒体列表文件,提供给客户端,让客户端顺序地拉取这些媒体文件播放,来实现看上去是在播放一条流的效果。由于传输层协议只需要标准的 HTTP 协议,HLS 可以方便的透过防火墙或者代理服务器,而且可以很方便的利用 CDN 进行分发加速,并且客户端实现起来也很方便。 HLS 把整个流分成一个个小的基于 HTTP 的文件来下载,每次只下载一些。HLS 协议由三部分组成:HTTP、M3U8、TS。这三部分中,HTTP 是传输协议,M3U8 是索引文件,TS 是音视频的媒体信息。HLS协议编码格式要求:视频的编码格式:H264 音频的编码格式:AAC、MP3、AC-3 视频的封装格式:ts 保存 ts 索引的 m3u8 文件配置/usr/local/nginx/conf/nginx.conf将RTMP流转为HLS流。在http模块的server配置里增加新的配置:location /live_hls{ types { #m3u8 type设置 application/vnd.apple.mpegurl m3u8; #ts分片文件设置 video/mp2t ts; } #指向访问m3u8文件目录 alias ./m3u8File; #和rtmp模块里的hls_path设置路径一样 add_header Cache-Control no-cache; #禁止缓存 }在rtmp模块的server配置里增加新的配置:hls on; #开启hls hls_path ./m3u8File; #hls的ts切片存放路径 (这是个目录,会自动创建的) hls_fragment 2s; #本地切片长度 hls_playlist_length 6s;#HLS播放列表长度/usr/local/nginx/conf/nginx.conf文件的完整的配置如下:worker_processes 1; #Nginx进程数,建议设置为等于CPU总核数 events { worker_connections 1024; #工作模式与连接数上限 } rtmp_auto_push on; #RTMP服务 rtmp { server { listen 8888; application live { live on; #开启实时 record all; record_unique on; record_path "./video"; #视频缓存的路径 record_suffix -%Y-%m-%d-%H_%M_%S.flv; hls on; #开启hls hls_path ./m3u8File; #hls的ts切片存放路径 hls_fragment 2s; #本地切片长度 hls_playlist_length 6s;#HLS播放列表长度 } } } #HTTP服务 http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 8099; server_name localhost; location / { root html; index index.html index.htm; } location /live_hls{ types{ #m3u8 type设置 application/vnd.apple.mpegurl m3u8; #ts分片文件设置 video/mp2t ts; } #指向访问m3u8文件目录 alias ./m3u8File; add_header Cache-Control no-cache; #禁止缓存 } location /control{ rtmp_control all; } location /stat{ rtmp_stat all; rtmp_stat_stylesheet stat.xsl; } location /stat.xsl{ root ./nginx-rtmp-module-master; } # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }配置之后重启服务器即可。按照前面的配置,RTMP推流地址和HTTP访问地址如下:RTMP推流和拉流地址: rtmp://127.0.0.1:8888/live/video01 那么对应的HTTP的访问地址:http://127.0.0.1:8099/live_hls/video01.m3u8说明: 转为HLS流之后,如果浏览器支持HLS流就可以直接输入地址播放。一般手机浏览器都支持的。比如:苹果手机的自带浏览器,QQ浏览器等浏览器都支持直接播放HLS流。PC机的谷歌浏览器默认是不支持的。2.10 NGINX配置HTTP文件服务器在5.8小节里介绍了如何配置NGINX保留RTMP推流的视频文件,如果想做一个直播回放,历史记录查看的播放器,那么就可以将rtmp视频缓存的目录作为HTTP文件服务器访问的根目录,通过访问这个根目录获取目录下文件的索引,得到视频文件的访问地址就可以直接进行播放,就能做一个视频回放播放器。在http模块里新增加一个server配置,并填入新的配置,详细内容如下: server { listen 8090; server_name localhost; location / { root ./video; #指定哪个目录作为Http文件服务器的根目录,如果你这里写了file就是你的根目录,那么访问的时候file就不会出现在目录中 autoindex on; #设置允许列出整个目录 autoindex_exact_size off; #默认为on,显示出文件的确切大小,单位是bytes。改为off后,显示出文件的大概大小,单位是kB或者MB或者GB autoindex_localtime on; #默认为off,显示的文件时间为GMT时间。改为on后,显示的文件时间为文件的服务器时间 charset utf-8; #防止文件乱码显示, 如果用utf-8还是乱码,就改成gbk试试 } }特别说明: nginx是支持配置多个server配置,监听不同的端口,可以给文件服务器单独设置一个监听端口,专门作为文件遍历使用。/usr/local/nginx/conf/nginx.conf文件的完整的配置如下:worker_processes 1; #Nginx进程数,建议设置为等于CPU总核数 events { worker_connections 1024; #工作模式与连接数上限 } rtmp_auto_push on; #RTMP服务 rtmp { server { listen 8888; application live { live on; #开启实时 record all; record_unique on; record_path "./video"; #视频缓存的路径 record_suffix -%Y-%m-%d-%H_%M_%S.flv; hls on; #开启hls hls_path ./m3u8File; #hls的ts切片存放路径 hls_fragment 2s; #本地切片长度 hls_playlist_length 6s;#HLS播放列表长度 } } } #HTTP服务 http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 8090; server_name localhost; location / { root ./video; #指定哪个目录作为Http文件服务器的根目录,如果你这里写了file就是你的根目录,那么访问的时候file就不会出现在目录中 autoindex on; #设置允许列出整个目录 autoindex_exact_size off; #默认为on,显示出文件的确切大小,单位是bytes。改为off后,显示出文件的大概大小,单位是kB或者MB或者GB autoindex_localtime on; #默认为off,显示的文件时间为GMT时间。改为on后,显示的文件时间为文件的服务器时间 charset utf-8; #防止文件乱码显示, 如果用utf-8还是乱码,就改成gbk试试 } } server { listen 8099; server_name localhost; location / { root html; index index.html index.htm; } location /live_hls{ types{ #m3u8 type设置 application/vnd.apple.mpegurl m3u8; #ts分片文件设置 video/mp2t ts; } #指向访问m3u8文件目录 alias ./m3u8File; add_header Cache-Control no-cache; #禁止缓存 } location /control{ rtmp_control all; } location /stat{ rtmp_stat all; rtmp_stat_stylesheet stat.xsl; } location /stat.xsl{ root ./nginx-rtmp-module-master; } # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }访问文件测试: http://127.0.0.1:80903. Linux下开机启动的执行流程3.1 开机执行脚本顺序第一步:init /etc/inittab 第二步:启动相应的脚本,并且打开终端 rc.sysinit rc.d(里面的脚本) rc.local 第三步:启动login登录界面 login 第四步:在用户登录的时候执行sh脚本的顺序,每次登录的时候都会完全执行的 /etc/profile.d/file /etc/profile /etc/bashrc /root/.bashrc /root/.bash_profile3.2 Linux中修改环境变量及生效方法修改/etc/profile 或者 /etc/bashrc 可以让环境变量全部用户全局生效(需要重启系统)。修改~/.bash_profile 或~/.bashrc对当前用户全局有效(需要重启系统)。如果需要立即生效,修改完之后用source命令执行,如:source .bash_profile3.3 rcX.d的启动级别一般有开机自启动的需求时,一般会在/etc/rc.local文件中写命令行或脚本执行命令的方式来实现。也可以在/etc/profile文件里实现(不建议)。现在很多Linux发行版,默认是没有/etc/rc.local这个文件或者没有去执行,而使用的是/etc/rcX.d。rcX.d并不是指这个目录或者文件就是叫rcX.d,这其中的X对应是0~6这7个数字,不同的数字对应着不同的级别查看当前系统/etc/rcX.d目录:[root@ecs-c687-ecrs work]# ls /etc/ | grep rc bashrc csh.cshrc inputrc mail.rc rc0.d rc1.d rc2.d rc3.d rc4.d rc5.d rc6.d rc.d rc.local vimrc virc wgetrc通过runlevel命令查看当前系统的启动级别:启动级别(X所代表的数字)启动级别所代表的含义0停机(不能使用)1单用户模式2多用户模式,但不能使用NFS等3完整的多用户模式4系统保留(未使用)5图形化界面模式6重启模式(不能使用)我当前使用的是CentOS7.4服务器版本,启动级别如下:[root@ecs-c687-ecrs ]# runlevel N 3查看/etc/rc3.d/目录下文件的详细信息:[root@ecs-c687-ecrs ~]# ls /etc/rc3.d/ -l total 0 lrwxrwxrwx. 1 root root 20 Feb 14 2022 K50netconsole -> ../init.d/netconsole lrwxrwxrwx. 1 root root 17 Feb 14 2022 K90network -> ../init.d/network lrwxrwxrwx 1 root root 19 Sep 15 22:07 S12hostguard -> ../init.d/hostguard lrwxrwxrwx 1 root root 24 Feb 14 2022 S50multi-queue-hw -> ../init.d/multi-queue-hw可以看到该目录下的文件都是链接文件,而且都是指向/etc/init.d中的shell脚本或者其他可执行文件,它们的命名方式基本都是以S或者K开头,其后紧跟一个数字,数字后则是链接文件的名字,这个名字可以自行定义。命名规则解释如下:以K90network为例: K表示stop,S表示start。(表示需要传入参数),也就是说开机自启动命令会向脚本传入start或者stop,在脚本里可以收到参数$1进行一些判断,完成一些不同情况下的逻辑处理。比如:开机执行什么代码,关机执行什么代码。 90 表示脚本执行等级。(通常越小越优先) network与/etc/init.d下的脚本文件名称保持一致。3.4 利用rcX.d实现开机自动执行脚本比如:需求是开机之后创建一个文件,并向文件里存放一些数据。(1)先在/etc/init.d目录下创建一个up_demo.sh脚本,编写脚本代码:#!/bin/bash echo $0 $1 >> /home/up_test.txt修改脚本权限:[root@ecs-c687-ecrs init.d]# chmod 777 /etc/init.d/up_demo.sh [root@ecs-c687-ecrs init.d]# ls up_demo.sh -l -rwxrwxrwx 1 root root 76 Sep 16 14:13 up_demo.sh(2) 在/etc/rc3.d目录里,创建软连接。 (因为我的系统启动级别为3)[root@ecs-c687-ecrs rc3.d]# ln -s /etc/init.d/up_demo.sh S10up_demo [root@ecs-c687-ecrs rc3.d]# ls -l total 0 lrwxrwxrwx. 1 root root 20 Feb 14 2022 K50netconsole -> ../init.d/netconsole lrwxrwxrwx. 1 root root 17 Feb 14 2022 K90network -> ../init.d/network lrwxrwxrwx 1 root root 22 Sep 16 14:17 S10up_demo -> /etc/init.d/up_demo.sh lrwxrwxrwx 1 root root 19 Sep 15 22:07 S12hostguard -> ../init.d/hostguard lrwxrwxrwx 1 root root 24 Feb 14 2022 S50multi-queue-hw -> ../init.d/multi-queue-hw(3)重启系统,进入到/home目录下查看文件内容,可以看到开机启动成功,内容已经写到up_test.txt文件里了。 Welcome to Huawei Cloud Service [root@ecs-c687-ecrs ~]# cd /home/ [root@ecs-c687-ecrs home]# ls lib_run.sh up_test.txt video work work_pc work.tar.gz [root@ecs-c687-ecrs home]# cat up_test.txt /etc/rc.d/init.d/up_demo.sh start [root@ecs-c687-ecrs home]#
  • [技术干货] 华为云ECS服务器部署_CentOS7.4服务器安装NFS,ffmpeg、Qt环境
    1. 环境介绍环境介绍:采用的是华为云的ECS弹性云服务器--镜像安装的CentOS7.4 64位 -----是服务器版,非桌面版哦。在CentOS7.4服务器版本的环境下搭建NFS服务器、安装ffmpeg、安装nginx服务器、部署Qt编译环境。(1)配置NGINX为HTTP服务器,安装rtmp模块,完成rtmp视频推流,支持缓存视频到本地目录、支持转为HLS流,通过浏览器访问直播流。(2)部署Qt编译环境,可以编译Qt代码,运行Qt程序。 因为是服务器,我这里部署Qt环境,运行Qt程序是属于控制台版本,不用Qt的GUI模块,只是用核心模块完成一些功能设计。(3)安装、编译 ffmpeg : 支持源码编译安装ffmpeg、在线安装ffmpeg,通过ffmpeg可以完成音视频的一些开发。比如:视频转码、视频合并、视频剪切、视频特效处理。----这里用ffmpeg并不是做播放器,是在服务器上用,主要是处理视频文件。(4)安装NFS服务器,主要是方便与本地Linux系统进行通信,编译代码,文件共享。(5)部署开机自动启动程序:方便设置自己的程序为开机启动。2. 部署NFS服务器注意:在云服务器上搭建NFS服务器,需要在控制台开放端口,不然NFS的端口无法正常访问,会被拦截。2.1 安装NFS[root@ecs-c687-ecrs work]# yum install -y rpc-bind nfs-utils Loaded plugins: fastestmirror Determining fastest mirrors base | 3.6 kB 00:00:00 epel | 4.7 kB 00:00:00 extras | 2.9 kB 00:00:00 updates | 2.9 kB 00:00:00 (1/7): base/7/x86_64/group_gz | 153 kB 00:00:00 (2/7): epel/x86_64/group_gz | 97 kB 00:00:00 (3/7): epel/x86_64/updateinfo | 1.0 MB 00:00:00 (4/7): base/7/x86_64/primary_db | 6.1 MB 00:00:00 (5/7): epel/x86_64/primary_db | 7.0 MB 00:00:00 (6/7): extras/7/x86_64/primary_db | 250 kB 00:00:00 (7/7): updates/7/x86_64/primary_db | 17 MB 00:00:01 No package rpc-bind available. Resolving Dependencies --> Running transaction check ---> Package nfs-utils.x86_64 1:1.3.0-0.68.el7.2 will be installed --> Processing Dependency: gssproxy >= 0.7.0-3 for package: 1:nfs-utils-1.3.0-0.68.el7.2.x86_64 --> Processing Dependency: rpcbind for package: 1:nfs-utils-1.3.0-0.68.el7.2.x86_64 --> Processing Dependency: quota for package: 1:nfs-utils-1.3.0-0.68.el7.2.x86_64 --> Processing Dependency: libnfsidmap for package: 1:nfs-utils-1.3.0-0.68.el7.2.x86_64 --> Processing Dependency: libevent for package: 1:nfs-utils-1.3.0-0.68.el7.2.x86_64 --> Processing Dependency: keyutils for package: 1:nfs-utils-1.3.0-0.68.el7.2.x86_64 --> Processing Dependency: libnfsidmap.so.0()(64bit) for package: 1:nfs-utils-1.3.0-0.68.el7.2.x86_64 --> Processing Dependency: libevent-2.0.so.5()(64bit) for package: 1:nfs-utils-1.3.0-0.68.el7.2.x86_64 --> Running transaction check ---> Package gssproxy.x86_64 0:0.7.0-30.el7_9 will be installed --> Processing Dependency: libini_config >= 1.3.1-31 for package: gssproxy-0.7.0-30.el7_9.x86_64 --> Processing Dependency: libverto-module-base for package: gssproxy-0.7.0-30.el7_9.x86_64 --> Processing Dependency: libref_array.so.1(REF_ARRAY_0.1.1)(64bit) for package: gssproxy-0.7.0-30.el7_9.x86_64 --> Processing Dependency: libini_config.so.3(INI_CONFIG_1.2.0)(64bit) for package: gssproxy-0.7.0-30.el7_9.x86_64 --> Processing Dependency: libini_config.so.3(INI_CONFIG_1.1.0)(64bit) for package: gssproxy-0.7.0-30.el7_9.x86_64 --> Processing Dependency: libref_array.so.1()(64bit) for package: gssproxy-0.7.0-30.el7_9.x86_64 --> Processing Dependency: libini_config.so.3()(64bit) for package: gssproxy-0.7.0-30.el7_9.x86_64 --> Processing Dependency: libcollection.so.2()(64bit) for package: gssproxy-0.7.0-30.el7_9.x86_64 --> Processing Dependency: libbasicobjects.so.0()(64bit) for package: gssproxy-0.7.0-30.el7_9.x86_64 ---> Package keyutils.x86_64 0:1.5.8-3.el7 will be installed ---> Package libevent.x86_64 0:2.0.21-4.el7 will be installed ---> Package libnfsidmap.x86_64 0:0.25-19.el7 will be installed ---> Package quota.x86_64 1:4.01-19.el7 will be installed --> Processing Dependency: quota-nls = 1:4.01-19.el7 for package: 1:quota-4.01-19.el7.x86_64 --> Processing Dependency: tcp_wrappers for package: 1:quota-4.01-19.el7.x86_64 ---> Package rpcbind.x86_64 0:0.2.0-49.el7 will be installed --> Running transaction check ---> Package libbasicobjects.x86_64 0:0.1.1-32.el7 will be installed ---> Package libcollection.x86_64 0:0.7.0-32.el7 will be installed ---> Package libini_config.x86_64 0:1.3.1-32.el7 will be installed --> Processing Dependency: libpath_utils.so.1(PATH_UTILS_0.2.1)(64bit) for package: libini_config-1.3.1-32.el7.x86_64 --> Processing Dependency: libpath_utils.so.1()(64bit) for package: libini_config-1.3.1-32.el7.x86_64 ---> Package libref_array.x86_64 0:0.1.5-32.el7 will be installed ---> Package libverto-libevent.x86_64 0:0.2.5-4.el7 will be installed ---> Package quota-nls.noarch 1:4.01-19.el7 will be installed ---> Package tcp_wrappers.x86_64 0:7.6-77.el7 will be installed --> Running transaction check ---> Package libpath_utils.x86_64 0:0.2.1-32.el7 will be installed --> Finished Dependency Resolution Dependencies Resolved ============================================================================================================================================================= Package Arch Version Repository Size ============================================================================================================================================================= Installing: nfs-utils x86_64 1:1.3.0-0.68.el7.2 updates 413 k Installing for dependencies: gssproxy x86_64 0.7.0-30.el7_9 updates 111 k keyutils x86_64 1.5.8-3.el7 base 54 k libbasicobjects x86_64 0.1.1-32.el7 base 26 k libcollection x86_64 0.7.0-32.el7 base 42 k libevent x86_64 2.0.21-4.el7 base 214 k libini_config x86_64 1.3.1-32.el7 base 64 k libnfsidmap x86_64 0.25-19.el7 base 50 k libpath_utils x86_64 0.2.1-32.el7 base 28 k libref_array x86_64 0.1.5-32.el7 base 27 k libverto-libevent x86_64 0.2.5-4.el7 base 8.9 k quota x86_64 1:4.01-19.el7 base 179 k quota-nls noarch 1:4.01-19.el7 base 90 k rpcbind x86_64 0.2.0-49.el7 base 60 k tcp_wrappers x86_64 7.6-77.el7 base 78 k Transaction Summary ============================================================================================================================================================= Install 1 Package (+14 Dependent packages) Total download size: 1.4 M Installed size: 4.1 M Downloading packages: (1/15): keyutils-1.5.8-3.el7.x86_64.rpm | 54 kB 00:00:00 (2/15): libcollection-0.7.0-32.el7.x86_64.rpm | 42 kB 00:00:00 (3/15): gssproxy-0.7.0-30.el7_9.x86_64.rpm | 111 kB 00:00:00 (4/15): libevent-2.0.21-4.el7.x86_64.rpm | 214 kB 00:00:00 (5/15): libbasicobjects-0.1.1-32.el7.x86_64.rpm | 26 kB 00:00:00 (6/15): libini_config-1.3.1-32.el7.x86_64.rpm | 64 kB 00:00:00 (7/15): libnfsidmap-0.25-19.el7.x86_64.rpm | 50 kB 00:00:00 (8/15): libpath_utils-0.2.1-32.el7.x86_64.rpm | 28 kB 00:00:00 (9/15): libref_array-0.1.5-32.el7.x86_64.rpm | 27 kB 00:00:00 (10/15): libverto-libevent-0.2.5-4.el7.x86_64.rpm | 8.9 kB 00:00:00 (11/15): quota-nls-4.01-19.el7.noarch.rpm | 90 kB 00:00:00 (12/15): quota-4.01-19.el7.x86_64.rpm | 179 kB 00:00:00 (13/15): tcp_wrappers-7.6-77.el7.x86_64.rpm | 78 kB 00:00:00 (14/15): rpcbind-0.2.0-49.el7.x86_64.rpm | 60 kB 00:00:00 (15/15): nfs-utils-1.3.0-0.68.el7.2.x86_64.rpm | 413 kB 00:00:00 ------------------------------------------------------------------------------------------------------------------------------------------------------------- Total 1.5 MB/s | 1.4 MB 00:00:00 Running transaction check Running transaction test Transaction test succeeded Running transaction Warning: RPMDB altered outside of yum. Installing : rpcbind-0.2.0-49.el7.x86_64 1/15 Installing : libbasicobjects-0.1.1-32.el7.x86_64 2/15 Installing : libref_array-0.1.5-32.el7.x86_64 3/15 Installing : libcollection-0.7.0-32.el7.x86_64 4/15 Installing : libevent-2.0.21-4.el7.x86_64 5/15 Installing : libverto-libevent-0.2.5-4.el7.x86_64 6/15 Installing : 1:quota-nls-4.01-19.el7.noarch 7/15 Installing : tcp_wrappers-7.6-77.el7.x86_64 8/15 Installing : 1:quota-4.01-19.el7.x86_64 9/15 Installing : keyutils-1.5.8-3.el7.x86_64 10/15 Installing : libnfsidmap-0.25-19.el7.x86_64 11/15 Installing : libpath_utils-0.2.1-32.el7.x86_64 12/15 Installing : libini_config-1.3.1-32.el7.x86_64 13/15 Installing : gssproxy-0.7.0-30.el7_9.x86_64 14/15 Installing : 1:nfs-utils-1.3.0-0.68.el7.2.x86_64 15/15 Verifying : gssproxy-0.7.0-30.el7_9.x86_64 1/15 Verifying : 1:quota-4.01-19.el7.x86_64 2/15 Verifying : libpath_utils-0.2.1-32.el7.x86_64 3/15 Verifying : libnfsidmap-0.25-19.el7.x86_64 4/15 Verifying : libevent-2.0.21-4.el7.x86_64 5/15 Verifying : keyutils-1.5.8-3.el7.x86_64 6/15 Verifying : libverto-libevent-0.2.5-4.el7.x86_64 7/15 Verifying : tcp_wrappers-7.6-77.el7.x86_64 8/15 Verifying : libcollection-0.7.0-32.el7.x86_64 9/15 Verifying : 1:quota-nls-4.01-19.el7.noarch 10/15 Verifying : libref_array-0.1.5-32.el7.x86_64 11/15 Verifying : libbasicobjects-0.1.1-32.el7.x86_64 12/15 Verifying : 1:nfs-utils-1.3.0-0.68.el7.2.x86_64 13/15 Verifying : libini_config-1.3.1-32.el7.x86_64 14/15 Verifying : rpcbind-0.2.0-49.el7.x86_64 15/15 Installed: nfs-utils.x86_64 1:1.3.0-0.68.el7.2 Dependency Installed: gssproxy.x86_64 0:0.7.0-30.el7_9 keyutils.x86_64 0:1.5.8-3.el7 libbasicobjects.x86_64 0:0.1.1-32.el7 libcollection.x86_64 0:0.7.0-32.el7 libevent.x86_64 0:2.0.21-4.el7 libini_config.x86_64 0:1.3.1-32.el7 libnfsidmap.x86_64 0:0.25-19.el7 libpath_utils.x86_64 0:0.2.1-32.el7 libref_array.x86_64 0:0.1.5-32.el7 libverto-libevent.x86_64 0:0.2.5-4.el7 quota.x86_64 1:4.01-19.el7 quota-nls.noarch 1:4.01-19.el7 rpcbind.x86_64 0:0.2.0-49.el7 tcp_wrappers.x86_64 0:7.6-77.el7 Complete! [root@ecs-c687-ecrs work]# yum install -y rpcbind Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile Package rpcbind-0.2.0-49.el7.x86_64 already installed and latest version Nothing to do [root@ecs-c687-ecrs work]#2.2 设置节点(1)设置节点名称hostnamectl set-hostname nfs(2)服务端安装NFSyum -y install nfs-utils rpcbind2.3 服务端配置在NFS服务端上创建共享目录/data/nfs并设置权限mkdir -p /data/nfs chmod 666 /data/nfs2.4 编辑export文件vim /etc/exports /data/nfs *(rw,no_root_squash,no_all_squash,sync) 其中的/data/nfs 就是NFS服务器需要共享出去的目录。 *号表示所有IP地址都可以访问。 参数介绍: rw 读写权限 no_root_squash 客户端使用 root 身份来操作服务器的文件系统 sync 代表数据会同步写入到内存与硬盘中(1)配置生效exportfs -r(2)启动rpcbind、nfs服务systemctl enable rpcbind && systemctl start rpcbind systemctl enable nfs && systemctl start nfs(3)查看 RPC 服务的注册状况rpcinfo -p localhost program vers proto port service 100000 4 tcp 111 portmapper 100000 3 tcp 111 portmapper 100000 2 tcp 111 portmapper 100000 4 udp 111 portmapper 100000 3 udp 111 portmapper 100000 2 udp 111 portmapper 100024 1 udp 39748 status 100024 1 tcp 41475 status 100005 1 udp 20048 mountd 100005 1 tcp 20048 mountd 100005 2 udp 20048 mountd 100005 2 tcp 20048 mountd 100005 3 udp 20048 mountd 100005 3 tcp 20048 mountd 100003 3 tcp 2049 nfs 100003 4 tcp 2049 nfs 100227 3 tcp 2049 nfs_acl 100003 3 udp 2049 nfs 100003 4 udp 2049 nfs 100227 3 udp 2049 nfs_acl 100021 1 udp 46720 nlockmgr 100021 3 udp 46720 nlockmgr 100021 4 udp 46720 nlockmgr 100021 1 tcp 33605 nlockmgr 100021 3 tcp 33605 nlockmgr 100021 4 tcp 33605 nlockmgr(4)showmount令来查阅是否可以联机showmount -e localhost 选项与参数: -a :显示目前主机与客户端的 NFS 联机分享的状态; -e :显示某部主机的 /etc/exports 所分享的目录数据。2.5 客户端配置(1)安装nfs-utils客户端 ---一般系统都自带了,不用安装。yum -y install nfs-utils(2)创建挂载目录---就是用来挂载NFS服务器的共享目录mkdir /home/work(3)查看服务器抛出的共享目录信息showmount -e (4)挂载服务器共享目录mount -t nfs : <挂载到本地的路径>(5)查看挂载结果df -h(6)卸载已挂在的NFSumount <挂载到本地的路径>3. 安装ffmpeg3.1 安装yasmwget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz #下载源码包 tar zxvf yasm-1.3.0.tar.gz #解压 cd yasm-1.3.0 #进入目录 ./configure #配置 make && make install #编译安装3.2 在线安装ffmpeg(1)设置下载源CentOS没有官方FFmpeg软件包,可以使用第三方YUM源(Nux Dextop)下载,就是版本较低,ffmpeg 2.8。(1) yum -y install epel-release (2) yum update -y (3) rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro (4) [CentOS 7] rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm [CentOS 6] sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm(2)安装FFmpeg 和 FFmpeg开发包sudo yum install ffmpeg ffmpeg-devel -y(3)测试ffmpeg是否安装OKffmpeg -version查看版本:[root@nfs ecrs_web_lib_centos]# ffmpeg -version ffmpeg version 2.8.15 Copyright (c) 2000-2018 the FFmpeg developers built with gcc 4.8.5 (GCC) 20150623 (Red Hat 4.8.5-36) configuration: --prefix=/usr --bindir=/usr/bin --datadir=/usr/share/ffmpeg --incdir=/usr/include/ffmpeg --libdir=/usr/lib64 --mandir=/usr/share/man --arch=x86_64 --optflags='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic' --extra-ldflags='-Wl,-z,relro ' --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libvo-amrwbenc --enable-version3 --enable-bzlib --disable-crystalhd --enable-gnutls --enable-ladspa --enable-libass --enable-libcdio --enable-libdc1394 --enable-libfdk-aac --enable-nonfree --disable-indev=jack --enable-libfreetype --enable-libgsm --enable-libmp3lame --enable-openal --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-libschroedinger --enable-libsoxr --enable-libspeex --enable-libtheora --enable-libvorbis --enable-libv4l2 --enable-libx264 --enable-libx265 --enable-libxvid --enable-x11grab --enable-avfilter --enable-avresample --enable-postproc --enable-pthreads --disable-static --enable-shared --enable-gpl --disable-debug --disable-stripping --shlibdir=/usr/lib64 --enable-runtime-cpudetect libavutil 54. 31.100 / 54. 31.100 libavcodec 56. 60.100 / 56. 60.100 libavformat 56. 40.101 / 56. 40.101 libavdevice 56. 4.100 / 56. 4.100 libavfilter 5. 40.101 / 5. 40.101 libavresample 2. 1. 0 / 2. 1. 0 libswscale 3. 1.101 / 3. 1.101 libswresample 1. 2.101 / 1. 2.101 libpostproc 53. 3.100 / 53. 3.100 [root@nfs ecrs_web_lib_centos]# 3.3 从源码编译ffmpeg(1)编译X264 [root@ecs-c687-ecrs work_pc]# tar xvf last_x264.tar.bz2 ./configure make make install (2)编译ffmpeg ./configure --enable-shared --target-os=linux --enable-gpl --enable-ffmpeg --enable-libx264 3.4 如果有多个ffmpeg版本存在怎么办?如果系统里有多个ffmpeg存在,那么在命令行执行ffmpeg肯定是不知道当前这个命令是不是自己想要的。如果是编译ffmpeg源码得到ffmpeg相关文件,那么在执行./configure时,加一个选项,设置一下安装路径--prefix=$PWD/_install 。这样执行make install时,安装的文件会拷贝到当前目录下的_install目录里。然后再通过ln建立一个链接文件到/usr/local/bin/目录下。ln -s /home/wbyq/ffmpe4.2.2/_install/bin/ffmpeg /usr/local/bin/ffmpeg422然后将/home/wbyq/ffmpe4.2.2/_install/lib目录加到系统库搜索的环境变量里。export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/wbyq/ffmpe4.2.2/_install/lib如果觉得设置环境变量麻烦,以后也不常修改这些库,那么可以将ffmpeg的全部.so库拷贝到/usr/lib目录下去。cp /home/wbyq/ffmpe4.2.2/_install/lib/* /usr/lib -dfv说明:拷贝动态库最好都加上-d参数,保留库之间的链接属性。拷贝之后再执行ldconfig命令生效,也就是刷新缓存文件,让系统知道你更改了/usr/lib目录。4. 编译zlib4.1 编译zlibwget http://www.zlib.net/zlib-1.2.12.tar.gz #下载zlib tar zxvf zlib-1.2.12.tar.gz #解压 cd zlib-1.2.12 #进入目录 ./configure #配置 make && make install #编译安装4.2 安装g++编译器如果需要编译C++代码,需提前安装g++编译器,默认只有gcc编译器。yum -y install gcc-c++4.3 编译quazipwget https://udomain.dl.sourceforge.net/project/quazip/quazip/0.7.3/quazip-0.7.3.zip #下载quazip unzip quazip-0.7.3.zip #解压 cd quazip-0.7.3 #进入目录 qmake #生成Makefile make #编译说明: quazip是Qt的工程,需要使用qmake进行编译。如果是桌面版Linux那就很简单的,直接在Qt官网上下载xxx.run安装包,在命令行运行就可以安装。那如果在Linux服务器上如何安装Qt的编译环境?因为可能有这么个需求,需要在Linux服务器命令行上编译Qt代码。 这种情况下就需要搭建一个Qt编译环境。**解决办法: **将桌面版上安装好的Qt目录下,对应的编译器套件目录打包拷贝到Linux服务器上解码即可使用。比如:我在ubuntu18.04上安装了Qt5.12.6,我的安装目录:/home/wbyq/Qt5.12.6, 那么将/home/wbyq/Qt5.12.6/5.12.6/gcc_64/目录打包即可。下面是/home/wbyq/Qt5.12.6/5.12.6/gcc_64/目录下的文件。wbyq@wbyq:~/work$ ls /home/wbyq/Qt5.12.6/5.12.6/gcc_64/ bin doc include lib mkspecs phrasebooks plugins qml translations打包压缩的命令:tar czvf gcc_64.tar.gz /home/wbyq/Qt5.12.6/5.12.6/gcc_64打包之后生成的文件: gcc_64.tar.gz ,拷贝到Linux服务器上解压,然后将bin目录下的qmake文件建立一个软链接文件到/usr/local/bin/目录下。ln -s /home/work/gcc_64/bin/qmake /usr/local/bin/qmake前面的目录/home/work/gcc_64/bin/qmake 是我在服务器上解压的路径。然后就可以在Qt的工程目录下使用qmake生成Makefile文件,然后执行make即可编译Qt程序了。如果编译好的Qt程序无法正常的运行,缺少库,需要告诉系统你的Qt动态库在哪里。export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/work/gcc_64/lib这行命令在当前命令行执行只是当前终端进程有效,如果想要全局生效,将这行代码加到/etc/profile文件最后,然后执行reoot命令重启系统即可。
  • [技术干货] Linux系统下基础命令介绍
    Linux系统与终端基础命令介绍Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。Linux操作系统诞生于1991 年10 月5 日(这是第一次正式向外公布时间)。Linux存在着许多不同的Linux版本,但它们都使用了Linux内核。Linux可安装在各种计算机硬件设备中,比如手机、平板电脑、路由器、视频游戏控制台、台式计算机、大型机和超级计算机。1.1 Linux目录结构1​ /bin 该目录中存放Linux的常用命令,在有的版本中是一些和根目录下相同的目录。2​ /boot 该目录下存放的都是系统启动时要用到的程序,当用lilo引导Linux时,会用到这里的一些信息。3​ /dev 该目录包含了Linux系统中使用的所有外部设备,它实际上是访问这些外部设备的端口,你可以访问这些外部设备,与访问一个文件或一个目录没有区别。例如在系统中键入“cd /dev/cdrom”,就可以看到光驱中的文件;键入“cd /dev/mouse”即可看鼠标的相关文件。4​ /etc 该目录存放了系统管理时要用到的各种配置文件和子目录,例如网络配置文件、文件系统、X系统配置文件、设备配置信息、设置用户信息等。5​ /sbin 该目录用来存放系统管理员的系统管理程序。6​ /home 如果建立一个名为“xx”的用户,那么在/home目录下就有一个对应的“/home/xx”路径,用来存放该用户的主目录。7​ /lib 该目录用来存放系统动态连接共享库,几乎所有的应用程序都会用到该目录下的共享库 。8​ /lost+found 该目录在大多数情况下都是空的。但当突然停电、或者非正常关机后,有些文件就临时存放在这里。9​ /mnt 该目录在一般情况下也是空的,你可以临时将别的文件系统挂在该目录下。10​ /proc 可以在该目录下获取系统信息,这些信息是在内存中由系统自己产生的11​ /root 如果你是以超级用户的身份登录的,这个就是超级用户的主目录12​ /tmp 用来存放不同程序执行时产生的临时文件。13​ /usr 用户的很多应用程序和文件都存放在该目录下。14​ /usr/X11R6:X-Window目录;15​ /usr/src:Linux源代码;16​ /usr/include:系统头文件;17​ /usr/lib:存放常用动态链接共享库、静态档案库;1.2​ Linux基本命令介绍1.2.1 su命令:切换用户。语法:su [用户名] ([]表示可选)linux下有两种帐号:1.root--超级用户帐号(系统管理员),使用这个帐号可以在系统中做任何事情。2.普通用户--这个帐号供普通用户使用,可以进行有限的操作。su命令的常见用法是变成超级用户,如果普通用户发出不带用户名的su命令 ,则系统提示输入根口令,输入之后则可切换为根用户。useradd testuser 创建用户testuserpasswd testuser 给已创建的用户testuser设置密码说明:新创建的用户会在/home下创建一个用户目录testuserusermod --help 修改用户这个命令的相关参数userdel testuser 删除用户testuserrm -rf testuser 删除用户testuser所在目录1.2.2 ls命令:遍历目录功能:ls是英文单词list的简写,其功能为列出目录的内容。这是用户最常用的一个命令之一,因为用户需要不时地查看某个目录的内容。该命令类似于DOS下的dir命令。语法:ls [选项] [目录或是文件]说明:对于每个目录,该命令将列出其中的所有子目录与文件。对于每个文件,ls将输出其文件名以及所要求的其他信息。默认情况下,输出条目按字母顺序排序。当未给出目录名或是文件名时,就显示当前目录的信息。常用选项:- a显示指定目录下所有子目录与文件,包括隐藏文件。- A显示指定目录下所有子目录与文件,包括隐藏文件。但不列出“.”和 “..”。- i在输出的第一列显示文件的i节点号。- l以长格式来显示文件的详细信息。这个选项最常用。- p在目录后面加一个“/”。- R递归式地显示指定目录的各个子目录中的文件。-t按照时间进行排序-S按照文件大小排序-s列出文件时显示文件的大小。如果加上-h参数可合理的显示大小单位。例如: ls -sh-F在文件末尾加上一个字符,代表该文件的类型。例如: *表示可执行,/表示目录,=表示套接字​ 用ls - l命令显示的信息中,开头是由10个字符构成的字符串,其中第一个字符表示文件类型:- 普通文件、d 目录 、l 符号链接、b 块设备文件、c 字符设备文件、p 命名管道(FIFO)、s socket文件​ 后面的9个字符表示文件的访问权限,分为3组,每组3位。第一组表示文件所有者的权限,第二组表示同组用户的权限,第三组表示其他用户的权限。每一组的三个字符分别表示对文件的读、写和执行权限。​ 各权限如下所示:r 读 (4)w 写 (2)x 执行 (1) 对于目录,表示进入权限。- 没有设置权限。1.2.3 man命令:查看帮助功能:用于查看命令、函数、头文件的使用帮助信息。用法: man [页码] <命令、函数名称、头文件名称>页码一般为1-7页。如果当前页看不到想要的信息,可以切换到其他页查看。1.2.4 cd命令:切换目录功能:切换目录语法:cd [目录路径]说明:该命令将当前工作目录切换至指定的目录。若没有指定目录路径, 则回到用户的主目录~。为了改变到指定目录,用户必须拥有对指定目录的执行和读权限。常用的目录表示符号:cd .. 到父目录,即上一级目录,相当于“向上”cd - 到上一次目录,相当于“后退”cd / 到根目录cd ~或者只写cd 回到到用户主目录下1.2.5 mkdir命令:创建目录功能:创建一个目录语法:mkdir [选项] dirname说明:该命令创建由dirname命名的目录。要求创建目录的用户在当前目录中 (dirname的父目录中)具有写权限,并且dirname不能是当前目录中已有的目录或 文件名称。参数:- m 对新建目录设置存取权限。也可以用chmod命令设置。- p 可以是一个路径名称。此时若路径中的某些目录尚不存在, 加上此选项后, 系统将自动建立好那 些尚不存在的目录,即一次可以建立多个目录。创建多层目录示例:mkdir ./dir1/dir2/dir3 -p1.2.6 touch命令:创建普通文件功能:创建一个文件。语法:touch [文件名称]1.2.7 rm命令:删除文件/目录功能:在linux中创建文件很容易,系统中随时会有文件变得过时且毫无用处。用户可以用rm命令将其删除。该命令的功能为删除一个目录中的一个或多个文件或目录,它也可以将某个目录及其下的所有文件及子目录均删除。对于链接文件,只是删除了链接,原有文件均保持不变。语法:rm [选项] <文件或者目录>说明:如果没有使用- r选项,则rm不会删除目录。参数:- f 忽略不存在的文件,从不给出提示。- r 指示rm将参数中列出的全部目录和子目录均递归地删除。- i 进行交互式删除。-v 输出已经删除的文件使用rm命令要格外小心。因为一旦一个文件被删除,它是不能被恢复的。为了防止此种情况的发生,可以使用rm命令中的 -i选项来确认要删除的每个文件。如果用户输入y,文件将被删除。如果输入任何其他东西,文件将被保留。1.2.8 cat命令:查看文件内容功能:查看文件内容语法:cat [选项] [文件]参数:-b 对非空输出行编号-E 在每行结束处显示$-n 对输出的所有行编号-s 不输出多行空行1.2.9 pwd命令:显示工作目录功能:在Linux层次目录结构中,用户可以在被授权的任意目录下利用mkdir命令创建新目录,也可以利用cd命令从一个目录转换到另一个目录。然而,没有提示符来告知用户目前处于哪一个目录中。要想知道当前所处的目录,可以使用pwd命令,该命令显示整个路径名。语法:pwd说明:此命令显示出当前工作目录的绝对路径。举例:pwd根目录以开头的“/”表示。如果pwd后面什么都没有,则显示当前所在位置。如果屏幕信息很多,用clear可以清除。1.2.10 cp命令:拷贝文件/目录功能:将给出的文件或目录拷贝到另一文件或目录中。语法:cp [选项] <源文件或目录> <目标文件或目录>说明:该命令把指定的源文件复制到目标文件或把多个源文件复制到目标目录中。参数:- a 该选项通常在拷贝目录时使用。它保留链接、文件属性,并递归地拷贝目录。- d 拷贝时保留链接。- f 删除已经存在的目标文件而不提示。- i 和f选项相反,在覆盖目标文件之前将给出提示要求用户确认。回答y时目标文件将被覆盖,是交互式拷贝。- r 若给出的源文件是一目录文件,此时cp将递归复制该目录下所有的子目录和文件。此时目标文件必须为一个目录名。一般使用-a参数。举例:# cp -a mjpeg/ socket/ 将mjpeg目录下的所有文件递归方式拷贝到socket目录下# cp -a 123.c 456.c 将123.c文件的内容拷贝到456.c文件中。1.2.11 mv命令:改名、移动、文件/目录功能:为文件或目录改名或将文件由一个目录移入另一个目录中。语法:mv [选项] <源文件或目录> <目标文件或目录>说明:视mv命令中第二个参数类型的不同(是目标文件还是目标目录),mv命令将文件重命名或将其移至一个新的目录中。当第二个参数类型是文件时,mv命令完成文件重命名,此时,源文件只能有一个(也可以是源目录名),它将所给的源文件或目录重命名为给定的目标文件名。当第二个参数是已存在的目录名称时,源文件或目录参数可以有多个,mv命令将各参数指定的源文件均移至目标目录中。在跨文件系统移动文件时,mv先拷贝,再将原有文件删除,而链至该文件的链接也将丢失。参数:-i 交互方式操作。如果mv操作将导致对已存在的目标文件的覆盖,此时系统询问是否重写,要求用户回答y或n,这样可以避免误覆盖文件。-f 禁止交互操作。在mv操作要覆盖某已有的目标文件时不给任何指示,指定此选项后,i选项将不再起作用。如果所给目标文件(不是目录)已存在,此时该文件的内容将被新文件覆盖。为防止用户在不经意的情况下用mv命令破坏另一个文件,建议用户在使用mv命令移动文件时,最好使用i选项。1.2.12 chmod命令:修改文件/目录权限功能:改变文件或目录的访问权限语法:chmod [权限] <文件或者目录>​ 通过ls -l命令可以查看目录或者文件的详细信息,其中第2~10个字符代表了文件的访问权限,当中的每3个为一组,左边三个字符表示所有者权限,中间3个字符表示与所有者同一组的用户的权限,右边3个字符是其他用户的权限。这三个一组共9个字符,代表的意义如下:r可读权限对文件而言,具有读取文件内容的权限;对目录来说,具有浏览目录的权w可写权限对文件而言,具有新增、修改文件内容的权限;对目录来说,具有删除、移动目录内文件的权限。x可执行权限对文件而言,具有执行文件的权限;对目录了来说该用户具有进入目录的权限。其中的权限可以使用数字的组合方式进行表示:r对应数值4w对应数值2x对应数值1数字设定的关键是取值,一开始许多初学者会被搞糊涂,其实很简单,我们将rwx看成二进制数,如果有则有1表示,没有则有0表示,那么rwx r-x r- -则可以表示成为:111 101 100再将其每三位转换成为一个十进制数,就是754。​ 例如,我们想让123.txt这个文件的权限为:所有者同组用户其他用户可读 r可读 r可读 r可写 w可写 w否 -可执行 x否 -否 -那么,我们先根据上表得到权限串为:rwx-rw-rw--,那么转换成二进制数就是111 110 100,再每三位转换成为一个十进制数,就得到764,因此我们执行命令:[root@xiaolong test_code]# chmod 764 123.txt1.2.13 ln命令:建立符号链接ln连接文件或目录,分为软链接和硬链接。软连接语法:ln -s <源文件> <目标文件> (删除源文件之后,链接变成无效的了),相当于快捷方式。硬链接语法:ln <源文件> <目标文件>(删除源文件之后,目标没有影响)举例:#ln -s a.txt p创建软链接之后通过ls -l命令,可以查看到p带有一个箭头指向a.txt。1.2.14 eog命令:查看图片功能:打开图片浏览器查看图片。语法:eog <图片文件>例如:eog 123.png1.2.15 echo命令:输出调试语句功能:echo命令的功能是在显示器上显示一段文字,一般起到一个提示的作用。语法:echo [参数] <输出的数据>参数:-n :表示在最后输出时不自动换行。​ 说明:使用echo输出字符串时,双引号会自动省略掉。(1)示例:echo "1234567890"echo 1234567890以上两条命令输出的效果一样。(2)示例:如果想要显示符号,需要使用转义符号。echo "\"1234567890"\" 输出的结结果=> "1234567890"1.2.16 重定义到文件在终端中常用的重定义符号为: > 和 >>其中:>符号表示覆盖、>>符号表示追加。示例:echo "12345" > 123.txt //将12345输出到123.txt文件中。echo "12345" >> 123.txt //将12345追加到123.txt文件中。ls / > 123.txt //将ls命令列出的数据输出到123.txt文件中。1.2.17 du命令:查看磁盘/文件的大小功能:查看文件的大小信息。用法: du [参数] <文件名称>常用参数如下:-k 用1024字节单位计算块数。-h 选择合适的单位计算大小。-b 选择字节单位计算大小。1.3 文件编辑器相关命令介绍在linux下常用的文本编辑器命令有: vi、vim、gedit等。其中vi与vim命令是基于命令行的编辑器。gedit命令是基于鼠标键盘的编辑器,类似于windows下的记事本。1.3.1 gedit编辑器介绍gedit编辑器用法示例:# gedit 123.c //创建一个123.c文件打开进行编辑打开的界面如下:设置显示行号:打开文件时,如果需要进入到文件的指定行位置,可以在命令的最后面写上需要进入的行号。示例:# gedit 123.c +88 //表示直接跳转到123.c文件的第88行1.3.2 vim编辑器介绍vim分为两种状态,即命令状态和编辑状态,在命令状态下,所键入的字符系统均作命令来处理,如:q代表退出,而编辑状态则是用来编辑文本的。当你进入vim时,会首先进入命令状态。在命令状态下,按”i”(插入)或”a”(添加)可以进入编辑状态,在编辑状态,按ESC键进入命令状态。​ 插入文本:a 从光标后面开始添加文本A 从光标所在行的末尾开始添加文本i 从光标前面开始插入文本I 从光标所在行的开始处插入文本o 在目前光标所在的下一行处插入新的一行O 在目前光标所在处的上一行插入新的一行s 删除游标所在字符,并进入编辑模式S 删除游标所在的行,并进入编辑模式r 输入字符,取代光标所在的那一个字符R 一直取代光标所在的字符,直到按下 ESC 为止​ 删除与修改:x 删除光标处的字符dd 删除光标所在的整行3dd 删除光标所在行以及下面的两行D或d$ 删除光标到行尾的文本,常用语删除注释语句d^或d0 删除光标到行首的文本​ 光标的移动:h 或 向左方向键(←) → 光标向左移动一个字符j 或 向下方向鍵(↓) → 光标向下移动一个字符k 或 向上方向鍵(↑) → 光标向上移动一个字符l 或 向右方向鍵(→) → 光标向右移动一个字符w 光标往后移一个字b 光标往前移一个字^ 光标移动到行首$ 光标移动到行尾Ctrl+f 向下翻一页 forwardCtrl+b 向上翻一页 backCtrl+d 向下翻半页 downCtrl+u 向上翻半页 upgg 光标定位到文档头G 光标定位到文档尾H 光标定位到当前页首L 光标定位到当前页的最后一行的行首[n]+ 光标向后移动n行,[n]表示一个整数,比如10+[n]- 光标向前移动n行,[n]表示一个整数,比如10+[n]G 光标定位到第n行行首, [n]表示一个整数,比如10+​ 查找与替换:/[str] 查找字符串str,[str]表示要查找的字符串,回车后会加亮显示所有找到的字符串,命令n移动到下一个找到的字符串,命令N移动到上一个找到的字符串示例: /hello​ 块操作:v 可视化块选择状态,选中块之后,可以对块进行删除(d),复制(y),剪切(x)u 撤销上次操作ctrl + r 恢复上次操作​ 结束编辑::q 在未修改文档的情况下退出:q! 放弃文档的修改,强行退出:w 保存:wq 保存并退出​ 其他::help 命令 查看该命令的帮助提示:%!xxd 十六进制模式:%!xxd -r 返回文本模式​ 如果在编辑过程中不小心按了Ctrl+s,vi会处于僵死状态,按Ctrl+q可以恢复。执行 vim +3 main.c //表示定位到main.c的第3行执行 vim +/printf main.c //表示定位到第一个printf处在命令模式下输入:new 2.c //表示再打开一个vi,是横向的 用vnew 2.c 表示纵向进行切换用Ctrl+w然后再按w即可切换在命令模式中输入gg=G可以自动对齐​ 配置vim显示行号在/etc/vimrc文件中加上以下代码:set numberset tabstop=41.4 编译器命令在linux系统下通常使用gcc作为主要编译器。GCC原名为 GNU C语言编译器(GNU C Compiler),因为它原本只能处理 C语言。GCC 很快地扩展,变得可处理 C++。后来又扩展能够支持更多编程语言。使用GCC编译器的时候,我们必须给出一系列必要的调用参数和文件名称。GCC编译器的调用参数大约有100多个,这里只介绍其中最基本、最常用的参数。GCC最基本的用法∶ gcc [参数] [文件名称]​ 常用的参数如下-c 只编译:不链接成为可执行文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件。-o output_filename:确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out。-g:产生符号调试工具(GNU的gdb)所必要的符号信息,要想对源代码进行调试,我们就必须加入这个选项。-O:对程序进行优化编译、链接,采用这个选项,整个源代码会在编译、链接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、链接的速度就相应地要慢一些。-O2:比-O更好的优化编译、链接,当然整个编译、链接过程会更慢。-E:仅执行编译预处理;-S:将C代码转换为汇编代码;示例:# gcc test.c -o app​ 编译时指定库与头文件路径-L:指定动态库路径。示例:gcc test.c -o app -L/usr/lib-I: 指定头文件存放的路径。示例:gcc test.c -o app -I/usr/include-l: 指定库名称。示例:示例:gcc test.c -o app -lpthread
  • [技术干货] 20. C语言_文件操作相关练习题
    当前文章列出了4个文件编程相关的练习题。文件拷贝实现、 文件加密、学生管理系统链表模板(未添加文件操作)、学生管理系统模板(通过文件系统保存信息)、等4个例子。1.1 文件拷贝实现#include #include #include int main() { /*1. 打开源文件*/ FILE *file_src; FILE *file_new; unsigned char buff[1024]; unsigned int cnt; file_src=fopen("D:/123.pdf","rb"); if(file_src==NULL) { printf("源文件打开失败!\n"); return -1; } /*2. 创建新文件*/ file_new=fopen("D:/456.pdf","wb"); if(file_new==NULL) { printf("新文件创建失败!\n"); return -1; } /*3. 拷贝文件*/ while(!feof(file_src)) { cnt=fread(buff,1,1024,file_src); fwrite(buff,1,cnt,file_new); } /*4. 关闭文件*/ fclose(file_new); fclose(file_src); printf("文件拷贝成功!\n"); return 0; }1.2 文件加密使用: 异或 ^ 密码类型: (1) 整型密码 (2) 字符串密码 比如: 银行提款机的密码、QQ密码 加密代码:#include #include #include int main() { /*1. 打开源文件*/ FILE *file_src; FILE *file_new; unsigned char buff[1024]; unsigned int cnt; unsigned int password=123456; //密码数据 unsigned int data; //存放读取的数据 file_src=fopen("D:/123.pdf","rb"); if(file_src==NULL) { printf("源文件打开失败!\n"); return -1; } /*2. 创建新文件*/ file_new=fopen("D:/456.pdf","wb"); if(file_new==NULL) { printf("新文件创建失败!\n"); return -1; } /*3. 文件加密*/ while(!feof(file_src)) { cnt=fread(&data,1,4,file_src); data=data^password;//文件数据加密 fwrite(&data,1,cnt,file_new); } /*4. 关闭文件*/ fclose(file_new); fclose(file_src); printf("文件加密成功!\n"); return 0; }解密代码: #include #include #include int main() { /*1. 打开源文件*/ FILE *file_src; FILE *file_new; unsigned char buff[1024]; unsigned int cnt; unsigned int password=123456; //密码数据 unsigned int data; //存放读取的数据 file_src=fopen("D:/456.pdf","rb"); if(file_src==NULL) { printf("源文件打开失败!\n"); return -1; } /*2. 创建新文件*/ file_new=fopen("D:/789.pdf","wb"); if(file_new==NULL) { printf("新文件创建失败!\n"); return -1; } /*3. 文件加密*/ while(!feof(file_src)) { cnt=fread(&data,1,4,file_src); data=data^password;//文件数据加密 fwrite(&data,1,cnt,file_new); } /*4. 关闭文件*/ fclose(file_new); fclose(file_src); printf("文件解密成功!\n"); return 0; }1.3 学生管理系统链表模板(未添加文件操作)#include #include #include //存放信息的结构体 struct MyStruct { char Name[50]; //存放姓名 int Number; //存放编号 struct MyStruct *next; //存放下一个节点的地址 }; //链表相关的函数接口 struct MyStruct *ListHead=NULL; //链表头 struct MyStruct *CreateListHead(struct MyStruct *head); void AddrListInfo(struct MyStruct *head,struct MyStruct data); void DeleteListInfo(struct MyStruct *head,int number); void PrintListAllInfo(struct MyStruct *head); int main() { struct MyStruct data1={"张三",123}; struct MyStruct data2={"李四",456}; struct MyStruct data3={"小王",789}; ListHead=CreateListHead(ListHead); //添加信息 AddrListInfo(ListHead,data1); AddrListInfo(ListHead,data2); AddrListInfo(ListHead,data3); //删除节点 DeleteListInfo(ListHead,123); DeleteListInfo(ListHead,789); //打印 PrintListAllInfo(ListHead); return 0; } /* 函数功能: 创建链表头 */ struct MyStruct *CreateListHead(struct MyStruct *head) { if(head==NULL) { head=malloc(sizeof(struct MyStruct)); head->next=NULL; } return head; } /* 函数功能: 在链表结尾添加节点 */ void AddrListInfo(struct MyStruct *head,struct MyStruct data) { struct MyStruct *p=head; struct MyStruct *tmp=NULL; while(p->next!=NULL) { p=p->next; } tmp=malloc(sizeof(struct MyStruct)); memcpy(tmp,&data,sizeof(struct MyStruct)); p->next=tmp; tmp->next=NULL; } /* 函数功能: 根据结构体里特有的成员区分进行删除链表节点信息 函数参数: int numbe 编号 */ void DeleteListInfo(struct MyStruct *head,int number) { struct MyStruct *p=head; struct MyStruct *tmp=NULL; while(p->next!=NULL) { tmp=p; //保存上一个节点的信息 p=p->next; if(p->Number==number) { tmp->next=tmp->next->next; free(p); p=head; //链表头归位 } } } /* 函数功能: 打印所有节点信息 函数参数: int numbe 编号 */ void PrintListAllInfo(struct MyStruct *head) { struct MyStruct *p=head; int cnt=0; printf("\n链表全部信息如下:\n"); while(p->next!=NULL) { p=p->next; cnt++; printf("第%d个节点信息: %s,Ý78d8cf5-73b4-4098-95e0-053cb812f30en",cnt,p->Name,p->Number); } }1.4 学生管理系统模板(通过文件系统保存信息)#include #include #include //存放信息的结构体 struct MyStruct { char Name[50]; //存放姓名 int Number; //存放编号 struct MyStruct *next; //存放下一个节点的地址 }; //链表相关的函数接口 struct MyStruct *ListHead=NULL; //链表头 struct MyStruct *CreateListHead(struct MyStruct *head); void AddrListInfo(struct MyStruct *head,struct MyStruct data); void DeleteListInfo(struct MyStruct *head,int number); void PrintListAllInfo(struct MyStruct *head); //文件操作相关函数 void SaveListAllInfo(struct MyStruct *head,char *path); void GetListAllInfo(struct MyStruct *head,char *path); #if 0 int main() { struct MyStruct data1={"张三",123}; struct MyStruct data2={"李四",456}; struct MyStruct data3={"小王",789}; ListHead=CreateListHead(ListHead); AddrListInfo(ListHead,data1); AddrListInfo(ListHead,data2); AddrListInfo(ListHead,data3); //保存节点信息 SaveListAllInfo(ListHead,"D:/list.ini"); //打印 PrintListAllInfo(ListHead); return 0; } #endif #if 1 int main() { ListHead=CreateListHead(ListHead); //获取节点信息 GetListAllInfo(ListHead,"D:/list.ini"); //打印 PrintListAllInfo(ListHead); return 0; } #endif /* 函数功能: 创建链表头 */ struct MyStruct *CreateListHead(struct MyStruct *head) { if(head==NULL) { head=malloc(sizeof(struct MyStruct)); head->next=NULL; } return head; } /* 函数功能: 在链表结尾添加节点 */ void AddrListInfo(struct MyStruct *head,struct MyStruct data) { struct MyStruct *p=head; struct MyStruct *tmp=NULL; while(p->next!=NULL) { p=p->next; } tmp=malloc(sizeof(struct MyStruct)); memcpy(tmp,&data,sizeof(struct MyStruct)); p->next=tmp; tmp->next=NULL; } /* 函数功能: 根据结构体里特有的成员区分进行删除链表节点信息 函数参数: int numbe 编号 */ void DeleteListInfo(struct MyStruct *head,int number) { struct MyStruct *p=head; struct MyStruct *tmp=NULL; while(p->next!=NULL) { tmp=p; //保存上一个节点的信息 p=p->next; if(p->Number==number) { tmp->next=tmp->next->next; free(p); p=head; //链表头归位 } } } /* 函数功能: 打印所有节点信息 函数参数: int numbe 编号 */ void PrintListAllInfo(struct MyStruct *head) { struct MyStruct *p=head; int cnt=0; printf("\n链表全部信息如下:\n"); while(p->next!=NULL) { p=p->next; cnt++; printf("第%d个节点信息: %s,Ý78d8cf5-73b4-4098-95e0-053cb812f30en",cnt,p->Name,p->Number); } } /* 函数功能: 保存链表节点信息 */ void SaveListAllInfo(struct MyStruct *head,char *path) { struct MyStruct *p=head; FILE *file; file=fopen(path,"a+b"); if(file==NULL) { printf("保存信息的文件打开失败!\n"); return ; } while(p->next!=NULL) { p=p->next; fwrite(p,1,sizeof(struct MyStruct),file); } fclose(file); } /* 函数功能: 从文件里获取链表节点信息 */ void GetListAllInfo(struct MyStruct *head,char *path) { struct MyStruct *p=head; FILE *file; struct MyStruct data; file=fopen(path,"rb"); if(file==NULL) { printf("保存信息的文件打开失败!\n"); return; } //循环读取文件里的数据 while(!feof(file)) { fread(&data,1,sizeof(struct MyStruct),file); //读取链表节点数据 AddrListInfo(head,data); //添加链表节点 } fclose(file); }
  • [技术干货] 19. C语言_文件IO操作函数总结
    当前文章涉及C语言文件操作相关知识点。列出最常见的文件操作函数、fopen、fread、fwrite、fclose 等。通过几个常见需求,写出例子理解文件操作函数的用法。1. 文件IO总结文件IO操作: 对文件系统里的文件进行: 打开、创建、读、写、关闭等运用。 C语言下标准文件IO接口(函数): (1)头文件: stdio.h 输入输出函数: printf 、scanf (2)相关函数: fopen、fread、fwrite、fclose 2.1 标准文件操作有两套函数: 1.标准C语言下的文件操作接口。fopen系列 常用于: 对普通文件的读写。 2.Linux操作系统下的文件操作接口。open系列 常用于: 对设备文件进行读写。 (鼠标、键盘、声卡、..)2. C语言标准文件操作接口2.1 最常用的4个函数#include //打开文件 FILE *fopen(const char *path, const char *mode); //读文件 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); //写文件 size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream); //关闭文件 int fclose(FILE *fp);2.3 写函数的基本运用#include #include #include int main() { FILE *file; int cnt; /*1. 打开文件*/ file=fopen("D:/123.txt","a+b"); if(file==NULL) { printf("文件打开失败!\n"); return -1; } /*2. 写数据*/ cnt=fwrite("1234567890",1,10,file); /*3. 关闭文件*/ fclose(file); printf("cnt=Ô670eb91-2f86-400c-aa89-91f9e3c5d5d6n",cnt); return 0; }2.4 读函数基本运用#include #include #include int main() { FILE *file; int cnt; char buff[100]; /*1. 打开文件*/ file=fopen("D:/123.txt","rb"); //malloc if(file==NULL) { printf("文件打开失败!\n"); return -1; } /*2. 写数据*/ cnt=fread(buff,1,100,file); /*3. 关闭文件*/ fclose(file); //free buff[cnt]='\0'; printf("%s\n",buff); printf("cnt=Ô670eb91-2f86-400c-aa89-91f9e3c5d5d6n",cnt); return 0; }2.5 文件指针位置偏移 (自动向后偏移)#include #include #include int main() { FILE *file; int cnt; char data; /*1. 打开文件*/ file=fopen("D:/123.txt","rb"); //malloc if(file==NULL) { printf("文件打开失败!\n"); return -1; } /*2. 读数据---验证文件指针是否可否自动向后偏移*/ cnt=fread(&data,1,1,file); printf("data=Ä670eb91-2f86-400c-aa89-91f9e3c5d5d6n",data); cnt=fread(&data,1,1,file); printf("data=Ä670eb91-2f86-400c-aa89-91f9e3c5d5d6n",data); cnt=fread(&data,1,1,file); printf("data=Ä670eb91-2f86-400c-aa89-91f9e3c5d5d6n",data); cnt=fread(&data,1,1,file); printf("data=Ä670eb91-2f86-400c-aa89-91f9e3c5d5d6n",data); cnt=fread(&data,1,1,file); printf("data=Ä670eb91-2f86-400c-aa89-91f9e3c5d5d6n",data); /*3. 关闭文件*/ fclose(file); //free return 0; }2.6 设置文件指针位置#include #include #include int main() { FILE *file; int cnt; char data; /*1. 打开文件*/ file=fopen("D:/123.txt","rb"); //malloc if(file==NULL) { printf("文件打开失败!\n"); return -1; } /*2. 偏移文件指针*/ fseek(file,5,SEEK_SET); /*3. 读数据---验证文件指针是否可否自动向后偏移*/ cnt=fread(&data,1,1,file); printf("data=Ä670eb91-2f86-400c-aa89-91f9e3c5d5d6n",data); /*4. 关闭文件*/ fclose(file); //free return 0; }2.7 以上午所学的函数,如何判断文件读完了?到文件结尾?#include #include #include int main() { FILE *file; int cnt; char data; /*1. 打开文件*/ file=fopen("D:/123.txt","rb"); //malloc if(file==NULL) { printf("文件打开失败!\n"); return -1; } /*2. 偏移文件指针*/ fseek(file,5,SEEK_SET); /*3. 读数据---验证文件指针是否可否自动向后偏移*/ while(1) { cnt=fread(&data,1,1,file); if(cnt!=1)break; printf("data=Ä670eb91-2f86-400c-aa89-91f9e3c5d5d6n",data); } /*4. 关闭文件*/ fclose(file); //free return 0; }2.8 文件读写结构体数据//写结构体数据 #include #include #include struct MyStruct { int a; int b; char c[100]; }; int main() { FILE *file; int cnt; struct MyStruct stu={666,888,"C语言文件操作学习"}; /*1. 打开文件*/ file=fopen("D:/123.txt","wb"); if(file==NULL) { printf("文件打开失败!\n"); return -1; } /*2. 读数据*/ cnt=fwrite(&stu,1,sizeof(struct MyStruct),file); printf("cnt=Ô670eb91-2f86-400c-aa89-91f9e3c5d5d6n",cnt); /*3. 关闭文件*/ fclose(file); //free return 0; } //读结构体数据 #include #include #include struct MyStruct { int a; int b; char c[100]; }; int main() { FILE *file; int cnt; struct MyStruct stu; /*1. 打开文件*/ file=fopen("D:/123.txt","rb"); if(file==NULL) { printf("文件打开失败!\n"); return -1; } /*2. 读数据*/ cnt=fread(&stu,1,sizeof(struct MyStruct),file); printf("cnt=Ô670eb91-2f86-400c-aa89-91f9e3c5d5d6n",cnt); printf("%d,%d,%s\n",stu.a,stu.b,stu.c); /*3. 关闭文件*/ fclose(file); //free return 0; }2.9 文件操作的作业练习1. 学习文件基本读写使用 2. 编写文件拷贝程序。 实现文件拷贝。 3. 文件加密解密实现。 需要编写一个菜单。 4. 完善学生管理系统。 需要将所有学生信息保存到文件里,完善功能。
  • [技术干货] C语言_结构体总结
    当前文章介绍动态堆空间内存分配与释放,C语言结构体定义、初始化、赋值、结构体数组、结构体指针的相关知识点,最后通过一个学生管理系统综合练习结构体数组的使用。1. 动态内存管理C语言代码----->编译----->链接------>可执行的二进制文件(windows下xxx.exe) 二进制文件中的数据是如何摆放的? 文本数据段、静态数据段、全局数据段。堆栈空间: 代码在运行的时候才有的空间。 栈空间: 系统负责申请,负责释放。比如: 函数形参变量、数组…… 堆空间: 程序员负责申请,负责释放。#include //标准库头文件 void *malloc(int size); //内存申请。 形参表示申请的空间大小,返回值:申请的空间的地址 void free(void *p); //内存释放。 形参就是要释放的空间首地址。动态空间申请示例。动态空间申请 #include "stdio.h" #include "string.h" #include int main() { int *p=malloc(sizeof(int)); //申请空间 if(p!=NULL) { printf("申请的空间地址: 0x%X\n",p); *p=888; printf("Õ7cea902-b3e1-42e4-8db7-b3b05d60d6den",*p); } free(p); //释放空间 return 0; } 示例2: #include "stdio.h" #include "string.h" #include char *func(void) { char*str=malloc(100); //char str[100]; if(str!=NULL) { strcpy(str,"1234567890"); printf("子函数打印:%s\n",str); //free(str); //释放空间 return str; } else { return NULL; } } int main() { char *p=func(); printf("主函数打印:%s\n",p); return 0; }2. 结构体2.1 定义语法结构体的概念: 可存放不同数据类型的集合。 比如: 存放一个班级学生的信息。 可以使用一个结构体存放一个学生的信息。 一个结构体数组存放整个班级的学习信息。 数组的概念: 可存放相同数据类型的集合。结构体的定义语法://声明一种新类型-----数据类型 struct <结构体的名称> { <结构体的成员>1; <结构体的成员>2; ………… }; //最后有分号结束 struct MyStruct { char a; int b; float c; char str[100]; };2.2 定义示例结构体如何赋值? 如何访问结构体内部成员#include "stdio.h" #include "string.h" #include //定义结构体数据类型 struct MyStruct { char a; int b; float c; char str[100]; }; int main() { struct MyStruct data={'A',123,456.789,"abcd"}; //data就是结构体类型的变量 //结构体变量访问内部成员的语法: . 点运算符 printf("Å7cea902-b3e1-42e4-8db7-b3b05d60d6den",data.a); printf("Õ7cea902-b3e1-42e4-8db7-b3b05d60d6den",data.b); printf("õ7cea902-b3e1-42e4-8db7-b3b05d60d6den",data.c); printf("%s\n",data.str); return 0; }2.3 初始化#include "stdio.h" #include "string.h" #include //定义结构体数据类型 struct MyStruct { char a; int b; float c; char str[100]; }data={'A',123,456.789,"abcd"}; //data就是结构体类型的变量 int main() { //结构体变量访问内部成员的语法: . 点运算符 printf("Å7cea902-b3e1-42e4-8db7-b3b05d60d6den",data.a); printf("Õ7cea902-b3e1-42e4-8db7-b3b05d60d6den",data.b); printf("õ7cea902-b3e1-42e4-8db7-b3b05d60d6den",data.c); printf("%s\n",data.str); return 0; }2.4 结构体赋值 //结构体变量访问内部成员的语法: . 点运算符 #include "stdio.h" #include "string.h" #include //定义结构体数据类型 struct MyStruct { char a; int b; float c; char str[100]; }; int main() { struct MyStruct data;//data就是结构体类型的变量 //成员单独赋值 data.a='A'; data.b=123; data.c=456.789; strcpy(data.str,"abcd"); //数组赋值 //结构体变量访问内部成员的语法: . 点运算符 printf("Å7cea902-b3e1-42e4-8db7-b3b05d60d6den",data.a); printf("Õ7cea902-b3e1-42e4-8db7-b3b05d60d6den",data.b); printf("õ7cea902-b3e1-42e4-8db7-b3b05d60d6den",data.c); printf("%s\n",data.str); return 0; }2.5 结构体数组结构体赋值分为两种标准: C89 、C99 结构体数组 #include "stdio.h" #include "string.h" #include //定义结构体数据类型 struct MyStruct { char a; int b; float c; char str[100]; }; int main() { struct MyStruct data[100];//data就是结构体数组类型变量 struct MyStruct data2[50]; //成员单独赋值 data[0].a='A'; data[0].b=123; data[0].c=456.789; strcpy(data[0].str,"abcd"); //数组赋值 //结构体变量访问内部成员的语法: . 点运算符 printf("Å7cea902-b3e1-42e4-8db7-b3b05d60d6den",data[0].a); printf("Õ7cea902-b3e1-42e4-8db7-b3b05d60d6den",data[0].b); printf("õ7cea902-b3e1-42e4-8db7-b3b05d60d6den",data[0].c); printf("%s\n",data[0].str); return 0; }2.6 结构体指针赋值#include "stdio.h" #include "string.h" #include //定义结构体数据类型 struct MyStruct { char a; int b; float c; char str[100]; }; int main() { //struct MyStruct buff[100]; //struct MyStruct *data=buff; //结构体指针类型变量 struct MyStruct *data=malloc(sizeof(struct MyStruct)); data->a='A'; data->b=123; data->c=456.789; strcpy(data->str,"abcd"); //结构体指针访问内部成员的变量 通过 -> 运算符。 printf("Å7cea902-b3e1-42e4-8db7-b3b05d60d6den",data->a); printf("Õ7cea902-b3e1-42e4-8db7-b3b05d60d6den",data->b); printf("õ7cea902-b3e1-42e4-8db7-b3b05d60d6den",data->c); printf("%s\n",data->str); return 0; }3. 学生管理系统作业: 学生管理系统需求: (每一个功能都是使用函数进行封装) 1.实现从键盘上录入学生信息。 (姓名、性别、学号、成绩、电话号码) 2.将结构体里的学生信息全部打印出来。 3.实现根据学生的姓名或者学号查找学生,查找到之后打印出学生的具体信息。 4.根据学生的成绩对学生信息进行排序。 5.根据学号删除学生信息。示例:#include "stdio.h" #include "string.h" #include //定义存放学生信息的结构体类型 struct StuDentInfo { char Name[20]; //姓名 int number; //学号 char phone[20];//电话号码 }; //全局变量区域 unsigned int StuDentCnt=0; //记录已经录入的全部学生数量 //函数声明区域 void PrintStuDentInfoList(void); void InputStuDentInfo(struct StuDentInfo*info); void FindStuDentInfo(struct StuDentInfo*info); void SortStuDentInfo(struct StuDentInfo*info); void PrintStuDentInfo(struct StuDentInfo*info); int main() { struct StuDentInfo data[100]; //可以100位学生的信息 int number; while(1) { PrintStuDentInfoList(); //打印功能列表 scanf("%d",&number); printf("\n"); switch(number) { case 1: InputStuDentInfo(data); break; case 2: FindStuDentInfo(data); break; case 3: SortStuDentInfo(data); break; case 4: PrintStuDentInfo(data); break; case 5: break; default: printf("选择错误!\n\n"); break; } } return 0; } /* 函数功能: 打印学生管理系统的功能列表 */ void PrintStuDentInfoList(void) { printf("\n--------------学生管理系统功能列表----------------\n"); printf("1. 录入学生信息\n"); printf("2. 根据学号查找学生信息\n"); printf("3. 根据学号排序\n"); printf("4. 打印所有学生信息\n"); printf("5. 删除指定的学生信息\n"); printf("请选择功能序号:"); } /* 函数功能: 录入学生信息 */ void InputStuDentInfo(struct StuDentInfo*info) { printf("输入学生姓名:"); scanf("%s",info[StuDentCnt].Name); printf("输入学号:"); scanf("%d",&info[StuDentCnt].number); printf("输入电话号码:"); scanf("%s",info[StuDentCnt].phone); StuDentCnt++; //数量自增 } /* 函数功能: 查找学生信息 */ void FindStuDentInfo(struct StuDentInfo*info) { int num,i; printf("输入查找的学号:"); scanf("%d",&num); for(i=0; iinfo[j+1].number) { tmp=info[j]; info[j]=info[j+1]; info[j+1]=tmp; } } } } /* 函数功能: 打印所有学生信息 */ void PrintStuDentInfo(struct StuDentInfo*info) { int i=0; printf("-----------所有学生的信息列表------------\n"); for(i=0;i
总条数:501 到第
上滑加载中