Linux C语言系统编程作业
跟着别人简单学了下Linux下的系统编程,把做过的几个作业简单记录一下。作业的内容都是针对的具体的知识点,实现难度并不高。
进程间通信
设计一个程序,打开一个匿名管道,然后生成一个子进程,主进程向管道中写入信息“Hello XXX”,子进程从管道中读取数据,将读取到的信息在屏幕上显示。
主要考察fork
、pipe
两个函数的使用,实现代码如下:
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define STRING "Hello wanakiki"
int main(){
int pipefd[2];
pid_t pid;
char buf[BUFSIZ];
if(pipe(pipefd) == -1){
perror("pipe()");
exit(1);
}
pid = fork();
if(pid == -1){
perror("fork()");
exit(1);
}
if(pid == 0){
// 子进程
printf("Child pid is: %d\n", getpid());
if(read(pipefd[0], buf, BUFSIZ) < 0){
perror("write()");
exit(1);
}
printf("%s\n", buf);
}
else{
// 父进程
printf("Parent pid is: %d\n", getpid());
if (write(pipefd[1], STRING, strlen(STRING) + 1) < 0)
{
perror("write()");
exit(1);
}
wait(NULL);
}
exit(0);
}
多进程
设计一个程序,运行后显示“开始计算”后,生成两个子进程。在主进程计算1+3+5+7+…+99的值,并将结果显示出来;在第一个子进程计算2+4+6+…+100的值,并将结果显示出来;第二个子进程中计算1+2+3+4+…+100的值,并将结果显示。
当需要创建的子进程数较少时,可以手动书写代码创建进程,在生成一个进程之后的父进程代码中再次调用fork
生成进程。但是当需要创建的子进程较多时此种方法实现起来较为麻烦,可以用循环方式生成。本实验我采用了手动创建的方案,循环创建子进程的代码可以在最后综合实验中找到。
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(){
int p1, p2;
int res; // 保存计算结果
int num = 0;
while((p1 = fork()) == -1); // 创建子进程
if(p1 == 0){
res = 0;
for(int i = 2; i < 101; i++){
res += i;
}
printf("第一个子进程计算结果 %d\n", res);
printf("第一个子进程%d", ++num);
}
else{
while((p2 = fork()) == -1); // 创建子进程
if(p2 == 0){
res = 0;
for(int i = 1; i < 101; i++){
res += i;
}
printf("第二个子进程计算结果 %d\n", res);
printf("第二个子进程%d", ++num);
}
else{
res = 0;
for(int i = 0; i < 100; i++){
res += i;
}
printf("主进程计算结果 %d\n", res);
printf("第三个子进程%d", ++num);
}
}
printf("end");
}
信号
编写一个程序,接受发送给自己的信号(30和31),收到信号后显示信号的内容。没收到信号时循环显示提示信息。
程序给自己发送信号可以通过调用raise
函数实现,在代码当中为了方便观察到发送信号的情况,对题目要求的两个信号的处理方法进行了设置。
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void show_sig(int sig){
printf("Get a signal: %d\n", sig);
}
int main(){
signal(SIGPWR, show_sig); // 设置信号处理方法
signal(SIGSYS, show_sig);
for(int i = 0; i < 8; i++){
printf("系统将会在3秒和5秒发送30 31信号,当前时间为: %d\n", i);
if(i == 3){
raise(30);
}
if(i == 5){
raise(31);
}
sleep(1);
}
return 0;
}
// https://blog.csdn.net/thanksgining/article/details/41824475
综合作业
有一个文件夹,下面有许多个C语言源程序。要求编写一个程序,统计所有C语言源程序中,下列系统函数的被调用次数。
printf open close read write fork signal
统计结果输出到myresult.txt文件按中,格式如下:
printf 49
open 13
close 13
read 24
write 16
fork 8
signal 0
建议(非强制,遵守下列建议会得到一个较好分数):
- 对每个.c文件,生成一个子进程(或者启动一个线程)统计,统计结果存入合适的文本文件中,最后再汇总结果
- 对每个函数,生成一个子进程(或者启动一个线程)统计,统计结果存入合适的文本文件中,最后再汇总结果。
- 访问文件使用Linux系统IO,如open, close, read, write。
- 编写出合适的makefile
- 可以生成多个可执行文件,通过一个主文件启动工作。
- 可以带Shell脚本
思路
程序的整体逻辑还是比较清楚的,读取文件夹内所有c文件,然后统计函数的调用次数,最后汇总到一个文件当中。如果不考虑多进程的话,实现起来并不复杂,但是题目要求使用多进程的方案,所以统计方式需要一定的变化。
我最开始的实现思路是对于每个C文件创建一个子进程来统计各个函数的使用次数,但实现的时候发现这样统计数据的保存不是很方便。如果每个文件都生成一个结果文件,当源文件数目较多时,生成的结果文件也会变得很多,汇总起来比较麻烦。如果是把每个文件的统计结果保存到一个文件当中,由于各个进程的执行时间不同,最后保存时还要进行额外的处理。
考虑到这些,我选择了一个比较简单的方案,对每个函数创建一个子进程,统计当前目录下所有源文件中该函数的调用次数,最后保存到结果文档当中,在程序结束之后用shell脚本对统计结果进行合并。
C程序:
#include <stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h> // 文件目录
#include <string.h>
/*struct dirent
{
long d_ino; // inode number 索引节点号
off_t d_off; // offset to this dirent 在目录文件中的偏移
unsigned short d_reclen; // length of this d_name 文件名长
unsigned char d_type; // the type of d_name 文件类型
char d_name [NAME_MAX+1]; // file name (null-terminated) 文件名,最长255字符
}
其中d_type表明该文件的类型:文件(8)、目录(4)、链接文件(10)等
*/
// 文件操作相关 https://blog.csdn.net/u014650722/article/details/51563679
char *aims[] = {"printf", "open", "close", "read", "write", "fork", "signal"};
int helper(char *filename, int id)
{
int fd = open(filename, O_RDONLY); // 只读模式打开文件
char buf[1024];
int count = 0;
while (read(fd, buf, 1024))
{
// strstr函数判断是否为子串
char * tmp = strstr(buf, aims[id]);
while(tmp != NULL)
{
count++;
tmp += strlen(aims[id]); //找到后必须移动,不然死循环
tmp = strstr(tmp, aims[id]);
}
}
close(fd);
return count;
}
void savefile(int id, int count){
// 保存结果
char buf[1024];
char *savename = (char *)malloc(strlen(aims[id]) + 4);
sprintf(savename, "%s.txt", aims[id]);
int fd = open(savename, O_CREAT | O_RDWR, S_IWUSR);
int len = sprintf(buf, "%s %d\n", aims[id], count);
write(fd, buf, len);
close(fd);
}
int main()
{
int i = 0;
int p = 0;
for(i = 0; i < 7; i++){
p = fork();
if(p == 0){
break;
}
}
if (p == 0)
{
// 获取目录下所有.c文件
DIR *directory_pointer;
struct dirent *entry;
if ((directory_pointer = opendir(".")) == NULL)
{
printf("Error open\n");
return 0;
}
else
{
int count = 0;
while ((entry = readdir(directory_pointer)) != NULL)
{
int len = strlen(entry->d_name);
if (entry->d_name[len - 1] == 'c' && entry->d_name[len - 2] == '.')
{
count += helper(entry->d_name, i);
}
}
savefile(i, count);
}
}
else
{
return 0; //主进程不做处理
}
}
shell脚本:
#!/bin/bash
funname=("printf" "open" "close" "read" "write" "fork" "signal")
gcc final.c -o final # c文件名为final.c
./final
rm final # 运行结束后删除
rm res.txt # 删除原有结果文本
for fun in ${funname[@]}
do
cat $fun.txt >> res.txt
rm $fun.txt
done