一些LinuxC的小知识点(二)

时间:2022-08-27 14:53:32

一、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!\n");
printf(color_Green);//恢复成默认的绿色
return EXIT_SUCCESS;
}

输出的效果:

一些LinuxC的小知识点(二)

需要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)。

一些LinuxC的小知识点(二)                                                                     图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.

 

一些LinuxC的小知识点(二)

                                                                         图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 ( "\n" );
return EXIT_SUCCESS;
}

 

输出的结果如下:

一些LinuxC的小知识点(二)

十一、正确使用errno

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

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

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

if (somecall() == -1) {
int errsv = errno;
printf("somecall() failed\n");
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