华清远见嵌入式培训_第七周回顾与反思(上)

前言

        七天的国庆假期已经结束,紧接着的是七天的工作日,前几天的我感叹假期七天有多短,此时此刻的我就感叹七天有多长!

        言归正传,本周的前五天课里,紧接着假期前的IO进程课程,第一天学习了:进程中各种常用函数的接口,多进程中的守护进程;第二天学习了:多线程的创建、执行、退出和回收和线程中各种常用函数的接口;第三天学习了:线程间的通信,包括互斥锁、无名信号量和条件变量,传统进程间通信方式的一部分,包括无名管道和有名管道;第四天学习了:传统进程间通信中的信号,SystemV引入的IPC进程间通信中的消息队列和共享内存;第五天学习了:SystemV引入的IPC进程间通信中的信号灯集,下午进行了这门课的考试,最后会附上这套试卷的改错和分析。

        这门课的主要特点是函数接口多、要熟悉和记忆的内容多、易混淆的知识点多,这几天的内容中,重点和难点是线程间通信进程间通信,因此,本次的总结反思将针对这几方面,进行整理归纳,难点分析和易混淆点辨析。

        同样,写此文章,是想以这种碎碎念的方式回顾重点、重复盲点、加深印象,复习、总结和反思本周的学习,仅供后期自己回顾使用。知识体系不够完善,内容也不够详细,仅供笔者复习使用。如果是有需要源码和笔记、或者对这方面感兴趣的同学,都可以私信我,发你完整的知识体系和详细内容的笔记。如有任何错误请多指正,也欢迎友好交流,小弟定会虚心听取大佬们的宝贵意见!

周六

一、进程相关的函数接口

1.1 getpid / getppid函数

        直接从字面意思理解,获取当前进程的pid和父进程的pid,功能参数返回值如下,着重注意这两个函数的返回值。

pid_t getpid(void);
功能:获取当前进程的进程号
参数:
    @无
返回值:返回当前进程的pid
pid_t getppid(void);
功能:获取父进程的进程号
参数:
    @无
返回值:返回父进程的pid

1.2 exit / _exit函数

        字面意思理解,退出(结束)进程的函数;

        注意两个函数的区别:exit结束进程时会刷新缓存区,_exit结束进程时,不会刷新缓存区

        和 return 区分:return本身并不具备结束进程的功能,只有当return在main函数中使用的时候

才具备结束进程的功能。

#include 
void exit(int status);
功能:用来结束一个正在执行的进程,会刷新缓冲区(库函数)
参数:
    @status:进程退出状态值
     #define EXIT_FAILURE 1  //失败退出
     #define EXIT_SUCCESS 0  //成功退出
返回值:无

#include 
void _exit(int status);
功能:用来结束一个正在执行的进程,不会刷新缓冲区(系统调用)
参数:
    @status:进程退出状态值
返回值:无

练习:

程序代码如下,请按执行顺序写出输出结果。


#include 
int main()
{
    pid_t pid1, pid2;

    if ((pid1 = fork()) == 0){
        sleep(3);
        printf("info1 from child process_1\n");
        exit(0);
        printf("info2 from child process_1\n");

    } else {
        if ((pid2 = fork()) == 0) {
            sleep(1);
            printf("info1 from child process_2\n");
            exit(0);

        } else {
            wait(NULL);
            wait(NULL);
            printf("info1 from parent process\n");
            printf("info2 from parent process");
            _exit(0);
        }
    }
}

分析:

        exit结束进程时会刷新缓存区,_exit结束进程时,不会刷新缓存区。

答案:

        info1 from child process_2
        info1 from child process_1
        info1 from parent process

1.3 wait / waitpid函数

        如果子进程使用exit/_exit退出,此时子进程的资源没有回收,子进程的状态会变成僵尸态,wait/waitpid函数的作业是为僵尸进程手动回收资源。

        注意:回收资源时,一次只能回收掉一个进程的资源,wait不能指定进程,谁先结束回收谁,waitpid可以指定回收。

        wait(NULL)  =  waitpid(-1,NULL,0)

#include 
#include 
pid_t wait(int *wstatus);
功能:阻塞回收子进程的资源(没有指定回收那个子进程的资源,谁先结束回收谁)
      (一次只能回收掉一个进程的资源)
参数:
    @wstatus:子进程退出的状态
返回值:成功返回回收掉的子进程的pid,失败返回-1置为错误码
#include 
#include 
pid_t waitpid(pid_t pid, int *wstatus, int options);
功能:可以指定回收某个进程的资源
参数:
    @pid:进程号
       < -1   先对这个值取绝对值,然后同组的任意的子进程的资源
       -1     回收任意子进程的资源
       0      回收同组的任意子进程资源
       > 0    回收pid指定的进程的资源
    @wstatus:进程退出的状态
    @options:回收资源的方式
            0:阻塞方式回收资源
      WNOHANG:非阻塞方式回收资源
返回值:成功返回pid,如果时非阻塞方式没有回收掉子进程的资源返回0
        失败返回-1,置位错误码

二、守护进程的相关知识

1.1 什么是守护进程

        守护进程就是后台运行的服务,随系统的启动而启动,随系统的关闭而关闭。

        守护进程可以脱离终端运行。

1.2 创建守护进程的流程

        1. 创建脱离终端的进程(孤儿进程);

        2. 修改孤儿进程的sid和gid(调用setsid函数);

        3. 切换当前进程的目录(调用chdir函数);

        4. 设置umask值(调用umask函数);

        5. 文件描述符重定向(调用dup、dup2函数);

1.3 使用到的函数接口

修改孤儿进程的sid和gid

pid_t setsid(void);
功能:将当前进程的pid,设置位组id和会话id
参数:
    @无
返回值:成功返回pid,失败返回-1置为错误码

切换当前进程的目录

int chdir(const char *path);
功能:切换目录
参数:
    @path:路径 "/"
返回值:成功返回0,失败返回-1置位错误码

设置umask值

mode_t umask(mode_t mask);
功能:设置文件掩码值
参数:
    @mask:掩码  0
返回值:总是会成功,返回mask

进行文件描述符的重定向

int dup(int oldfd);
功能:拷贝oldfd生成新的newfd,这个newfd采用最小未分配的原则分配,
    新旧文件描述符都可以操作同一个文件,他们公用光标和文件的状态。
参数:
    @oldfd:旧的文件描述符
返回值:成功返回新的文件描述符,失败返回-1置位错误码
        
int dup2(int oldfd, int newfd);
功能:将oldfd重定向位newfd,如果newfd对应的有打开的文件,就会把文件先关闭在重用
    以后对newfd操作就相当于对oldfd操作,共用光标和文件状态
参数:
    @oldfd:旧的文件描述符
 @newfd:新的文件描述符
返回值:成功返回新的文件描述符,失败返回-1置位错误码

这一步可以直接用下面这三行总结,即将标准输入、标准输出和标准出错都重定向到fd。

dup2(fd,0);
dup2(fd,1);
dup2(fd,2);

练习:

创建一个脱离终端、后台运行的守护进程

#include 

int main(int argc,const char * argv[])
{
    pid_t pid;
    int fd;
    pid = fork();
    if(pid ==-1){
        PRINT_ERR("fork error");
    }else if(pid==0){
        //1.孤儿进程
        //2.设置会话id和组id
        if(setsid()==-1)
            PRINT_ERR("setsid error");
        //3.切换目录
        if(chdir("/"))
             PRINT_ERR("chdir error");
        //4.设置umask
        umask(0);

        //5.创建日志文件
        if((fd = open("mylog.log",O_RDWR|O_CREAT|O_APPEND,0666))==-1)
            PRINT_ERR("open error");
        
        //6.进行文件描述符重定向
        dup2(fd,0);
        dup2(fd,1);
        dup2(fd,2);
        //7.开启自己的服务
        while(1){
            printf("i am test daemon process...\n");
            fflush(stdout);
            sleep(1);
        }

    }else{
        exit(EXIT_SUCCESS);
    }
    return 0;
}

周日

一、进程中用来对可执行程序进行替换的函数

1.1 system函数

        system函数的作用,简单理解就是实现在C语言代码中,执行linux命令,功能参数返回值如下:

#include 
int system(const char *command);
功能:在一个进程中fork出来一个子进程,在子进程内执行command这个命令(或者可执行程序),在system函数执行完才会返回
参数:
    @command:可执行程序路径及名字
返回值:
    如果是非0代表函数执行失败,如果是0代表成功
    *如果command为NULL,则如果shell可用,则为非零值;
        如果没有shell可用,则为0。
    *如果无法创建子进程,或者无法检索其状态,则返回值为-1。
    *如果一个shell不能在子进程中执行,那么返回值_exit(127)
    *如果所有系统调用都成功,则返回值是用于执行命令的子shell的终止状态。(shell的终止状态是它执行的                
    最后一个命令的终止状态。)

1.2 exec函数族

        exec函数族中包含:execl、execv、execlp、execvp、execle、execvpe;

        功能简单理解就是,用另外的程序替换掉当前进程中,正在执行的程序。

        注意:函数执行后,后面的被替换掉的函数就不会被执行了。

int execl(const char *path, const char *arg, ... /* (char  *) NULL */);
功能:替换当前进程正在执行的程序
参数:
    @path:可执行程序的路径及名字 "/bin/ls"
    @arg:执行可执行程序的参数列表  "ls","-lh",NULL
返回值:只有执行失败会返回,返回-1置位错误码

int execv(const char *path, char *const argv[]);
功能:替换当前进程正在执行的程序
参数:
    @path:可执行程序的路径及名字 "/bin/ls"
    @arg:执行参数的容器  argv[]  = "ls","-lh",NULL
返回值:只有执行失败会返回,返回-1置位错误码

         execlp/execvp在执行程序替换的时候,如果可执行程序的路径通过PATH环境变量指定了在填写第一个参数的时候可以不用写路径,而直接写可执行程序的名字。

        ( PATH变量的位置,sudo vi /etc/environment )

int execlp(const char *file, const char *arg, ... /* (char  *) NULL */);
功能:替换当前进程正在执行的程序
参数:
    @file:可执行程序的名字 "ls"
    @arg:执行可执行程序的参数列表  "ls","-lh",NULL
返回值:只有错误会返回,返回-1置位错误码

int execvp(const char *file, char *const argv[]);
功能:替换当前进程正在执行的程序
参数:
    @file:可执行程序的名字 "ls"
    @arg:执行参数的容器  argv[]  = "ls","-lh",NULL
返回值:只有错误会返回,返回-1置位错误码

         execle和execvpe的特点是,可以指定目录下的可执行来替换当前的进程中的程序。

int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
功能:替换当前进程正在执行的程序
参数:
    @path:可执行程序的路径及名字 "/bin/ls"
    @arg:执行可执行程序的参数列表  "ls","-lh",NULL
    @envp:想传递的环境变量
返回值:只有错误会返回,返回-1置位错误码
int execvpe(const char *file, char *const argv[], char *const envp[]);
功能:替换当前进程正在执行的程序
参数:
    @file:可执行程序的名字 "ls"
    @arg:执行可执行程序的参数列表  "ls","-lh",NULL
    @envp:想传递的环境变量
返回值:只有错误会返回,返回-1置位错误码

二、多线程

2.1 什么是多线程

        线程(Light Weight Process,LWP),即轻量级的进程,进程是分配资源的最小单位,线程是调度的最小单位。

        线程大小为8k,共享进程的内存(0-3G),现成的大小基本可以忽略不计,通常认为线程不占内存。

        多线程没有多进程安全,因为多线程共用同一个进程的资源,如果多线程中的一个线程导致进程崩溃,所有的其他的线程也会终止;多进程中,进程和进程资源独立,互不影响。

        多线程的效率要比多进程高。

        线程的函数是通过第三方库实现的libpthread.so,所以在使用线程创建函数的时候需要-lpthread链接第三方库。线程的函数的man手册需要通过sudo apt-get install安装sudo apt-get install manpages-posix manpages-posix-dev

        获取线程信息的命令:        ps -eLf

        线程执行和进程一样,没有先后顺序,时间片轮序,上下文切换。

        多线程在内存上是共用的,一个线程将变量改掉之后,另外一个线程能拿到修改后的值。

2.2 pthread_create函数

#include 

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                  void *(*start_routine) (void *), void *arg);
功能:创建一个线程
参数:
    @thread:获取到线程号
    @attr:线程的属性 一般NULL
    @start_routine:线程体
    @arg:向线程体传递参数
返回值:成功返回0,失败返回错误码

2.3 pthread_self函数

pthread_t pthread_self(void);
功能:获取当前线程的线程号
参数:
    @无
返回值:总是会成功,成功返回线程号

2.4 pthread_exit函数

void pthread_exit(void *retval);
功能:退出当前的线程
参数:
    @retval:线程退出的状态值
返回值:无

2.5 pthread_join函数

int pthread_join(pthread_t thread, void **retval);
功能:阻塞等待回收线程的资源(线程必须是结合态)
参数:
    @thread:线程号
 @retval:接收pthread_exit退出时候的状态值
返回值:成功返回0,失败返回错误码

2.6 pthread_detach函数

        线程有两种状态:结合态和分离态

        结合态的线程结束后需要调用pthread_join来回收资源,如果没有调用pthread_join当进程结束的时候

        资源也是可以被回收调用。

        分离态的线程在结束后资源会被自动回收

int pthread_detach(pthread_t thread);
功能:将线程标记为分离态
参数:
    @thread:线程号
返回值:成功返回0,失败返回错误码

2.7 pthread_cancel函数

int pthread_cancel(pthread_t thread);
功能:取消thread线程的执行
参数:
    @thread:线程号
返回值:成功返回0,失败返回非0

你可能感兴趣的:(华清远见嵌入式培训,学习总结,c语言,vim,ubuntu,linux)