• 一些LinuxC的小知识点(二)


    一、read系统调用

          系统调用read的作用是:从与文件描述符filedes相关联的文件里读入nbytes个字节的数据,并把它们放到数据区buf中。它返回实际读入的字节数。这可能会小于请求的字节数。如果read调用返回0,就表述未读入任何数据,已经达到了文件尾。同样,如果返回的是-1,就表示read调用出现了错误。

    #include <unistd>
    size_t read(int filedes,void *buf,size_t nbytes);

    二、普通文件(file)和设备文件(device)的区别

    普通文件,即常用的磁盘文件由字节组成,磁盘文件中的字节数就是文件大小。而设备文件是链接,不是容器。键盘和鼠标不存储点击数:)设备文件的i节点存储 的是指向内核的子程序的指针,而不是文件的大小和存储列表。内核中传输设备数据的子程序被称为设备驱动程序。

    三、在代码中控制控制台颜色

    为了更方便地一眼看出程序输出的信息,很多时候我们都想要用颜色让输出的结果更为直观,无奈Linux下的颜色永远都是黑底绿字。其实改变颜色的方式很简单。如下代码所示

    #include <stdio.h>
    #include <stdlib.h>
    #include "include/consoleColor.h"
    int main(int argc, char *argv[])
    {
        printf(color_Red);//红色
        printf("Hello, world!
    ");
        printf(color_Green);//恢复成默认的绿色
        return EXIT_SUCCESS;
    }

    输出的效果:

    image

    需要include的头文件

    #ifndef __CONSOLE_COLOR__
    #define __CONSOLE_COLOR__
    char * color_Black= "x1b[01;30m";
    char * color_Red= "x1b[01;31m";
    char * color_Green= "x1b[01;32m";
    char * color_Yellow= "x1b[01;33m";
    char * color_Blue= "x1b[01;34m";
    char * color_PurplishRed= "x1b[01;35m";
    char * color_Cyan= "x1b[01;36m";
    char * color_white= "x1b[01;37m";
    #endif

    相关阅读:让你的linux虚拟终端五彩缤纷(1)——LS颜色设置

    四、多线程程序makefile编写的注意事项

    对于多线程makefile中,要注意添加-lpthread,以便添加pthread的链接库。另外,需要加入宏定义-D_REENTRANT,以便将有的不可充入的函数或者变量,变成可重入的,以便适应多线程的要求(详见图1)。

    image                                                                     图1 为什么添加-lpthread和_REENTERANT

    下面是一个多线程的makefile。其中在ihome_arm.c这个文件中,我们用到了多线程。在ihome_arm.c中我们需要引用comtest.c中的函数。

    ihome_arm : comtest.o ihome_arm.o
        arm
    -linux-gcc -o ihome_arm comtest.o ihome_arm.o -L/usr/include/nptl -
    lpthread
    all: pthread
    comtest.o : comtest.c
        arm
    -linux-gcc -
    c comtest.c
    ihome_arm.o : ihome_arm.c comtest.c
        arm
    -linux-gcc -D_REENTRANT -I/usr/include/nptl -
    c ihome_arm.c
    clean :
        
    rm ihome_arm comtest.o ihome_arm.o

    值得注意的是:在上面代码段中灰色背景下的语句前都要添加一个tab分隔符,必须是tab分隔符,不然就会报“Makefile:3:***遗漏分隔符。停止 。”。因为makefile中规定,如果是bash语句,那么必须在语句的最前用tab作分隔符。其中关于

    -L/usr/include/nptl 的说明见图2.

     

    image

                                                                             图2 为什么添加-L/usr/include/nptl

    以上图片来自《Linux程序设计 第四版》

     

    五、头文件和源文件引用的注意事项

    知识点四中,我们程序里面有comtest.c、comtest.h、ihome_arm.c。其中在ihome_arm.c这个文件中,我们用到了多线程。在ihome_arm.c中我们需要引用comtest.c中的函数。我曾很傻气地直接在ihome_arm.c中直接引用comtest.c,结果导致大量的重复定义出现。正确的方式应该是,为ihome_arm.c引用的类型、变量、函数写一个comtest.h的头文件,这样就不会引起大量的重复定义。头文件编写的注意事项见上一篇文章。

     

    六、Linux的sleep()和usleep()的使用和区别

    1、sleep函数

    函数名: sleep
    头文件: #include <windows.h> // 在VC中使用带上头文件
                 #include <unistd.h>    // 在gcc编译器中,使用的头文件因gcc版本的不同而不同
    功  能 : 执行挂起指定的秒数
    语  法 : unsigned sleep(unsigned seconds);

    示例:

    #include <unist.h>
     #include<stdio.h>
     int main()
     {
           int a;
           a=1;
           printf("hello");
           sleep(a);      
           printf("world"); 
           return 0;
     }

    2、usleep函数

    函数名: usleep
    头文件: #include <unistd.h>
    功  能:  usleep功能把进程挂起一段时间, 单位是微秒(百万分之一秒);
    语  法:  void usleep(int micro_seconds);
    返回值: 无

    内容说明:本函数可暂时使程序停止执行。参数 micro_seconds 为要暂停的微秒数(us)。
     注 意
    这个函数不能工作在windows 操作系统中。用在Linux的测试环境下面。
    参 见:usleep() 与sleep()类似,用于延迟挂起进程。进程被挂起放到reday queue。一般情况下,延迟时间数量级是秒的时候,尽可能使用sleep()函数。如果延迟时间为几十毫秒(1ms = 1000us),或者更小,尽可能使用usleep()函数。这样才能最佳的利用CPU时间。

    七、时钟换算:

     微秒,时间单位,符号us(英语:microsecond ).
    1微秒等于百万分之一(10的负6 次方秒)
    0.000 001 微秒 = 1皮秒
    0.001 微秒 = 1纳秒
    1,000 微秒 = 1毫秒
    1,000,000 微秒 = 1秒
    1s = 1000ms
    1ms = 1000μs
    1μs = 1000ns
    1ns = 1000ps
    1秒(s) = 1000 毫秒(ms) = 1,000,000 微秒(μs) = 1,000,000,000 纳秒(ns) = 1,000,000,000,000 皮秒(ps)

    八、利用man查找制定C语言函数

    man 3  函数

    其中3代表man要查找的是C库函数。参考链接

    九、多线程编程中主线程等待子进程结束

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    
    //省略了必要的函数
    int main ( int argc, char **argv )
    {
        struct MainArgus mMainArgus;
        mMainArgus.argc=argc;
        mMainArgus.argv=argv;
        int res;//用来判断线程是否创建成功,是否退出的标志
        pthread_t a_thread;
        void *thread_result;
        res = pthread_create ( &a_thread,NULL,uart_pthread, ( void * ) ( &mMainArgus ) );
        if ( res!=0 )
        {
            exit ( EXIT_FAILURE );
        }
        res
    =pthread_join ( a_thread,&
    thread_result );
    
        if ( res!=0 )
        {
            exit ( EXIT_FAILURE );
        }
        return EXIT_SUCCESS;
    }

          在代码段中的灰色背景代码中,这句话关系到子线程a_thread能不能正常地运行完,pthread_join让main所在的主线程等待a_thread线程结束,才继续从pthread_join语句中往下执行。如果没有这一句,当子线程是比较耗时的操作,它在主线程运行到return EXIT_SUCCESS;的时候还没有结束的话,那么随着main所在的主线程的退出,子线程a_thread也将退出,也就是a_thread不能被正确地执行完,而是意外地退出了。
         我们给pthread_join传递了两个参数,一个正在等待器结束的线程的标识符athread,另一个是指向线程返回值的指针。这个函数将等到它所指定的线程终止后才返回。然后main函数在pthread_join行之后,继续执行。

    十、errno的由来

    errno定义在头文件<errno,h>中,他是一个整形的变量。当系统调用和其他库函数发送错误的时候,errno用来标识错误是什么。

    常用的用来报告错误的函数。它们是strerrorperror

    1、strerror用来将错误代码errno映射成一个字符串,该字符串对发生的错误类型进行说明。

    函数原型如下

    #include <string.h>
    
    char *strerror(int errnum);
    
    char *strerror_r(int errnum, char *buf, size_t buflen);

    2、perror用来把errno变量中报告的当前错误映射到一个字符串,并把它输出到标准错误输出流。该字符串的前面先加上字符串s(如果不为空)中给出的信息,在加上一个冒号和一个空格。

    函数原型如下

    #include <stdio.h>
    void perror(const char *s);

    3、一个小样例。在下面的这个例子中,我们错误地打开一个不存在的文件,产生一个errno,并用strerror和perror进行报告。

    #include <stdio.h> //为了使用perror
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h> //为了使用errno
    #include <string.h>//为了使用strerror
    int main ( int argc, char *argv[] )
    {
        int CommFd = open ( "/dev/kkk", O_RDWR, 0 );//这里故意打开一个错误的设备,以便引发一个错误
        if ( CommFd==-1 )
        {
            int errnosv=errno;
            perror ( "HelloError" );
            printf ( strerror ( errnosv ) );
        }
        printf ( "
    " );
        return EXIT_SUCCESS;
    }

     

    输出的结果如下:

    image

    十一、正确使用errno

    一个常见的错误用法如下所示:

    if (somecall() == -1) {
            printf("somecall() failed
    ");
            if (errno == ...) { ... }
        }

    当somecall引发了一个错误,errno被赋上值之后。你不应该直接使用errno进行其他的操作,在以上代码段中,errno可能被printf函数改变。如果我们要使用errno,最好的办法就是如下代码段所示的,把它保存在一个变量中,然后再使用。

    if (somecall() == -1) {
            int errsv = errno;
            printf("somecall() failed
    ");
            if (errsv == ...) { ... }
        }

    十二、extern关键字的使用

    首先注意,extern关键字不是用来声明全局变量的。下面看一段错误的代码

    //假设这是一个头文件MyCode.h
    extern char something[255];

    然后我们想要在下面的源文件中引用something。

    //MyCode.c
    #include <stdlib.h>
    #include <string.h>
    #include <MyCode.h>
    int main(){
       strcpy(something,"Hello world");//在这个地方编译器会爆出something没有定义(undefined错误)
       printf(something);
    }

    因为extern不是这么用的。extern的出现是为了解决源文件中的东西,不能被其他源文件访问的问题。一般来说,假设我们要访问一个源文件(假设是源文件myCode.c)中的变量,那么我们需要先在源文件MyCode.c中定义一个变量,通常是在所有函数的开头。然后在这个源文件对应的头文件MyCode.h中用extern标志这个变量,那么当其他源文件MyCodeTwo.c引用了头文件MyCode,h的时候,就可以访问到MyCode.c中定义的变量。如下代码段所示。

    //MyCode.c修改后
    #include <stdlib.h>
    #include <string.h>
    #include <MyCode.h>
    char something[255];//定义一个全局变量
    int main(){
       strcpy(something,"Hello world");
       printf(something);
    }
    //MyCodeTwo.c
    #include <stdlib.h>
    #include <string.h>
    #include <MyCode.h>
    int main(){
       strcpy(something,"Hello world");//可以访问到MyCode.c的something
       printf(something);
    }

    十三、Tar打包、压缩与解压缩到指定目录的方法

    tar在linux上是常用的打包、压缩、加压缩工具,他的参数很多,这里仅仅列举常用的压缩与解压缩参数。

    1、将整个/home/www/images 目录下的文件全部打包为 /home/www/images.tar

    tar -cvf /home/www/images.tar /home/www/images ← 仅打包,不压缩
    
    tar -zcvf /home/www/images.tar.gz /home/www/images ← 打包后,以gzip压缩

    在参数f后面的压缩文件名是自己取的,习惯上用tar来做,如果加z参数,则以tar.gz 或tgz来代表gzip压缩过的tar file文件

    2、将 /home/www/images.tar.gz 解压到/home/www下面

    cd /home/www
    
    tar -zxvf /home/images.tar.gz

    解压到指定的目录

    tar -zxvf /home/images.tar.gz -C /specific dir

    解包到指定的目录

    tar xvf filename.tar -C /specific dir
  • 相关阅读:
    通过IP获取所在城市
    一次完整的HTTP请求过程
    Array.prototype.slice.call(arguments)
    移动web资源整理
    Null 和 Undefined
    JS正则表达式
    JavaScript和JSP的区别?
    JS判断图片上传时文件大小和图片尺寸
    JavaScript中的callee与caller的区别
    php结合redis实现高并发下的抢购、秒杀功能
  • 原文地址:https://www.cnblogs.com/kissazi2/p/3148053.html
Copyright © 2020-2023  润新知