zoukankan      html  css  js  c++  java
  • 信息安全系统设计基础第十二周学习总结

    信息安全系统设计基础第十二周学习总结

    【学习时间:8 小时】

    【学习内容:process文件夹中的代码理解和执行、故障排除】

    一、代码理解&执行(因为我的虚拟机无法共享文件,所以借用20135211李行之的电脑共同完成代码执行)

    1.argv(文件夹)-argtest.c文件

    #include <stdio.h>
    #include <stdlib.h>
    #include "argv.h"//该函数库中包括freemakeargv.c及makeargv.c函数的调用
    
    int main(int argc, char *argv[]) 
    {
       char delim[] = " 	";//制表符
       int i;
       char **myargv;//见下方解释
       int numtokens;
    
       if (argc != 2)//如果输入的命令字符个数不等于2,就输出标准错误 
       {
            fprintf(stderr, "Usage: %s string
    ", argv[0]);
            return 1;
       }   
      if ((numtokens = makeargv(argv[1], delim, &myargv)) == -1) 
      {
            fprintf(stderr, "Failed to construct an argument array for %s
    ", argv[1]);//翻译过来就是无法构造一个参数数组
            return 1;
       } 
       printf("The argument array contains:
    ");
       for (i = 0; i < numtokens; i++)
            printf("%d:%s
    ", i, myargv[i]);
       execvp(myargv[0], myargv);
    
       return 0;
    }
    

    另附加 freemakeargv.cmakeargv.c函数

    #include <stdlib.h>
    #include "argv.h"
    
    void freemakeargv(char **argv) {
       if (argv == NULL)
             return;
       if (*argv != NULL)
            free(*argv);
       free(argv);
    }
    /*-----------------------------------------------------------------------------*/
    #include <errno.h>
    #include <stdlib.h>
    #include <string.h>
    #include "argv.h"
    
    int makeargv(const char *s, const char *delimiters, char ***argvp)//见下方解释
    {
       int error;
       int i;
       int numtokens;
       const char *snew;
       char *t;
    
       if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) 
      {
            errno = EINVAL;
            return -1;
      }
       *argvp = NULL;//把字符串数组置为空   
       snew = s + strspn(s, delimiters);//返回字符串s开头连续包含字符串delimiters内的字符数目  
       if ((t = malloc(strlen(snew) + 1)) == NULL) 
            return -1; 
       strcpy(t, snew);   
       numtokens = 0;
       if (strtok(t, delimiters) != NULL)//关于strtok函数的用法见下方
            for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ; 
    
       if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL)
      {//malloc函数请求分配了numtokens+1个字节的空间;如果是返回值是NULL说明分配不成功
            error = errno;
            free(t);//释放malloc函数给指针变量分配的内存空间的函数。使用后该指针变量一定要重新指向NULL。
            errno = error;
            return -1; 
       } 
       if (numtokens == 0) 
            free(t);
       else 
      {
            strcpy(t, snew);
            **argvp = strtok(t, delimiters);
            for (i = 1; i < numtokens; i++)
                *((*argvp) + i) = strtok(NULL, delimiters);
      } 
    *((*argvp) + numtokens) = NULL;   
    return numtokens;
    } 
    

    关于该系列代码的难点理解 

    【1.为什么是* *myargv?】 

    经过查阅得知,比较准确的说法是: **相相当于二级指针,char **就是指向字符型指针的指针。最常使用的地方就是 int main(int argc,char **argv),相当于int main(int argc,char *argv[])。也就是说,可以看作是指向了字符串数组

    【2.为什么是 int makeargv(const char *s, const char *delimiters, char * **argvp)】

    把最后一个参数理解为向字符串数组取地址(从左到右,第一个代表取地址,后两个 **代表上文中说过的字符串数组)

    【3.关于strtok函数?】 

    strtok函数用来将字符串分割成一个个片段,它的原型是char *strtok(charr s[],const char *delim)。只要在s中遇到delim中包含的字符(不一定是delim),就把这个字符改成。每次调用成功后返回的都是被分割出的片段的指针。

    【4.errno与error?】

    前者是记录系统最后一次错误的函数;后者是系统错误

    2.env文件夹-environ.c文件

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        printf("PATH=%s
    ", getenv("PATH"));//getenv函数用来取得参数PATH环境变量的值,执行成功则返回该内容的指针
        setenv("PATH", "hello", 1);//见下方解释
        printf("PATH=%s
    ", getenv("PATH"));
    #if 0
        printf("PATH=%s
    ", getenv("PATH"));
        setenv("PATH", "hellohello", 0);
        printf("PATH=%s
    ", getenv("PATH"));
    
    
        printf("MY_VER=%s
    ", getenv("MY_VER"));//版本
        setenv("MY_VER", "1.1", 0);
        printf("MY_VER=%s
    ", getenv("MY_VER"));
    #endif
        return 0;
    }
    

    【setenv函数的作用?】 

    setenv用来在本次函数运行的过程中增加或者修改环境变量。当最后一个参数不为0的时候,原来的内容会被修改为第二个参数所指的内容。

    3.env文件夹-environvar.c文件

    #include <stdio.h>
    int main(void)
    {
        extern char **environ;
        int i;
        for(i = 0; environ[i] != NULL; i++)
            printf("%s
    ", environ[i]);
    
        return 0;
    }
    

    【environ变量是什么?】

    该变量指向一个叫“environment”的字符串数组。包括USER(登录用户的名字),LOGNAME(与user类似),HOME(用户登录目录),LANG(地域名),PATH等

    4.pipe文件夹-consumer.c文件

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    
    #define FIFO_NAME "/tmp/myfifo"
    #define BUFFER_SIZE PIPE_BUF
    int main()
    {
    int pipe_fd;
    int res;
    
    int open_mode = O_RDONLY;
    char buffer[BUFFER_SIZE + 1];
    int bytes = 0;
    
    memset(buffer, 0, sizeof(buffer));
    
    printf("Process %d opeining FIFO O_RDONLY 
    ", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d
    ", getpid(), pipe_fd);
    
    if (pipe_fd != -1) {
        do {
            res = read(pipe_fd, buffer, BUFFER_SIZE);
            bytes += res;
        } while (res > 0);
        close(pipe_fd);
    } else {
        exit(EXIT_FAILURE);
    }
    
    printf("Process %d finished, %d bytes read
    ", getpid(), bytes);
    exit(EXIT_SUCCESS);
    }
    

    【1.PIPE_BUF的值是多少?】

    4096字节

    【2.memset函数用法?】

    原型:memset(void *s,int ch,size_t n);将s中前n个字节用ch替换并返回s

    【3.open函数用法?】 

    open(const char *pathname,int flags);第一个参数是欲打开的文件路径字符串,第二个参数是打开方式

    【4.FIFONAME是什么?】 

    这里需要补充一下fifo的含义,它是一种文件类型,可以通过查看文件stat结构中的stmode成员的值来判断文件是否是FIFO文件。fifo是用来在进程中使用文件来传输数据的,也具有管道特性,可以在数据读出的时候清除数据。

    5.env文件夹-producer文件

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    
    #define FIFO_NAME "/tmp/myfifo"
    #define BUFFER_SIZE PIPE_BUF
    #define TEN_MEG (1024 * 1024 * 10)
    
    int main()
    {
    int pipe_fd;
    int res;
    int open_mode = O_WRONLY;
    
    int bytes = 0;
    char buffer[BUFFER_SIZE + 1];
    
    if (access(FIFO_NAME, F_OK) == -1) {//检查文件是否有相应的权限
        res = mkfifo(FIFO_NAME, 0777);//依据FIFO_NAME创建fifo文件,0777依次是相应权限
        if (res != 0) {
            fprintf(stderr, "Could not create fifo %s 
    ",
                FIFO_NAME);
            exit(EXIT_FAILURE);
        }
    }
    
    printf("Process %d opening FIFO O_WRONLY
    ", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d
    ", getpid(), pipe_fd);
    
    if (pipe_fd != -1) {
        while (bytes < TEN_MEG) {
            res = write(pipe_fd, buffer, BUFFER_SIZE);
            if (res == -1) {
                fprintf(stderr, "Write error on pipe
    ");
                exit(EXIT_FAILURE);
            }
            bytes += res;
        }
        close(pipe_fd);
    } else {
        exit(EXIT_FAILURE);
    }
    
    printf("Process %d finish
    ", getpid());
    exit(EXIT_SUCCESS);
    

    }

    6.env文件夹-testmf.c文件

    #include  <stdio.h>
    #include  <stdlib.h>
    #include  <sys/types.h>
    #include  <sys/stat.h>
    
    int main()//就是创建fifo文件
    {
        int res = mkfifo("/tmp/myfifo", 0777);
        if (res == 0) {
            printf("FIFO created 
    ");
        }
        exit(EXIT_SUCCESS);
    }

    (修改testmf.c代码)

    (修改之后的执行结果)

    7.pipe文件夹-pipe.c文件

    #include    <stdio.h>
    #include<stdlib.h>
    #include    <unistd.h>
    
    #define oops(m,x)   //当linux系统执行代码遇到问题时,就会报告oops
    { perror(m); exit(x); }
    
    int main(int ac, char **av)
    {
        int thepipe[2], newfd,pid;              
    
        if ( ac != 3 ){//输入的命令长度不等于3
            fprintf(stderr, "usage: pipe cmd1 cmd2
    ");
            exit(1);
        }
        if ( pipe( thepipe ) == -1 )    //以下是各种错误   
            oops("Cannot get a pipe", 1);
    
        if ( (pid = fork()) == -1 )         
            oops("Cannot fork", 2);
    
        if ( pid > 0 ){         
            close(thepipe[1]);  
    
            if ( dup2(thepipe[0], 0) == -1 )
                oops("could not redirect stdin",3);
    
            close(thepipe[0]);  
            execlp( av[2], av[2], NULL);
            oops(av[2], 4);
        }
    
        close(thepipe[0]);      
    
        if ( dup2(thepipe[1], 1) == -1 )
            oops("could not redirect stdout", 4);
    
        close(thepipe[1]);      
        execlp( av[1], av[1], NULL);
        oops(av[1], 5);
    }

    8.pipe文件夹-stdinredir1.c文件

    #include    <stdio.h>
    #include    <fcntl.h>
    
    int main()
    {
        int fd ;
        char    line[100];
    
        fgets( line, 100, stdin ); printf("%s", line );//就是打印输入的字符串
        fgets( line, 100, stdin ); printf("%s", line );
        fgets( line, 100, stdin ); printf("%s", line );
    
        close(0);
        fd = open("/etc/passwd", O_RDONLY);
        if ( fd != 0 ){//打开或者创建失败的时候才会执行
            fprintf(stderr,"Could not open data as fd 0
    ");
            exit(1);
        }
    
        fgets( line, 100, stdin ); printf("%s", line );
        fgets( line, 100, stdin ); printf("%s", line );
        fgets( line, 100, stdin ); printf("%s", line );
    }

    9.pipe文件夹-testtty.c文件

    #include <unistd.h>
    int main()
    {
        char *buf = "abcde
    ";
        write(0, buf, 6);
    }
    

    【write函数】 

    write(int handle,void *buf,int nbyte); 第一个参数是文件描述符,第二个参数是指向一端内存单元的指针,第三个参数是要写入指定文件的字节个数;成功时返回字节个数,否则返回-1。

    10.signal文件夹-sigactdemo1.c文件

    #include    <stdio.h>
    #include<unistd.h>
    #include    <signal.h>
    #define INPUTLEN    100
    void inthandler();  
    int main()
    {
        struct sigaction newhandler;//见下方解释 
        sigset_t blocked;   //信号集,用来描述信号的集合,与信号阻塞相关函数配合使用
        char x[INPUTLEN];
        newhandler.sa_handler = inthandler; //函数指针
        newhandler.sa_flags = SA_RESTART|SA_NODEFER
            |SA_RESETHAND;  //sa_flags是一个位掩码。这里,第一个参数使得被信号打断的一些原语“正常返回”
        sigemptyset(&blocked);  
        sigaddset(&blocked, SIGQUIT);   
        newhandler.sa_mask = blocked;   
        if (sigaction(SIGINT, &newhandler, NULL) == -1)
            perror("sigaction");
        else
            while (1) {
                fgets(x, INPUTLEN, stdin);
                printf("input: %s", x);
            }
        return 0;
    }
    void inthandler(int s)
    {
        printf("Called with signal %d
    ", s);
        sleep(s * 4);
        printf("done handling signal %d
    ", s);
    }
    

    具体参见http://www.cnblogs.com/gogly/articles/2416989.html 

    【sigaction结构体,用来查询或设置信号处理方式。比如它指定了对特定信号的处理,信号所传递的信息,信号处理函数执行过程中应该屏蔽掉哪些函数等。】

    11.signal文件夹-sigdemo1.c文件夹

    #include    <stdio.h>
    #include    <signal.h>
    void    f(int);         
    int main()
    {
        int i;
        signal( SIGINT, f );        
        for(i=0; i<5; i++ ){        
            printf("hello
    ");
            sleep(2);
        }
    
        return 0;
    }
    
    void f(int signum)          
    {
        printf("OUCH!
    ");
    }
    

    这样的代码是无法在输出的时候体现出对f的调用的。我认为其中signal( SIGINT, f );这一句中,因为f的返回值是“void”,所以无法体现。

    补充:signal函数,原型 signal(参数1,参数2);,其中参数1是我们进行处理的信号,参数2是我们处理的方式。

    后来我对代码进行了修改,体现了对f函数的调用

    12.signal文件夹-sigdemo3.c文件

    #include    <stdio.h>
    #include<string.h>
    #include    <signal.h>
    #include<unistd.h>
    
    #define INPUTLEN    100
    
    int main(int argc, char *argv[])
    {
        void inthandler(int);
        void quithandler(int);
        char input[INPUTLEN];
        int nchars;
    
        signal(SIGINT, inthandler);//^C 
        signal(SIGQUIT, quithandler);//^
    
        do {//输入什么,就输出什么(在read函数不发生错误的情况下)
            printf("
    Type a message
    ");
            nchars = read(0, input, (INPUTLEN - 1));
            if (nchars == -1)
                perror("read returned an error");
            else {
                input[nchars] = '';
                printf("You typed: %s", input);
            }
        }
        while (strncmp(input, "quit", 4) != 0);//只有输入quit的时候才会退出
        return 0;
    }
    
    void inthandler(int s)
    {
        printf(" Received signal %d .. waiting
    ", s);
        sleep(2);
        printf("  Leaving inthandler 
    ");
    }
    
    void quithandler(int s)
    {
        printf(" Received signal %d .. waiting
    ", s);
        sleep(3);
        printf("  Leaving quithandler 
    ");
    }

    13.exec3.c文件

    #include <stdio.h>
    #include <unistd.h>
    
    int main()
    {
        char    *arglist[3];
        char*myenv[3];
        myenv[0] = "PATH=:/bin:";
        myenv[1] = NULL;
    
        arglist[0] = "ls";
        arglist[1] = "-l";
        arglist[2] = 0 ;
        printf("* * * About to exec ls -l
    ");
    //  execv( "/bin/ls" , arglist );
    //  execvp( "ls" , arglist );
    //  execvpe("ls" , arglist, myenv);
    
        execlp("ls", "ls", "-l", NULL);
        printf("* * * ls is done. bye
    ");
    }
    

    【execlp函数?】 

    从PATH环境变量中查找文件并执行。原型:int execlp(const char *file,const char *arg,……); 从PATH环境变量所指的目录中查找符号参数file的文件名,然后将第二个及以后的参数当作该文件的argv[0],argv[1],……,最后一个参数必须用NULL结束。

    【execv函数?】 

    原型:int execv(const char *pathname,char *const argv[]);装入并运行其他程序 对比:execvp函数原型: int execvp(const char *file,char *const argv[]);

    二、关于执行时的困难&解决

    1.a.out如何运行?

    在Linux系统中,如果没有给编译的文件指定文件名,就会自动命名为*.out文件。执行该类文件的时候,直接是 绝对路径/a.out。  经过尝试,发现给出的a.out文件“格式错误”,无法运行。  后经依次尝试,发现给出的所有可执行文件都有类似的问题。这个问题的原因在下面的操作中逐渐得到了解答。

    (选择打开方式为“在终端执行”)

    2.如何在linux中执行.sh文件?

    (参考http://zhidao.baidu.com/question/1733278229438599307.html

    首先你要让文件有能够执行的权限,比如你的文件是a.sh那么你可以 chmod +x a.sh 然后运行文件就可以了 ./a.sh 这样运行是a.sh在当前工作目录,如果文件没在当前目录,那么就需要用绝对路径来执行,比如 /opt/a.sh /opt/test/a.sh

    3.关于指针数组与数组指针的区别?

    (参考http://www.cnblogs.com/mq0036/p/3382732.html

    • 数组指针(也称行指针)

      • 定义 int (*p)[n];()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
      • 如要将二维数组赋给一指针,应这样赋值:

        int a[3][4];    
        int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
        p=a;//将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
        p++;   //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
        
      • 数组指针也称指向一维数组的指针,亦称行指针。

    • 指针数组

      • 定义 int p[n];[]优先级高,先与p结合成为一个数组,再由int说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 p=a; 这里p表示指针数组第一个元素的值,a的首地址的值。
      • 如要将二维数组赋给一指针数组:

        int *p[3];
        int a[3][4];
        p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
        for(i=0;i<3;i++)
            p[i]=a[i];
        

        这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2] 所以要分别赋值。

    4.关于函数指针和指针函数的区别?

    参考(http://blog.csdn.net/htyurencaotang/article/details/11490081

    • 指针函数是指带指针的函数,即本质是一个函数。函数返回类型是某一类型的指针

      • 类型标识符 *函数名(参数表) ;具体格式:int *f(x,y);

      • 首先它是一个函数,只不过这个函数的返回值是一个地址值。指针函数一定有函数返回值,而且在主调函数中,函数返回值必须赋给同类型的指针变量。例如:

        01.float *fun();  
        02.float *p;  
        03.p = fun(a);  
        
    • 函数指针是指向函数的指针变量,即本质是一个指针变量。 

      • 指向函数的指针包含了函数的地址,可以通过它来调用函数。声明格式如下: 类型说明符 (*函数名)(参数)
      • 使用的时候:

        01.int (*f)(int x); /*声明一个函数指针 */  
        02.f=func; /*将func函数的首地址赋给指针f */  
        

    5.execl1执行故障?

    在直接 gcc execl1 -o execl1.2(因为之前已经有execl可执行文件了)之后,系统提示如图。后来改变方法,先生成.o 文件再生成可执行文件,就可以执行代码了。

    通过网上搜索,发现出现这个错误提示是因为使用了第三方库而没有把它的.m文件添加到compile source中去,而是可能只把这些文件copy到当前文件夹里就觉得可以了;这样在直接编译链接执行的时候根本没有自动引用(网上提供的解决方法建立在专门的编写软件基础上,在target--build phases--compile source中添加.m;不过针对我们的情况,可以分步完成以保证最终生成可执行文件)。

    6.consumer执行故障?

    开始的时候直接gcc consumer.o -o consumer仍然出现上文中的错误。后来删除consumer .o 文件之后分步编译,可以执行。

    7.producer.c函数运行错误?

    开始的时候,提示有错误。如图。  后来查询了access函数,发现它的功能是确定文件或者文件夹的访问权限。如果指定的存取方式有效,则返回0;否则返回-1.其中,f_ok只是判断该文件是否存在。而整个函数需要头文件<unistd.h>。加上该头文件之后,代码可以正常编译。

    三、其他代码执行结果

    (fifo文件夹-c,p,tags代码执行)

    (forkdemo1.c执行)

    (forkdemo2.c文件)

    (forkdemo3.c执行)

    (forkdemo4.c执行)

    (forkgdb.c执行)

    (listargs.c代码执行)

    (pipe.c代码执行)

    (pipedemoo2.c代码执行。其中,其主体结构为do……while(1),是一个无法停止的循环。可以用ctrl+C强制退出代码执行)

    (sigdemo2.c代码执行,同上)

    (stdindir2.c代码执行)

    (testbuf1.c代码执行)

    (testpid.c代码执行)

    (testpp.c代码执行;并没有结果输出,知识申请了一个20字节大小的空间)

    (waitdemo1.c代码执行)

    (waitdemo2.c代码执行)

    (watch.sh程序,每sleep30秒打印一次)

    (watch2.sh程序,结果同上)

    四、疑问

    1.关于argtest.c代码

    snew = s + strspn(s, delimiters);//返回字符串s开头连续包含字符串delimiters内的字符数目  
    if ((t = malloc(strlen(snew) + 1)) == NULL) 
        return -1; 
    

    strspn函数返回的是字符个数,怎么再加上字符串s ?

    2.res = mkfifo(FIFO_NAME, 0777);我知道0777是对应的权限;然而这四个数字分别对应哪些用户的权限?

    补充:每个文件的三组权限(拥有者,所属用户组,其他用户,记住这个顺序是一定的)

    3.sigactdemo1.c代码:

    newhandler.sa_handler = inthandler; 这样赋值对吗?(原型是void inthandler(int s);)

    五、心得

    这次学习过程与之前有着很大的不同。就像我在期中总结的时候说的那样,我在实践方面需要进一步加强;这次如此多的代码的执行正好很好地锻炼了我的动手能力。比起理解代码,动手让代码“跑出来”更需要自己动脑筋(百科里会有对某个函数的详细解释,但很少有对某段代码如何运行的教程)。

  • 相关阅读:
    自定义View的ToolBar布局报错Error:(2) No resource identifier found for attribute 'context' in package 'c
    在学git之主分支 branch
    获取发布版SHA1
    关于开启线程与UI的操作
    播放音频和视频(VideoView控件)
    通知栏Notification的应用
    Android 真机调式 Installation failed with message 远程主机强迫关闭了一个现有的连接。. It is possible that this issue is resolved by uninstalling an existing version of the apk if it is present, and then re-installing. WA
    运行程序申请危险权限
    mysql乐观锁总结和实践
    Nginx配置文件nginx.conf中文详解
  • 原文地址:https://www.cnblogs.com/lwr-/p/5004420.html
Copyright © 2011-2022 走看看