当前位置: 首页 > web >正文

Linux程序设计--期末复习

阅读以下程序:#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>int main()
{int in, out;int n = 0;char c;in = open("./1.txt", O_RDONLY);out = open("./1_back.txt", O_WRONLY);if (in == -1 || out == -1){ // 1printf("open file error\n");exit(0);}n = read(in, &c, 1);while (n > 0){write(out, &c, 1);n = read(in, &c, 1);}close(in);close(out);exit(0);
}以下关于如何改进与提高该程序,正确的是(D)。A.out = open("./1_back.txt", O_WRONLY); 改为out = open("1_back.txt", O_WRONLY); ,可以显著提高性能和效率。
B.while(n > 0) 改为while(n > 1), 可以减少读写次数,可以显著提高性能和效率。
C.in = open("./1.txt", O_RDONLY); 改为in = open("1.txt", O_RDONLY);,可以显著提高性能和效率。
D.char c定义改为char c[256]; 其它使用到c的地方做响应修改。并引入memeset对c进行初始化。这样可以显著提高性能和效率。

阅读以下程序,有关叙述错误的是,#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>int main(int c, char *a[])
{DIR *dir;struct dirent *pitem;if (c <= 1){printf("para error\n");exit(2);}dir = opendir(a[1]);if (dir == 0){printf("open dir failed\n");exit(1);}printf("d_ino,         filename\n");while ((pitem = readdir(dir)) != 0)printf("%ld         %s\n", pitem->d_ino, pitem->d_name);closedir(dir);exit(0);
}A.假设该程序编译链接后的运行程序名为mydir,当(在mydir所在目录)运行./mydir 时,会输出“para error”并结束。
B.该示例程序展示了文件目录的基本操作接口:readdir,opendir, closedir, writedir等基本用法。 
C.去掉 #include<stdlib.h> 语句 ,(系统默认编译设置下)会有exit调用语句有关的警告信息。 
D.假设该程序编译链接后的运行程序名为mydir,当(在mydir所在目录)运行./mydir /tmp时,会输出 /tmp目录包含的每个文件(含目录)的inode节点号及文件(含目录)名,然后结束。

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>void list_directory(const char *path, int show_details, int depth); //A// 显示文件权限
void print_permissions(mode_t mode) {printf((S_ISDIR(mode)) ? "d" : "-");printf((mode & S_IRUSR) ? "r" : "-");printf((mode & S_IWUSR) ? "w" : "-");printf((mode & S_IXUSR) ? "x" : "-");printf((mode & S_IRGRP) ? "r" : "-");printf((mode & S_IWGRP) ? "w" : "-");printf((mode & S_IXGRP) ? "x" : "-");printf((mode & S_IROTH) ? "r" : "-");printf((mode & S_IWOTH) ? "w" : "-");printf((mode & S_IXOTH) ? "x" : "-");
}// 显示文件详细信息
void print_file_details(const char *path, const char *filename, int show_details, int depth) {struct stat statbuf;char full_path[1024], path2[1024];snprintf(full_path, sizeof(full_path), "%s/%s", path, filename);
//    printf("%s in print file  details fun\n",path);if (stat(full_path, &statbuf) == -1) {perror("stat");return;}printf("%*s", depth, "");// 文件权限print_permissions(statbuf.st_mode);printf(" ");// 硬链接数printf("%ld ", statbuf.st_nlink);// 文件所有者struct passwd *pw = getpwuid(statbuf.st_uid);if (pw) {printf("%s ", pw->pw_name);} else {printf("%d ", statbuf.st_uid);}// 文件所属组struct group *gr = getgrgid(statbuf.st_gid);if (gr) {printf("%s ", gr->gr_name);} else {printf("%d ", statbuf.st_gid);}// 文件大小printf("%8ld ", statbuf.st_size);// 最后修改时间char timebuf[80];strftime(timebuf, sizeof(timebuf), "%b %d %H:%M", localtime(&statbuf.st_mtime));printf("%s ", timebuf);// 文件名printf("%s\n", filename);if (S_ISDIR(statbuf.st_mode) && depth > 0) {list_directory(full_path, show_details, depth + 4);}
}// 列出目录内容
void list_directory(const char *path, int show_details, int depth) {DIR *dir;struct dirent *entry;char path2[1024];struct stat mybuf;//printf("%s\n",path);if ((dir = opendir(path)) == NULL) {perror("opendir");return;}while ((entry = readdir(dir)) != NULL) {if (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0)continue;if (show_details) {print_file_details(path, entry->d_name, show_details, depth);//closedir(dir);} else {printf("%*s %s\n", depth, "", entry->d_name);snprintf(path2, sizeof(path2), "%s/%s", path, entry->d_name);lstat(path2, &mybuf);if (S_ISDIR(mybuf.st_mode) && depth > 0)list_directory(path2, show_details, depth + 4);}}closedir(dir);
}int main(int argc, char *argv[]) {const char *path = ".";int show_details = 0;int depth = 0;// 解析命令行参数if (argc > 1) {for (int i = 1; i < argc; i++) {if (strcmp(argv[i], "-r") == 0)depth = 1;else if (strcmp(argv[i], "-l") == 0) {show_details = 1;} else {path = argv[i];}}}// 列出目录内容
//    printf("%s\n",path);list_directory(path, show_details, depth);return 0;
}A. 将前面(//A对应行)的list_directory函数声明删除注销,(-o)编译链接时会有警告信息出现;
B. 假设该程序编译链接后的运行程序名为myls,当(在myls所在目录)运行./myls -l  /usr/local时,会输出/usr/local目录包含的每个文件的详细信息(长格式),然后结束。
C. 假设该程序编译链接后的运行程序名为myls,当(在myls所在目录)运行./myls -r  /usr/local时,会按层进方式输出/usr/local目录包含的各级文件(含目录)名,然后结束。
D. 该程序中有一个递归,就是list_directory直接调用自己形成递归,没有其它形式的。

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>void printdir(char *dir, int depth)
{DIR *dp;struct dirent *entry;struct stat statbuf;if ((dp = opendir(dir)) == NULL) {fprintf(stderr, "cannot open directory: %s\n", dir);return;}chdir(dir);while ((entry = readdir(dp)) != NULL) {lstat(entry->d_name, &statbuf);if (S_ISDIR(statbuf.st_mode)) {/* Found a directory, but ignore . and .. */if (strcmp(".", entry->d_name) == 0 ||strcmp("..", entry->d_name) == 0)continue;printf("%*s%s/\n", depth, "", entry->d_name);/* Recurse at a new indent level */printdir(entry->d_name, depth + 4);}elseprintf("%*s%s\n", depth, "", entry->d_name);}chdir("..");closedir(dp);
}int main(int argc, char* argv[])
{char *topdir, pwd[2] = ".";if (argc != 2)topdir = pwd;elsetopdir = argv[1];printf("Directory scan of %s\n", topdir);printdir(topdir, 0);printf("done.\n");exit(0);
}A.S_ISDIR(statbuf.st_mode)是判断(文件类型)是否是目录
B. 假设该程序编译链接后的运行程序名为mys,当(在mys所在目录)运行./mys  时,会输出当前目录包含的各级文件(含目录)名(以及最后的Done.),并结束。
C.if(strcmp(".",entry->d_name) == 0 ||strcmp("..",entry->d_name) == 0)continue;语句不仅仅会使得程序不打印输出.(以及..)隐藏文件名,也由于continue语句,使得不会出现"."调用"."的局面;D. 该程序没有递归终止条件,因此它会一直递归调用下去,不会停止,直到系统资源耗尽。正确答案:D:该程序没有递归终止条件,因此它会一直递归调用下去,不会停止,直到系统资源耗尽

// 程序一/program1.c
#include <stdlib.h>
#include <stdio.h>int main() {system("ls -l");printf("Ok\n");exit(0);
}// 程序二/program2.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>int main() {execlp("ls", "ls", "-l", NULL);printf("OK\n");exit(1);
}A. 程序一(成功)运行后,最后一行是OK。
B. 当它们(成功编译链接后)在相同目录下成功运行后,输出结果是一样的。
C. 程序二运行时,其实原进程被替换了,即当执行ls -l命令,执行该命令的(Shell)进程将原进程替换了。
D. 程序一运行时,其实有多个进程,一个是program1程序运行的进程,两外一个是执行ls -l命令的(Shell)进程。
  1. system() 函数的工作原理(对应程序一)

    • system() 函数会创建一个新的 Shell 进程,在该进程中执行指定的命令(如 ls -l)。
    • 原进程会等待 Shell 进程执行完毕后,继续执行后续代码(如 printf("Ok\n"))。
    • 答案:选项 A 正确。程序一成功运行后,会先输出 ls -l 的结果,最后一行是 “Ok”。
  2. execlp() 函数的工作原理(对应程序二)

    • execlp() 函数会用新的程序(如 ls)替换当前进程的映像,原程序的后续代码不会被执行。
    • 如果 execlp() 执行成功,printf("OK\n")exit(1) 永远不会被执行;只有当 execlp() 失败时才会继续执行后续代码。
    • 答案:选项 C 正确。程序二运行时,原进程会被 ls 进程完全替换。
  3. 进程创建与替换的区别(对应选项 D 和 B)

    • 程序一:使用 system() 创建新的 Shell 进程,原进程与新进程同时存在,因此有多个进程。
    • 程序二:使用 execlp() 替换当前进程,没有创建新进程,只有一个进程(但进程映像被替换为 ls)。
    • 输出结果对比
      • 程序一:输出 ls -l 的结果后,还会输出 “Ok”。
      • 程序二:仅输出 ls -l 的结果(成功时),不会输出 “OK”。
    • 答案:选项 D 正确,选项 B 错误。程序一和程序二的输出结果不同,因此选项 B 是错误的。
  4. 错误处理与返回值

    • 程序二的 execlp() 后如果执行失败,会执行 printf("OK\n") 并返回 1,但成功时不会执行这些代码。
    • 程序一的 system() 会等待命令执行完毕,并返回命令的退出状态。

结论:

根据代码分析,错误选项是 B。程序一和程序二的输出结果不同,程序一最后会输出 “Ok”,而程序二(成功时)不会输出 “OK”。


// myhello.c
#include <stdlib.h>
#include <stdio.h>int main() {printf("hello world\n");exit(1);
}// myfork.c
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>int main() {int pid;int n = 0;pid = fork();switch(pid) {case -1:printf("fork error\n");break;case 0:printf("p chlid:%d\n", n);execlp("myh", "myh", NULL);printf("over\n");exit(22);default:n++;printf("p parent:%d\n", n);}printf("waiting\n");wait(&n);if (WIFEXITED(n))printf("the exit code is %d\n", WEXITSTATUS(n));elseprintf("%d process exit normally\n", n);exit(0);
}A. "p chlid:0"输出行一定在"waiting"输出行前面。因为输出“p chlid:0”的语句在输出"waiting"的语句前面。
B. 该程序创建子进程成功运行后,不一定有字符串“p chlid:0"输出。
C. 该程序创建子进程成功运行后,输出内容中一定有“p parent:1”字符串。
D. 有两行"waiting"字符串输出,父子进程各输出一行
  1. 选项A

    • 错误。虽然子进程的 printf("p chlid:0\n") 在代码顺序上先于父进程的 printf("waiting\n"),但进程调度是不确定的。父进程可能在子进程执行前就完成了自己的 printf("waiting\n")。因此,“p chlid:0” 不一定在 “waiting” 之前输出。
  2. 选项B

    • 正确。若 execlp("myh", "myh", NULL) 成功执行,子进程会被 myh 程序完全替换,不会执行后续的 printf("over\n")exit(22)。但若 execlp 失败(例如 myh 不在 PATH 中),则会执行 printf("over\n")exit(22),此时子进程会输出 “p chlid:0”。因此,子进程成功运行后(即 execlp 成功),不会有 “p chlid:0” 输出。
  3. 选项C

    • 错误。父进程的 printf("p parent:1\n")default 分支中,仅当 fork() 返回子进程的 PID(非零值)时执行。但若 fork() 失败(返回 -1),则父进程会执行 case -1 分支,不会输出 “p parent:1”。题目中虽提到“创建子进程成功运行后”,但选项C的描述是“一定有”,而实际存在 fork() 失败的情况,因此该选项错误。
  4. 选项D

    • 错误。父子进程都会执行 printf("waiting\n"),但由于 execlp 成功时子进程会被替换,不会执行该行代码。因此,通常只有父进程会输出 “waiting”。若 execlp 失败,子进程会输出 “over” 和 “waiting”,但题目假设子进程成功运行(即 execlp 成功),因此该选项错误。

正确答案是 B


// myalarm.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>int i = 0;void myalarm(int s) {i = rand() % 3;
}int main() {// int i;  // 注释掉的局部变量声明,不影响全局变量isignal(SIGALRM, myalarm);  // Awhile (1) {alarm(1);switch (i) {case 1:printf("Hello world\n");break;case 2:printf("Welcome\n");break;case 3:printf("byebye\n");break;default:printf("error\n");}sleep(1);}exit(0);
}A. 该程序逻辑表明,定义了一个全局变量i。该变量在信号处理函数和main函数中都能使用,从而起到控制switch分支的作用。
B. 该程序逻辑表明,定义了一个alarm时钟信号处理函数
C. 由于有while(1)死循环,该程序(运行时)将会有很多的Hello world输出。
D. 由于有while(1)死循环,该程序(运行时)将会有很多的Byebbye输出。
  • **信号处理函数myalarm**中,i = rand() % 3的取值为 0、1、2(因为%3的余数最大为2)。
  • switch分支中:
    • case 1对应输出Hello worldcase 2对应Welcomecase 3永远不会触发(因为i最大为2),默认分支defaulti=0时执行(输出error)。
  1. 选项A

    • 正确。全局变量i被信号处理函数和main函数共享,用于控制switch分支,符合代码逻辑。
  2. 选项B

    • 正确。通过signal(SIGALRM, myalarm)定义了闹钟信号(SIGALRM)的处理函数myalarm
  3. 选项C

    • 正确。当i=1时会输出Hello world,由于alarm(1)sleep(1)每秒触发一次信号,循环多次后可能多次输出Hello world
  4. 选项D

    • 错误i = rand() % 3的取值范围是0、1、2,永远不会等于3,因此case 3(输出byebye)永远不会被触发,该选项描述与代码逻辑矛盾。

错误的陈述是D


下面是整理好格式的代码:

// mysignal.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>void f(int s) {printf("I am in signal func\n");signal(SIGINT, SIG_DFL);  // 将SIGINT的处理恢复为默认(终止进程)
}int main() {int i;signal(SIGINT, f);  // 注册SIGINT的处理函数fwhile (1) {printf("Hello world %d\n", i++);sleep(1);}exit(0);
}A. 由于有while(1)死循环,该程序一旦启动运行就需要(外力)干预才能使它停止运行。
B. 按一次Ctrl+c能使该程序停止运行
C. 按二次Ctrl+c才能使该程序停止运行
D. 按一次Ctrl+z能使该程序停止运行
  1. 初始时,程序通过 signal(SIGINT, f)SIGINT(键盘中断,对应 Ctrl+C)的处理函数设为 f

  2. 当第一次收到 Ctrl+C 时:

    • 调用 f 函数,打印 "I am in signal func\n"
    • f 中,通过 signal(SIGINT, SIG_DFL)SIGINT 的处理恢复为 默认行为(即终止进程)。
  3. 第二次收到 Ctrl+C 时:

    • 由于处理方式已变为 SIG_DFL,程序会直接终止。
  4. 选项A

    • 正确。程序包含 while(1) 死循环,若无外部信号(如 SIGINT)干预,会一直运行,需外力终止。
  5. 选项B

    • 错误。第一次按 Ctrl+C 时,程序进入信号处理函数 f,但并未终止,而是继续循环(仅修改了后续 SIGINT 的处理方式)。按一次 Ctrl+C 不会停止程序
  6. 选项C

    • 正确。第一次 Ctrl+C 触发 f 函数,设置 SIGINT 为默认处理;第二次 Ctrl+C 时,程序按默认行为终止。因此需按两次 Ctrl+C 才能停止。
  7. 选项D

    • 正确。Ctrl+Z 发送的是 SIGTSTP 信号,程序未注册该信号的处理函数,其默认行为是暂停进程(放入后台挂起),属于“停止运行”的一种形式。

错误的陈述是B


// mypipe.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main() {int des[2];int pid;char mes[256];pipe(des);int s;pid = fork();if (pid == 0) {memset(mes, '\0', 256);read(des[0], mes, 256);printf("read:%s\n", mes);} else if (pid > 0) {write(des[1], "hello ahut\n", 10);  // 注意:字符串实际长度为11,此处写入10字节可能不完整} else {printf("error\n");}exit(0);
}A. 该程序逻辑表明,先创建管道,再创建子进程。因为这样,便于子进程继承管道的读写描述符des数组。
B. 该程序中,父进程用文件描述符des[1]向道中写数据信息
C. 该程序中,子进程用文件描述符des[0]从管道中读取数据信息
D. 父进程创建子进程失败后,将向管道中写error信息

错误选项分析:

程序核心逻辑
  1. 管道创建与进程分叉

    • 先通过 pipe(des) 创建管道,再通过 fork() 分叉进程,子进程会继承父进程的文件描述符 des[0](读端)和 des[1](写端)。
    • 父进程(pid > 0)负责向管道写端 des[1] 写入数据(字符串 “hello ahut\n”,但代码中写入长度为 10,实际应是 11 字节,存在越界风险,但不影响选项判断)。
    • 子进程(pid == 0)负责从管道读端 des[0] 读取数据并打印。
  2. fork 失败处理

    • fork() 返回负数(pid < 0)时,进入 else 分支,仅打印 “error\n”,未向管道写入任何数据
选项逐一分析
  1. 选项A

    • 正确。先创建管道再分叉进程,子进程通过继承机制获得管道描述符,这是管道在父子进程间通信的标准用法。
  2. 选项B

    • 正确。管道的写端为 des[1],父进程在 pid > 0 分支中通过 write(des[1], ...) 向管道写入数据。
  3. 选项C

    • 正确。管道的读端为 des[0],子进程在 pid == 0 分支中通过 read(des[0], ...) 从管道读取数据。
  4. 选项D

    • 错误。当 fork() 失败(pid < 0)时,程序在 else 分支中仅打印 “error\n”,并未向管道写入任何信息(如 “error”)。管道的读写操作仅在 fork() 成功后的父子进程中执行,失败时不涉及管道操作。

错误的陈述是D


下面是整理好格式的代码:

// mysig3.c
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>void fint(int s) {printf("i am in child:exiting\n");exit(0);
}int pid;void falarm(int s) {printf("paren in sig handler\n");kill(pid, SIGINT);
}int main() {pid = fork();if (pid > 0) {signal(SIGALRM, falarm);//sleep(2);alarm(2);while (1) {printf("parent\n");sleep(2);}}else if (pid == 0) {signal(SIGINT, fint);pause();}else {printf("error");}exit(0);
}A. 该程序在运行时,由于在ALARM信号处理中先打印,再给子进程发送SIGINT信息,因此“paren in sig handler”的输出应该在“i am in child:exiting”输出的前面
B. 该程序中,子进程可以注释掉pause的调用,而不影响父进程给子进程发送信号的处理,子进程也一定会输出i am in child:exiting字符串
C. 该程序逻辑表明,定义了两个信号处理函数,一个处理SIGINT,一个处理SIGALRM
D. 该程序在运行时,父进程会输出很多parent行

错误选项分析:

程序核心逻辑
  1. 进程分叉与信号注册

    • 父进程(pid > 0):设置 SIGALRM 信号处理函数 falarm,启动2秒闹钟后进入无限循环,每秒打印 “parent”。
    • 子进程(pid == 0):设置 SIGINT 信号处理函数 fint,然后调用 pause() 阻塞等待信号。
  2. 信号触发流程

    • 2秒后闹钟触发 SIGALRM,父进程执行 falarm,打印 “paren in sig handler”,并向子进程发送 SIGINT
    • 子进程收到 SIGINT 后执行 fint,打印 “i am in child:exiting” 并退出。
选项逐一分析
  1. 选项A

    • 正确。父进程在 falarm 中先打印再发送信号,子进程收到信号后才打印退出信息,因此 “paren in sig handler” 必然在 “i am in child:exiting” 之前输出。
  2. 选项B

    • 错误。若注释掉子进程的 pause(),子进程会直接执行 exit(0) 退出,不会等待父进程发送的 SIGINT,也不会输出 “i am in child:exiting”。因此该选项描述与代码逻辑矛盾。
  3. 选项C

    • 正确。程序通过 signal() 分别为 SIGINTSIGALRM 注册了处理函数 fintfalarm
  4. 选项D

    • 正确。父进程在 while(1) 循环中每秒打印 “parent”,直到子进程退出后父进程仍会继续循环打印。

结论

错误的陈述是B


// myf.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main() {char buffer[256];strcpy(buffer, "ihrllo world\0");printf("%s", buffer);exit(0);
}// mys.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main() {char buffer[256];memset(buffer, '\0', 256);scanf("%s", buffer);printf("read:%s\n", buffer);exit(0);
}// pipe1.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main() {int des[2];int pid;char mes[256];pipe(des);int s;pid = fork();if (pid == 0) {close(0);dup(des[0]);close(des[0]);close(des[1]);execl("./mys", "mys", NULL);}else if (pid > 0) {close(1);dup(des[1]);close(des[0]);close(des[1]);execl("./myf", "myf", NULL);}else {printf("error\n");}exit(0);
}A. read:ihrllo
B. 没有输出
C. read:world
D. read:ihrllo world

执行流程分析:

  1. 管道创建与进程分叉

    • pipe1.c 创建管道后分叉进程,子进程(pid == 0)执行 mys,父进程(pid > 0)执行 myf
  2. 子进程(mys)准备

    • 关闭标准输入(0),将管道读端(des[0])复制到标准输入(0),然后关闭管道两端。
    • 调用 execl 执行 mys,此时 mys 的标准输入连接到管道读端。
  3. 父进程(myf)准备

    • 关闭标准输出(1),将管道写端(des[1])复制到标准输出(1),然后关闭管道两端。
    • 调用 execl 执行 myf,此时 myf 的标准输出连接到管道写端。
  4. 数据流向

    • myf 输出 "ihrllo world" 到管道写端。
    • mys 从管道读端读取数据,使用 scanf("%s", buffer) 读取字符串,遇到空格停止,因此只能读取 "ihrllo"
    • mys 输出 "read:ihrllo\n"

结论:

正确答案是A(read:ihrllo)。


代码整理:

fifocom.h
#define FIFO_NAME  "/tmp/myfifo"struct mes {int pid;int state;
};
fiforead.c
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>int main() {char mes[256];int des;struct mes mymes;int r, iret;r = access(FIFO_NAME, F_OK);if (r == -1) {iret = mkfifo(FIFO_NAME, 0777);if (iret < 0) {printf("mkfifo error\n");exit(1);}}des = open(FIFO_NAME, O_RDONLY);read(des, &mymes, sizeof(struct mes));printf("read from fifo:%d,%d\n", mymes.pid, mymes.state);exit(0);
}
myfifow3.c
#include "fifocom.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>int main() {char mes[256];int des;struct mes mymes;int iret, res;res = access(FIFO_NAME, F_OK);if (res == -1) {iret = mkfifo(FIFO_NAME, 0777);if (iret < 0) {printf("mkfifo error");exit(0);}}des = open(FIFO_NAME, O_WRONLY);mymes.pid = getpid();mymes.state = 1;write(des, &mymes, sizeof(struct mes));printf("write to fifo:%d,%d\n", mymes.pid, mymes.state);exit(0);
}

A. 通过access判定当fifo管道还没有建立时,将建立一个管道。
B. 该管道的名字(文件名)是/tmp/myfifo
C. 当运行到open处,如果有进程已经以读方式打开该fifo管道,open会立即返回;当还没有进程以读方式打开该fifo管道,open也会立即返回。
D. (如果可以写的话)该程序向管道中写一个struct mes结构体信息。

错误选项分析:

选项C
  • 错误原因
    当以 O_WRONLY 模式打开 FIFO 时,若没有进程以读模式(O_RDONLY)打开该 FIFO,open 会阻塞,直到有读端打开或被信号中断。
    • 读端打开规则:O_RDONLY 打开 FIFO 时,若没有写端打开,不会阻塞(立即返回)。
    • 写端打开规则:O_WRONLY 打开 FIFO 时,若没有读端打开,会阻塞(不会立即返回)。
  • 代码验证
    myfifow3.c 中以 O_WRONLY 打开 FIFO,若此时 fiforead.c 未运行(无读端打开),open 会阻塞,直到读端打开。
其他选项正确性
  1. 选项A

    • 正确。通过 access(FIFO_NAME, F_OK) 检查 FIFO 是否存在,不存在时调用 mkfifo 创建。
  2. 选项B

    • 正确。fifocom.h 中定义 FIFO_NAME/tmp/myfifo,管道文件名为该路径。
  3. 选项D

    • 正确。程序通过 write(des, &mymes, sizeof(struct mes)) 向 FIFO 中写入 struct mes 结构体数据。

结论:

错误的陈述是C


下面是整理好格式的代码和分析:

代码整理:

mysh.sh
#!/bin/sh
read str
echo "in shell:$str"
exit 0
mypopen1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main() {FILE *ffile;char buffer[256];int r;ffile = popen("./mysh.sh", "w");  // 注意:原代码中第二个参数有多余空格,应为"w"if (ffile == NULL) {printf("error\n");exit(1);}memset(buffer, '\0', 256);strcpy(buffer, "there was a lake beside the gate of the university...");r = fwrite(buffer, sizeof(char), 255, ffile);pclose(ffile);exit(0);
}A. 该程序中,open的第二参数也可以是“r”,也能编译与运行,其运行结果也将一样。
B. 当正确运行myp1时,mysh,sh中的read实际是从(popen)管道中读取数据信息。
C. 当正确运行myp1时,将输出in shell:there was a lake beside the gate of the university...
D. 该程序实际执行时,mysh.sh应该具备相应权限,如r和x权限,否则会出现权限不容许等出错。

错误选项分析:

选项A
  • 错误原因
    popen 的第二个参数 "w" 表示向子进程的标准输入写入数据(子进程的 read 会从管道读取数据)。
    若改为 "r",则表示从子进程的标准输出读取数据,此时:
    1. 子进程的 read str 会等待真正的标准输入(如键盘输入),而非管道数据(因为管道连接的是子进程的标准输出,而非输入)。
    2. 父进程无法通过 fwrite 向子进程发送数据,导致子进程的 read 阻塞或读取不到数据,最终输出结果完全不同。
  • 结论:选项A错误,模式 "r""w" 功能完全相反,结果不可能一样。
其他选项正确性
  1. 选项B

    • 正确。popen("w") 创建的管道连接子进程的标准输入,mysh.sh 中的 read str 会从该管道读取父进程写入的数据。
  2. 选项C

    • 正确。父进程通过 fwrite 向管道写入字符串,子进程读取后会输出 in shell:there was a lake beside the gate of the university...
  3. 选项D

    • 正确。执行 ./mysh.sh 需要脚本具备 可读(r)和可执行(x)权限,否则 popen 会因权限不足失败。

结论:

错误的陈述是A


下面是整理好格式的代码和分析:

// mypopen2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main(int c, char *a[]) {FILE *ffile;char buffer[256], *p;int r;int i = 1;if (c <= 1) {printf("number of para is less\n");exit(1);}i = 1;p = buffer;memset(buffer, '\0', 256);while (i < c) {strcat(buffer, a[i]);strcat(buffer, " ");i++;}buffer[strlen(buffer) + 1] = '\0';  // 错误:越界写入ffile = popen(buffer, "r");if (ffile == NULL) {printf("popen open file error\n");exit(1);}memset(buffer, '\0', 256);r = fread(buffer, sizeof(char), 255, ffile);while (r > 0) {printf("read %s", buffer);memset(buffer, '\0', 256);r = fread(buffer, sizeof(char), 255, ffile);}pclose(ffile);exit(0);
}A. 该程序运行时,参数个数应该至少需要两个。否则会输出“number of para is less”错误并退出。
B. 程序中将传给该程序的参数数组a[](a[0]元素除外)拼接成一个命令对应的字符串,再将该字符串作为popen函数的第一个参数。
C.(myp所在目录为当前目录)正确运行./myp  find . -name pi.c时,如果当前目录中有pi.c文件(且当前用户有读写权限),将输出read ./pi.c
D. 该程序很好的将参数拼接起来构成一个命令字符串,该字符串长度不受限制。

错误选项分析:

选项D
  • 错误原因
    程序中使用 char buffer[256] 存储拼接后的命令字符串,其长度最大为255字节(需留一个字节给字符串终止符 \0)。若参数拼接后的字符串超过255字节,会导致缓冲区溢出,引发未定义行为。
  • 代码问题
    • buffer[strlen(buffer) + 1] = '\0'; 是错误的,应改为 buffer[strlen(buffer)] = '\0';,但这无法解决长度限制问题。
    • 即使修正赋值位置,buffer 的静态大小仍限制了命令长度。
其他选项正确性
  1. 选项A

    • 正确。程序通过 if (c <= 1) 判断参数个数,不足时输出错误并退出。
  2. 选项B

    • 正确。程序将 a[1]a[c-1] 的参数用空格拼接成完整命令字符串,如 find . -name pi.c
  3. 选项C

    • 正确。若当前目录存在 pi.c,执行 find . -name pi.c 会输出 ./pi.c,父进程通过 fread 读取后打印 read ./pi.c

结论:

错误的陈述是D

http://www.xdnf.cn/news/6296.html

相关文章:

  • 企业网络新选择:软件定义架构下的MPLS
  • 【Docker】Windows10环境下安装DockerDesktop
  • TCP 三次握手建立连接详解
  • C2S-Scale:Cell2Sentence v2
  • 在星河社区学习PARL使用强化学习来训练AI
  • C#高级编程:IO和序列化
  • linux内核主要由哪五个模块构成?
  • ultralytics 中的 RT-DETR 之 模型结构解析
  • 【python机器学习】Day 25 异常处理
  • 吴恩达机器学习笔记:多变量梯度下降
  • Permission Denied Error on Port 6277 When Starting MCP
  • 彻底解决QT5 中文编译不过问题
  • HCIP-Datacom Core Technology V1.0_1认识网络设备
  • 【unity游戏开发——编辑器扩展】EditorWindow自定义unity窗口拓展
  • AI-02a5a6.神经网络-与学习相关的技巧-批量归一化
  • Spring Boot拦截器详解:原理、实现与应用场景
  • centos7忘记root密码后使用单用户模式重置
  • 算法备案如何判断自己的产品是否具备舆论属性
  • LeetCode100.5 盛最多水的容器
  • Linux系统之----基础IO
  • 亚马逊电商广告革命:当AI推荐沦为红海陷阱,中国卖家如何破局?
  • AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年5月14日第77弹
  • 最短路与拓扑(2)
  • vim启动的时候,执行gg
  • 现场维护三重四极杆质谱系统和四极杆清洗方法,确保所有目标化合物的可靠性检测
  • 牛顿均差知识
  • 写作--简单句基础练习
  • AI时代的弯道超车之第九章:AI如何改变传统教育模式
  • C PRIMER PLUS——第10节:结构体、共用(同)体/联合体
  • 字符串检索算法:KMP和Trie树