预计时间(10小时)

运行代码 6小时
整理笔记 3小时

实际时间(12小时)

运行代码 7小时
整理笔记 3小时

代码实践

代码运行及理解

1、exec1.c

功能:装入并运行其它程序的函数

 #include <stdio.h>#include <unistd.h>int main(){char    *arglist[3];arglist[0] = "ls";arglist[1] = "-l";arglist[2] = 0 ;//NULLprintf("* * * About to exec ls -l\n"); execvp( "ls" , arglist );//第一个参数传递的是文件名 printf("* * * ls is done. bye"); return 0; }

运行结果:

分析:

根据代码我们知道,执行的指令是:

ls -l
开始显示“ * * * About to exec ls -l” 
执行完之后应该显示“ * * * ls is done. bye”

然后从上图中,我们可以看到,并没有显示最后一句,这是因为在系统处理器中,在执行execvp( "ls" , arglist );语句时,已经将最后的打印语句覆盖掉了,处理器中并没有这句打印语句,所以最终结果如图所示。

2.exce2.c

功能:装入并运行其它程序的函数

  #include <stdio.h>#include <unistd.h>int main(){char    *arglist[3];arglist[0] = "ls";arglist[1] = "-l";arglist[2] = 0 ; printf("* * * About to exec ls -l\n"); execvp( arglist[0] , arglist ); printf("* * * ls is done. bye\n"); }

运行结果

与exec1.c的结果相同(见上两图)

分析:

exec2.c的代码运行结果与exec1.c的结果相同,说明两者实现的功能是一样的,但是唯一的区别就在于

execvp( arglist[0] , arglist ); 
//将文件名存放在arglist[0]中

这个语句上,在exec1.c中,这句代码这这样写的:

execvp( "ls" , arglist );

也就是把“ls”替换成了“arglist[0]”,所以并不会影响结果。

3.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\n"); // execv( "/bin/ls" , arglist ); // execvp( "ls" , arglist ); // execvpe("ls" , arglist, myenv);  execlp("ls", "ls", "-l", NULL); printf("* * * ls is done. bye\n"); }

运行结果

我分别把代码中注释的行依次放开,单独运行,结果还是和exec1.c中的结果一样,还是不能执行出最后一条打印语句。

查看帮助文档

分析:

运行结果还是和exec1.c的一样 
从代码中我们可以看出:

    execv( "/bin/ls" , arglist );//第一个参数传递的是路径execvp( "ls" , arglist );//第一个参数传递的是文件名execvpe("ls" , arglist, myenv);//比execvp函数,增加了第三个参数:环境变量execlp("ls", "ls", "-l", NULL);//第一个参数表示文件名,

上面的语句和exec1.c中的语句相同

    execvp( "ls" , arglist );

注意:exce族的区别

(1):这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。

(2)规律: 
①、不带字母p(表示 path)的exec函数第一个参数必须是程序的相对路径或绝对路径,例如“/bin/ls”或“./a.out”,而不能是“ls”或“a.out”。 
②、对于带字母p的函数: 如果参数中包含/,则将其视为路径名。否则视为不带路径的程序名,在PATH环境变量的目录列表中搜索这个程序。 
③、带有字母l(表示list)的exec函数要求将新程序的每个命令行参数都当作一个参数传给它,命令行参数的个数是可变的,因此函数原型中有…,…中的最后一个可变参数应该是 NULL,起sentinel的作用。 
④、对于带有字母v(表示vector)的函数,则应该先构造一个指向各参数的指针数组,然后将该数组的首地址当作参数传给它,数组中的最后一个指针也应该是NULL,就像main函数的argv参数或者环境变量表一样。

(3):对于以e(表示environment)结尾的exec函数,可以把一份新的环境变量表传给它,其他exec函数仍使用当前的环境变量表执行新程序。

(4): 事实上,只有execve是真正的系统调用,其它五个函数最终都调用execve,所以execve在man手册第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。

4、forkdemo1.c

fork函数: :将运行着的程序分成2个(几乎)完全一样的进程,每个进程都启动一个从代码的同一位置开始执行的线程。若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1。

#include    <stdio.h>#include    <sys/types.h>#include    <unistd.h>int main(){int ret_from_fork, mypid;mypid = getpid();              printf("Before: my pid is %d\n", mypid); ret_from_fork = fork(); sleep(1); printf("After: my pid is %d, fork() said %d\n", getpid(), ret_from_fork); return 0; }

运行结果

分析:

从图中可以看出,After打印语句打印了两次,第一次打印的After语句是父进程执行的,因为fork函数的返回值不是0,说明是父进程在执行,第二次打印的After语句是子进程执行的,因为fork函数的返回值是0,而我的id是子进程id。

5、forkdemo2.c

  #include <stdio.h>#include <unistd.h>int main(){printf("before:my pid is %d\n", getpid() );fork();fork();printf("aftre:my pid is %d\n", getpid() );return 0;}

运行结果

分析:

因为执行了两次fork函数,执行第一次,分出2个线程,执行第二次,之前的两个线程分别分出2个线程,所以一共是四个线程,最终出现4次After语句。

6、forkdemo3.c

 #include    <stdio.h>#include    <stdlib.h>#include    <unistd.h>int fork_rv;int main(){printf("Before: my pid is %d\n", getpid());fork_rv = fork();       /* create new process   */if ( fork_rv == -1 )        /* check for error  */ perror("fork"); else if ( fork_rv == 0 ){ printf("I am the parent. my child is %d\n", getpid()); exit(0); } else{ printf("I am the parent. my child is %d\n", fork_rv); exit(0); } return 0; }

运行结果

分析:

fork函数会将一个进程分成两个进程,并且会返回两次,所以如上图所示,我们可以看到,出现了一次“I am the parent. my child is 4954”,又出现了一次“I am the parent. my child is 4954”。这个c文件,还包括了错误处理,提高了代码的健壮性。

7、forkdemo4.c

 #include    <stdio.h>#include    <stdlib.h>#include    <unistd.h>int main(){int fork_rv;printf("Before: my pid is %d\n", getpid());fork_rv = fork();       /* create new process   */if ( fork_rv == -1 )        /* check for error  */ perror("fork"); else if ( fork_rv == 0 ){ printf("I am the child. my pid=%d\n", getpid()); printf("parent pid= %d, my pid=%d\n", getppid(), getpid()); exit(0); } else{ printf("I am the parent. my child is %d\n", fork_rv); sleep(10); exit(0); } return 0; }

运行结果

现象:最后一行是10s之后才出现的

分析:

这是因为sleep(10)函数,使父进程睡眠10s再执行exit(0)语句。 
注释掉sleep的结果如图:

因为,没有sleep的休眠,父进程直接执行exit(0)退出进程,出现了输入命令的提示行;而此时子进程还没有退出,之后子进程执行到exit(0)再退出,然而这次没有出现输入命令的提示行。

8、forkgdb.c

#include <stdio.h>#include <stdlib.h>#include <unistd.h>int  gi=0;int main(){int li=0;static int si=0; int i=0; pid_t pid = fork(); if(pid == -1){ exit(-1); } else if(pid == 0){ for(i=0; i<5; i++){ printf("child li:%d\n", li++); sleep(1); printf("child gi:%d\n", gi++); printf("child si:%d\n", si++); } exit(0); } else{ for(i=0; i<5; i++){ printf("parent li:%d\n", li++); printf("parent gi:%d\n", gi++); sleep(1); printf("parent si:%d\n", si++); } exit(0); } return 0; }

运行结果

分析:

开始时,先进入父进程的循环,打印parent li:0和parent gi:0,接着休眠一秒,子进程打印child li:0,休眠一秒,父进程接着打印parent si:0,又休眠,循环往复进行,直到跳出循环,从上图中,我们可以看到,是父进程先结束,退出,出现输入命令的提示行,接着子进程打印完最后两句后,也退出。

9、psh1.c

功能:执行用户输入的指令,该指令用数组存储。

 #include    <stdio.h>#include    <stdlib.h>#include    <string.h>#include    <unistd.h>#define MAXARGS     20              #define ARGLEN      100             int execute( char *arglist[] ){execvp(arglist[0], arglist);        perror("execvp failed"); exit(1); } char * makestring( char *buf ) { char *cp; buf[strlen(buf)-1] = '\0'; cp = malloc( strlen(buf)+1 ); if ( cp == NULL ){ fprintf(stderr,"no memory\n"); exit(1); } strcpy(cp, buf); return cp; } int main() { char *arglist[MAXARGS+1]; int numargs; char argbuf[ARGLEN]; numargs = 0; while ( numargs < MAXARGS ) { printf("Arg[%d]? ", numargs); if ( fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n' ) arglist[numargs++] = makestring(argbuf); else { if ( numargs > 0 ){ arglist[numargs]=NULL; execute( arglist ); numargs = 0; } } } return 0; }

运行结果

失败的情况一:

失败的情况二:

成功的情况:

分析:

首先while循环输入命令,并将输入的值转换为字符串型,直到输入回车换行时,调用execute函数,将存储命令的数组作为参数传入,实现执行指令的功能。 
运行结果中失败的情况,分别显示不同错误的处理方式不同。

10、psh2.c

功能:在子进程中执行用户输入的指令,利用wait函数,通过父进程,实现循环输入指令。

 #include    <stdio.h>#include    <stdlib.h>#include    <string.h>#include    <sys/types.h>#include    <sys/wait.h>#include    <unistd.h> #include <signal.h> #define MAXARGS 20 #define ARGLEN 100 char *makestring( char *buf ) { char *cp; buf[strlen(buf)-1] = '\0'; cp = malloc( strlen(buf)+1 ); if ( cp == NULL ){ fprintf(stderr,"no memory\n"); exit(1); } strcpy(cp, buf); return cp; } void execute( char *arglist[] ) { int pid,exitstatus; pid = fork(); switch( pid ){ case -1: perror("fork failed"); exit(1); case 0: execvp(arglist[0], arglist); perror("execvp failed"); exit(1); default: while( wait(&exitstatus) != pid ) ; printf("child exited with status %d,%d\n", exitstatus>>8, exitstatus&0377); } } int main() { char *arglist[MAXARGS+1]; int numargs; char argbuf[ARGLEN]; numargs = 0; while ( numargs < MAXARGS ) { printf("Arg[%d]? ", numargs); if ( fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n' ) arglist[numargs++] = makestring(argbuf); else { if ( numargs > 0 ){ arglist[numargs]=NULL; execute( arglist ); numargs = 0; } } } return 0; }

运行结果

分析:

这个代码与psh1.c代码最大的区别就在于execute函数。 
调用wait(&status)等价于调用waitpid(-1.&status,0),当option=0时,waitpid挂起调用进程的执行,直到它的等待集合中的一个子进程终止。只要有一个子进程没有结束,父进程就被挂起。所以当wait返回pid时没说明,子进程都已经结束,即用户输入的指令都已经执行完毕。因为execute函数在大的循环中调用,所以会循环执行下去,除非用户强制退出。 
另外,当子进程正常执行完用户指令后,子进程的状态为0,若执行指令出错,子进程的状态为1.

11、testbuf1.c

功能:打印hello,但没有结束进程,若此时向标准输入设备中输入数据,屏幕上会显示

 #include <stdio.h>#include <stdlib.h>int main(){printf("hello");fflush(stdout);while(1);}

运行结果

分析:

打印hello,但没有结束进程,若此时向标准输入设备中输入数据,屏幕上会显示出来。必须用户强制退出,才能退出程序。

12、testbuf2.c

功能:同testbuf1.c一样 #include <stdio.h> int main() { printf("hello\n"); while(1); }

运行结果

分析:

从testbuf1.c和testbuf2.c的运行结果上看,我们可以猜出fflush(stdout);的功能就是打印换行符。

13、testbuf3.c

功能:比较打印的差别 #include <stdio.h>

 int main(){fprintf(stdout, "1234", 5);fprintf(stderr, "abcd", 4);}

运行结果

分析:

根据结果,我猜测,是因为stderr缓冲区的大小为4,所以,没有‘\0’,所以它先打印,因为字符串必须有‘\0’才能结束,所以最终结果如图所示。

14、testpid.c

功能:显示进程的id

  #include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(){printf("my pid: %d \n", getpid());printf("my parent's pid: %d \n", getppid());return 0; }

运行结果

分析:

显示进程id和其父进程的id

15、testpp.c

功能:

  #include <stdio.h>#include <stdlib.h>int main(){char **pp;pp[0] = malloc(20);return 0;}

运行结果

分析:

提示段错误。可能是数组越界导致的。我猜测,应该是计算机中的存储字节要大于20。 
好吧,修改成64后,还是提示段错误。不知道了。。。

16、testsystem.c

功能:

 #include    <stdlib.h>int main ( int argc, char *argv[] ){system(argv[1]);system(argv[2]);return EXIT_SUCCESS;}               /* ----------  end of function main  ---------- */

运行结果

分析:

system函数:发出一个DOS命令 
用 法: int system(char *command); 
system函数需加头文件<stdlib.h>后方可调用。 
最终就是执行用户输入的指令。

17、waitdemo1.c

功能:验证父子进程的调用顺序,测试函数sleep、wait在进程调用中的作用。

#include    <stdio.h>#include    <stdlib.h>#include    <sys/types.h>#include    <sys/wait.h>#include    <unistd.h>#define DELAY   4void child_code(int delay){printf("child %d here. will sleep for %d seconds\n", getpid(), delay); sleep(delay); printf("child done. about to exit\n"); exit(17); } void parent_code(int childpid) { int wait_rv=0; /* return value from wait() */ wait_rv = wait(NULL); printf("done waiting for %d. Wait returned: %d\n", childpid, wait_rv); } int main() { int newpid; printf("before: mypid is %d\n", getpid()); if ( (newpid = fork()) == -1 ) perror("fork"); else if ( newpid == 0 ) child_code(DELAY); else parent_code(newpid); return 0; }

运行结果

分析:

我们可以看到hildcode函数里,调用了sleep函数,这表示执行完printf("child %d here. will sleep for %d seconds\n", getpid(), delay);语句后,系统休眠4s继续进行。 
为什么这里不会让父进程继续进行?本来父子进程时并发执行的,按理说应该子进程休眠,父进程正常执行的,是因为parent
code函数里的wait_rv = wait(NULL);代码,说明要子程序执行完毕,父进程才能继续往下进行。 
所以最终结果如上图所示。

18、waitdemo2.c

功能:在waitdemo1.c的基础上,设置了状态位。

#include    <stdio.h>#include    <stdlib.h>#include    <sys/types.h>#include    <sys/wait.h>#include    <unistd.h>#define DELAY   10void child_code(int delay){printf("child %d here. will sleep for %d seconds\n", getpid(), delay); sleep(delay); printf("child done. about to exit\n"); exit(27); } void parent_code(int childpid) { int wait_rv; int child_status; int high_8, low_7, bit_7; wait_rv = wait(&child_status); printf("done waiting for %d. Wait returned: %d\n", childpid, wait_rv); high_8 = child_status >> 8; /* 1111 1111 0000 0000 */ low_7 = child_status & 0x7F; /* 0000 0000 0111 1111 */ bit_7 = child_status & 0x80; /* 0000 0000 1000 0000 */ printf("status: exit=%d, sig=%d, core=%d\n", high_8, low_7, bit_7); } int main() { int newpid; printf("before: mypid is %d\n", getpid()); if ( (newpid = fork()) == -1 ) perror("fork"); else if ( newpid == 0 ) child_code(DELAY); else parent_code(newpid); }

运行结果

分析:

waitdemo2.c和waitdemo1.c最大的不同就是:设置了子进程结束后父进程的状态位。

19、argtest.c

功能:

 #include <stdio.h>#include <stdlib.h>#include "argv.h"int main(int argc, char *argv[]) {char delim[] = " \t";int i;char **myargv; int numtokens; if (argc != 2) { fprintf(stderr, "Usage: %s string\n", argv[0]); return 1; } if ((numtokens = makeargv(argv[1], delim, &myargv)) == -1) { fprintf(stderr, "Failed to construct an argument array for %s\n", argv[1]); return 1; } printf("The argument array contains:\n"); for (i = 0; i < numtokens; i++) printf("%d:%s\n", i, myargv[i]); execvp(myargv[0], myargv); return 0; }

运行结果

分析:

编译运行出错!

20、freemakeargv.c

功能:

 #include <stdlib.h>#include "argv.h"void freemakeargv(char **argv) {if (argv == NULL)return;if (*argv != NULL)free(*argv); free(argv); }

运行结果

分析:

21、makeargv.c

功能:

#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); if ((t = malloc(strlen(snew) + 1)) == NULL) return -1; strcpy(t, snew); numtokens = 0; if (strtok(t, delimiters) != NULL) for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ; if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) { error = errno; free(t); 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; } 

运行结果

分析:

编译运行出错!

22、environ.c

功能:打印设置环境变量的值

 #include <stdio.h>#include <stdlib.h>int main(void){printf("PATH=%s\n", getenv("PATH"));setenv("PATH", "hello", 1);printf("PATH=%s\n", getenv("PATH"));#if 0 printf("PATH=%s\n", getenv("PATH")); setenv("PATH", "hellohello", 0); printf("PATH=%s\n", getenv("PATH")); printf("MY_VER=%s\n", getenv("MY_VER")); setenv("MY_VER", "1.1", 0); printf("MY_VER=%s\n", getenv("MY_VER")); #endif return 0; }

运行结果

分析:

如图所示:先打印了一开始的初始环境变量,接着重新设置环境变量,并打印输出。

23、environvar.c

功能:

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

运行结果

分析:

将外部变量environ的内容打印出来,也就是把系统相关宏值,打印出来。

24、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 \n", getpid()); pipe_fd = open(FIFO_NAME, open_mode);//判断打开文件是否成功 printf("Process %d result %d\n", 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\n", getpid(), bytes); exit(EXIT_SUCCESS); }

运行结果

分析:

输出打开文件流的进程号,以及打开文件进程号,并返回打开文件的结果。并且可以输入消息。

25、producer.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 #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); if (res != 0) { fprintf(stderr, "Could not create fifo %s \n", FIFO_NAME); exit(EXIT_FAILURE); } } printf("Process %d opening FIFO O_WRONLY\n", getpid()); pipe_fd = open(FIFO_NAME, open_mode); printf("Process %d result %d\n", 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\n"); exit(EXIT_FAILURE); } bytes += res; } close(pipe_fd); } else { exit(EXIT_FAILURE); } printf("Process %d finish\n", getpid()); exit(EXIT_SUCCESS); }

运行结果

26、testmf.c

 #include  <stdio.h>#include  <stdlib.h>#include  <sys/types.h>#include  <sys/stat.h>int main(){int res = mkfifo("/tmp/myfifo", 0777);if (res == 0) { printf("FIFO created \n"); } exit(EXIT_SUCCESS); }

运行结果

分析:

结果如上图,说明创建失败了。

27、listargs.c

功能:打印指令

 #include    <stdio.h>main( int ac, char *av[] ){int i;printf("Number of args: %d, Args are:\n", ac);for(i=0;i<ac;i++)printf("args[%d] %s\n", i, av[i]); fprintf(stderr,"This message is sent to stderr.\n"); }

运行结果

分析:

打印用户输入的指令,输出相关信息。

28、pipe.c

功能:实现管道的功能

 #include    <stdio.h>#include    <stdlib.h>#include    <unistd.h>#define oops(m,x)   { perror(m); exit(x); }int main(int ac, char **av){int thepipe[2],         newfd,              pid;                if ( ac != 3 ){fprintf(stderr, "usage: pipe cmd1 cmd2\n"); 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); }

运行结果

分析:

相当于管道的作用

29、pipedemo.c

功能:将输入输出用管道连接

 #include    <stdio.h>#include    <stdlib.h>#include    <string.h>#include    <unistd.h>int main(){int len, i, apipe[2];   char buf[BUFSIZ]; if ( pipe ( apipe ) == -1 ){ perror("could not make pipe"); exit(1); } printf("Got a pipe! It is file descriptors: { %d %d }\n", apipe[0], apipe[1]); while ( fgets(buf, BUFSIZ, stdin) ){ len = strlen( buf ); if ( write( apipe[1], buf, len) != len ){ perror("writing to pipe"); break; } for ( i = 0 ; i<len ; i++ ) buf[i] = 'X' ; len = read( apipe[0], buf, BUFSIZ ) ; if ( len == -1 ){ perror("reading from pipe"); break; } if ( write( 1 , buf, len ) != len ){ perror("writing to stdout"); break; } } }

运行结果

分析:

我猜测是打开文件,将从标准输入中输入的数据打印到标准输出上,需要强制退出。

30、pipedemo2.c

功能:

#include    <stdio.h>#include    <stdlib.h>#include    <string.h>#include    <unistd.h>#define CHILD_MESS  "I want a cookie\n"#define PAR_MESS    "testing..\n"#define oops(m,x)   { perror(m); exit(x); }main(){int pipefd[2];      int len; char buf[BUFSIZ]; int read_len; if ( pipe( pipefd ) == -1 ) oops("cannot get a pipe", 1); switch( fork() ){ case -1: oops("cannot fork", 2); case 0: len = strlen(CHILD_MESS); while ( 1 ){ if (write( pipefd[1], CHILD_MESS, len) != len ) oops("write", 3); sleep(5); } default: len = strlen( PAR_MESS ); while ( 1 ){ if ( write( pipefd[1], PAR_MESS, len)!=len ) oops("write", 4); sleep(1); read_len = read( pipefd[0], buf, BUFSIZ ); if ( read_len <= 0 ) break; write( 1 , buf, read_len ); } } }

运行结果

分析:

从上图中我们可以分析出:fork函数执行后,进程先执行父进程,父进程休眠1s,接着子进程运行;子进程打印"I want a cookie",接着休眠5s,父进程接着打印,所以出现4次testing..之后又出现子进程的打印语句。

31、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\n"); exit(1); } fgets( line, 100, stdin ); printf("%s", line ); fgets( line, 100, stdin ); printf("%s", line ); fgets( line, 100, stdin ); printf("%s", line ); }

运行结果

分析:

如上图所示:先从标准输入输入3行信息,接着分别打印这三行信息,执行打开文件语句,若打开正常,则从文件中读取前三行信息。

32、stdinredir2.c

#include    <stdio.h>#include    <stdlib.h>#include    <fcntl.h>//#define   CLOSE_DUP       //#define   USE_DUP2    main(){int fd ;int newfd;char    line[100]; fgets( line, 100, stdin ); printf("%s", line ); fgets( line, 100, stdin ); printf("%s", line ); fgets( line, 100, stdin ); printf("%s", line ); fd = open("data", O_RDONLY); #ifdef CLOSE_DUP close(0); newfd = dup(fd); #else newfd = dup2(fd,0); #endif if ( newfd != 0 ){ fprintf(stderr,"Could not duplicate fd to 0\n"); exit(1); } close(fd); fgets( line, 100, stdin ); printf("%s", line ); fgets( line, 100, stdin ); printf("%s", line ); fgets( line, 100, stdin ); printf("%s", line ); }

运行结果

分析:

上图结果表示打开文件失败的情况。

33、testtty.c

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

运行结果

分析:

将缓冲区中的内容打印出来

34、whotofile.c

#include    <stdio.h>#include    <stdlib.h>#include    <unistd.h>int main(){int pid ;int fd;printf("About to run who into a file\n"); if( (pid = fork() ) == -1 ){ perror("fork"); exit(1); } if ( pid == 0 ){ close(1); /* close, */ fd = creat( "userlist", 0644 ); /* then open */ execlp( "who", "who", NULL ); /* and run */ perror("execlp"); exit(1); } if ( pid != 0 ){ wait(NULL); printf("Done running who. results in userlist\n"); } return 0; }

运行结果

分析:

定义函数:int close(int fd); 
函数说明:当使用完文件后若已不再需要则可使用 close()关闭该文件, 二close()会让数据写回磁盘, 并释放该文件所占用的资源. 参数fd 为先前由open()或creat()所返回的文件描述词. 
返回值:若文件顺利关闭则返回0, 发生错误时返回-1. 
从结果中我们可以看出,没有子进程的执行,我们知道fork函数会产生2个返回,所以子进程是一定有的,close(1)关闭了子进程的标准输出,所以之后的执行都无法打印出来。

35、sigactdemo.c

 #include    <stdio.h>#include    <unistd.h>#include    <signal.h>#define INPUTLEN    100void 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; 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\n", s); sleep(s * 4); printf("done handling signal %d\n", s); }

运行结果

分析:

从上图的结果中我们可以看到,该代码的功能是,将标准输入的信息打印到标准输出上,需要强制退出结束进程。

36、sigactdemo2.c

#include <unistd.h>#include <signal.h>#include <stdio.h>void sig_alrm( int signo ){/*do nothing*/}unsigned int mysleep(unsigned int nsecs){struct sigaction newact, oldact; unsigned int unslept; newact.sa_handler = sig_alrm; sigemptyset( &newact.sa_mask ); newact.sa_flags = 0; sigaction( SIGALRM, &newact, &oldact ); alarm( nsecs ); pause(); unslept = alarm ( 0 ); sigaction( SIGALRM, &oldact, NULL ); return unslept; } int main( void ) { while( 1 ) { mysleep( 2 ); printf( "Two seconds passed\n" ); } return 0; }

运行结果

分析:

从上图中可以看出,该代码的功能是每2s打印"Two seconds passed"。

37、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\n"); sleep(2); } return 0; } void f(int signum) { printf("OUCH!\n"); }

运行结果

分析:

每隔2s打印一次hello。

38、sigdemo2.c

 #include    <stdio.h>#include    <signal.h>main(){signal( SIGINT, SIG_IGN );printf("you can't stop me!\n");while( 1 ){sleep(1);printf("haha\n"); } }

运行结果

分析:

每隔1s打印一次"haha"。

39、sigdemo3.c

 #include    <stdio.h>#include    <string.h>#include    <signal.h>#include    <unistd.h>#define INPUTLEN    100int main(int argc, char *argv[]){void inthandler(int);void quithandler(int); char input[INPUTLEN]; int nchars; signal(SIGINT, inthandler);//^C signal(SIGQUIT, quithandler);//^\ do { printf("\nType a message\n"); nchars = read(0, input, (INPUTLEN - 1)); if (nchars == -1) perror("read returned an error"); else { input[nchars] = '\0'; printf("You typed: %s", input); } } while (strncmp(input, "quit", 4) != 0); return 0; } void inthandler(int s) { printf(" Received signal %d .. waiting\n", s); sleep(2); printf(" Leaving inthandler \n"); } void quithandler(int s) { printf(" Received signal %d .. waiting\n", s); sleep(3); printf(" Leaving quithandler \n"); }

运行结果

分析:

从上图看出,我们从标准输入输入消息,在标准输出上打印出来。

遇到的问题

1、不是说fork函数,产生的两个线程的执行顺序是不确定的,为什么我多次执行,都是先显示父进程的,如图所示

2、testpp.c代码老是提示段错误,不知道什么原因,开始我猜测是数组越界,我把malloc函数的值改大了,还是提示段错误? 
3、argv文件夹下的c文件都不能正常编译运行,例如,argtest.c编译出现错误,如图所示。

参考资料

1、关于execl(),execlp(),execle(),execv(),execvp(),execvp(),execve()原语的区别: http://www.2cto.com/kf/201409/334500.html

 

转载于:https://www.cnblogs.com/java-stx/p/4999488.html

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

  1. 20135327郭皓——信息安全系统设计基础第十二周学习总结

    第十二周(11.23-11.29): 学习计时:共6小时 读书: 代码: 作业: 博客: 一.学习目标 掌握进程控制 掌握信号处理的方法 掌握管道和fifo进行进程间通信的方法 二.学习资源  编译. ...

  2. 信息安全系统设计基础第十五周总结

    信息安全系统设计基础第十五周总结 [内容:链接汇总] 一.每周读书笔记链接汇总 [第一周读书笔记] http://www.cnblogs.com/shadow135211/p/4824555.html ...

  3. 20145236《信息安全系统设计基础》第0周学习总结

    20145236冯佳<信息安全系统设计基础>第0周学习总结 浅析<软件工程> 通过对老师推荐的博客的阅读,让我接触到了许多以前不曾接触的东西.软件工程学,听起来有点儿高大上的感 ...

  4. 20145203 《信息安全系统设计基础》第十三周学习总结

    20145203 <信息安全系统设计基础>第十三周学习总结 第十二章 并发编程 教材学习内容总结 绪论 三种基本的构造并发程序的方法: ①进程: 每个逻辑控制流是一个进程,由内核进行调度, ...

  5. 2017-2018-1 20155227 《信息安全系统设计基础》第十三周学习总结

    2017-2018-1 20155227 <信息安全系统设计基础>第十三周学习总结 找出全书你认为最重要的一章,深入重新学习一下,要求(期末占10分): 完成这一章所有习题详细总结本章要点 ...

  6. 20145315 《信息安全系统设计基础》第14周学习总结

    20145315 <信息安全系统设计基础>第14周学习总结 课程内容总结 物理地址和虚拟地址 物理地址:计算机系统的主存被组织成一个由M个连续的字节大小的单元组成的数组.每个字节都有唯一的 ...

  7. 20145209 《信息安全系统设计基础》第14周学习总结

    20145209 <信息安全系统设计基础>第14周学习总结 教材学习内容总结 虚拟存储器是计算机系统最重要的概念之一,它是对主存的一个抽象 三个重要能力: 它将主存看成是一个存储在磁盘上的 ...

  8. 20145325张梓靖 《信息安全系统设计基础》第13周学习总结

    20145325张梓靖 <信息安全系统设计基础>第13周学习总结 教材学习 客户端--服务器编程模型 每个网络应用都是基于客户端--服务器模型的.一个应用是由一个服务器进程和多个客户端进程 ...

  9. 20144303石宇森 《信息安全系统设计基础》第13周学习总结

    20144303石宇森 <信息安全系统设计基础>第13周学习总结 教材内容学习 网络编程 客户端-服务器编程模型 一个应用是由一个服务器进程和一个或多个客户端进程组成 服务器进程 -> ...

最新文章

  1. 结构体成员数组不定长如何实现
  2. 走你!Github 开源整合
  3. Windows下本机简易监控系统搭建(Telegraf+Influxdb+Grafana)--转
  4. 使用FFmpeg进行视频抽取音频,之后进行语音识别转为文字
  5. 程序员,你是选择25k的996还是18k的八小时工作日
  6. [WPF]静态资源(StaticResource)和动态资源(DynamicResource)
  7. MySQL复制常见问题处理
  8. 安装了silverlight还是提示_win10系统安装.netframework3.5方法
  9. 色彩搭配总是显得很乱?配色专辑把色彩简单化
  10. iPhone xs是最尴尬的一款手机吗?为什么?
  11. linux安装mvn及nexus远程仓库
  12. 【数据处理】 python 极速极简画图——频数(率)分布直方图
  13. springboot 多模块 Found multiple @SpringBootConfiguration annotated classes
  14. pyth命令_如何:在Windows上设置用于从命令行运行.py文件的Python可执行文件
  15. 数学不好能学计算机编程吗,数学很差能学计算机吗 有哪些窍门
  16. Chrom for mac下载
  17. C语言简介之进制转换,原码、反码、补码,位运算符,函数
  18. Web调用浏览器摄像头
  19. 【xshell】xshell到期提醒更新问题解决
  20. 基于STM32和超声波测距传感器的测距功能设计

热门文章

  1. python画折线图代码-Python绘制折线图和散点图的详细方法介绍(代码示例)
  2. python怎么加载图片-如何用python获取图像
  3. python百度网盘-bypy-百度网盘Python客户端 linux
  4. python画图三维-Python使用matplotlib绘制三维图形示例
  5. python从入门到精通视频教程百度云-let's python从入门到精通视频教程
  6. 自学python爬虫要多久-入门Python爬虫要学习多久?
  7. python保留字-9.Python关键字(保留字)一览表
  8. python有什么作用-大数据学习之python语言有什么作用?
  9. 0基础学python-如何从零基础自学Python?
  10. python代码怎么运行-python代码是怎样运行的