前言

继续Linux的学习,学到了Linux系统下的进程与线程的概念,布置了有关其的实验题,用C语言编程启动进程线程,习惯了Java多线程编程,这次在Linux下玩一玩C语言进程线程编程。
本文原创,创作不易,转载请注明!!!
本文链接
个人博客:https://ronglin.fun/?p=155
PDF链接:见博客网站
CSDN: https://blog.csdn.net/RongLin02/article/details/115419994

准备工作

环境与配置

主机:联想Y7000P;64位windows10;CPU:i7-9750H;显卡:GTX 1660 Ti;内存:16G
虚拟机:Ubuntu 18.04 LTS;硬盘100G;内存4G;64位;4核心
Linux内核:5.11.8

安装IDE

工欲善其事,必先利其器,首先安装一个好用的C语言IDE,C语言在Linux下的IDE有很多,比如geany,codeBlocks等等,当然也有大佬用文本编辑器vim nano gedit等直接写,然后用gcc编译也是可以的。我这里用Windows下我比较喜欢的一个编译器codeBlocks。

方法一

2022.2.28可用
输入以下代码

sudo add-apt-repository universe
sudo apt update
sudo apt install codeblocks

方法二

为了更方遍的调试,先安装一下GCC。输入:

sudo apt install gcc

然后安装codeBlocks和其所依赖的库,输入:

sudo add-apt-repository ppa:damien-moore/codeblocks-stable
sudo apt-get update
sudo apt-get install codeblocks
sudo apt-get install codeblocks-contrib

等待其自己安装完就行了。
如果上面那个ppa库没法用还可以用下边这个

sudo add-apt-repository ppa:pasgui/ppa
sudo apt-get update
sudo apt-get install codeblocks
sudo apt-get install codeblocks-contrib

补充资料:
PPA:PPA全称为 Personal Package Archives(个人软件包档案),是 Ubuntu Launchpad 网站提供的一项服务,当然不仅限于 Launchpad 。它允许个人用户上传软件源代码,通过 Launchpad 进行编译并发布为二进制软件包,作为 apt/新立得源供其他用户下载和更新。
sudo add-apt-repository ppa:pasgui/ppa这句代码,会在/etc/apt/sources.list.d目录下添加PPA源,供我们使用,所以如若想删除添加的PPA源,到/etc/apt/sources.list.d目录下,删除对应的文件即可。

安装完成后,直接在终端中输入codeblocks,弹出来界面,点击ok即可。

更多关于Ubuntu 18.04安装codeBlocks的方法,可以参考:https://blog.csdn.net/AAMahone/article/details/86531631

题外话

界面太小

由于是虚拟机,可能界面比例很小,导致 代码编辑区很小,看起来很不舒服,大概我知道的有两种解决:
方法一:把虚拟机界面最大化
我的虚拟机是VirualBox,要想调整虚拟机界面大小,要先安装 增强模式,如果之前安装过了,就可以直接调整界面大小了。安装增强模式也很简单,在虚拟机菜单栏设备安装增强功能,安装完成后,返回桌面,会发现多了一个光盘,点击光盘,在标题栏的下方右侧,也就是右上角,有一个 运行程序,点击,然后重启Ubuntu即可。一定要点击运行程序,要不然增强功能不生效!
方法二:调整codeblocks各个栏目的大小
codeblocks的GUI设计很人性化,每一个栏都可以调整大小,同时也可以在菜单栏view中关闭不需要显示的部分,例如我个人的喜欢布局如下:

中文下方红色波浪线

这个功能其实是拼写纠错功能,可能是在Ubuntu下对中文还没完全适配好,导致输入中文就出现红色波浪线,尤其是在printf中和注释里,让人眼花
菜单栏PluginsManage plugins 找到 SpellChecker disable它,然后重启codeblocks就行了。

创建进程

实验简述

实验说明:
学会通过基本的Linux进程控制函数,由父进程创建子进程,并实现协同工作。创建两个进程,让子进程读取一个文件,父进程等待子进程读完文件后继续执行。
解决方案:
进程协同工作就是要协调好两个或两个以上的进程,使之安排好先后次序并依次执行,可以用wait()或者waitpid()函数来实现这一点。当只需要等待任一子进程运行结束时,可在父进程中调用wait()函数。若需要等待某一特定子进程的运行结果时,需调用waitpid()函数,它是非阻塞型函数。
程序框架

当fork( )调用成功后,父子进程完成各自的任务,但当父进程的工作告一段落,需要用到子进程的结果时,它就停下来调用wait( ),一直等到子进程运行结束,然后利用子进程的结果继续执行,这样就圆满地解决了父子进程间的协同工作问题。

源代码

指导书都写得这么详细了,那么直接开整吧
有关创建project的操作和Windows下的操作一样,菜单栏FilenewProjectConsole application
我在/home/ronglin/code/gnu/新建一个project文件夹,用来存放C项目,要选择c代码,本次实验项目名称为create_process

然后一路next就行了。当然因为本次实验实际上只需要一个.c文件就行了。也可以只是新建一个.c文件,菜单栏FilenewFile..C/C++ source然后也是填写信息,一路next就行了,但是缺点就是单独一个.c/cpp文件是没办法用codeblocks的调试功能的。
在Sources的main.c中写代码,如果是main.cpp说明新建的是一个c++程序,虽然大概差不多,但是还是推荐c和c++代码分离。
因为子进程还要打开一个文件,我在工程目录下新建一个text.txt文件用于测试。

然后就是头秃的过程了。

fork()创建

#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>#define COLMAX 1024 //每一个字符串的最大长度(列)
#define ROWMAX 64   //字符串最大个数(行)/*
本代码实现用子进程打开同目录下的text.txt文件
并且父进程输出内容
*/int main()
{int p_id = -1;if( (p_id=fork()) == -1 ) //子进程创建失败{printf("Process_1 Create Error\n");}else if(p_id == 0)   //子进程部分{printf("%d Process Start Work\n",getpid());char text[ROWMAX][COLMAX]={0};FILE *fp = fopen("./text.txt","r+");//打开文件if( fp == NULL )//打开文件失败{printf("Fail to open file!\n");}else{int i=0;while( (fscanf(fp,"%s",text[i])) != EOF){printf("%s\n",text[i]);i++;sleep(1);   //等待1s方便查看输出}}fclose(fp);exit(0);}//父进程部分waitpid(p_id,NULL,0);//阻塞等待printf("%d proccess is end\n",p_id);return 0;
}


由于是用的scanf,遇到空格就读入完毕,所以结果呈现如上图。

vfork()创建

因为fork()创建的子进程其实是父进程数据的copy,也就是说父子进程的数据并不是共享的,子进程只是父进程的翻版,类似于c语言函数中的传递形式参数,要想数据共享,其实也有很多方法,资料书上提到很多CLONE_开头的标志位就是控制哪些东西是父子进程共享的。不过有更简单的vfork()函数,其实本质上是一样的,都是调用内核函数do_fork()。接下来就实现,子进程读取文件,然后父进程输出,需要注意的是,不论多进程还是多线程,数据一旦共享,会涉及到安全问题,不过vfork()创建子进程之后,父进程就会阻塞,所以就没安全问题了。
直接看源码吧

#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>#define COLMAX 1024 //每一个字符串的最大长度(列)
#define ROWMAX 64   //字符串最大个数(行)/*
本代码实现用子进程打开同目录下的text.txt文件
并且父进程输出内容
*/int main()
{int count=0;int p_id = -1;char text[ROWMAX][COLMAX]={0};if( (p_id=vfork()) == -1 ) //子进程创建失败{printf("Process_1 Create Error\n");}else if(p_id == 0)   //子进程部分{printf("%d Process Start Work\n",getpid());FILE *fp = fopen("./text.txt","r+");//打开文件if( fp == NULL )//打开文件失败{printf("Fail to open file!\n");}else{while( (fscanf(fp,"%s",text[count++])) != EOF);}fclose(fp);exit(0);}//父进程部分for(int i=0;i<count;i++){printf("%s\n",text[i]);sleep(1);//暂停1s方便调试}printf("%d proccess is end\n",p_id);return 0;
}

输出结果同上。

线程共享进程中的数据

实验简述

实验说明:
了解线程与进程之间的数据共享关系。创建一个线程,在线程中更改进程中的数。
解决方案:
在进程中定义共享数据,在线程中直接引用并输出该数据。
程序框架:

源代码

创建第二个project–share_thread,因为有一部分Java多线程开发的基础,所以直接开整。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>static int shdata=4;void *create(void *arg)
{printf("new pthread...\n");printf("shdata data = %d \n",shdata);return (void *)(0);
}int main()
{pthread_t mythread ;int error = 0;error = pthread_create(&mythread,NULL,create,NULL);if(error){printf("pthread_create is not created...\n");return -1;}sleep(1);printf("pthread_create is ok...\n");return 0;
}

写完代码发现,编译一直报错,查阅资料才知道,pthread库不是Linux系统默认的库,需要使用库libpthread.a
如果是用gcc指令编译则应该输入这个
gcc test.c -lpthread -o pthread

如果是codeblocks就添加库就行了,首先需要在终端中输入:

locate libpthread.a

先找一下 文件在哪

有两个,一般用/usr目录下的,将/usr/lib/x86_64-linux-gnu/libpthread.a复制下来。
codeblocks,菜单栏SettingsCompiler,左侧Global compiler settings,中间Linker settings
在左侧Link libraries中 add 刚刚复制好的路径,右侧Other linker options,输入-lpthread
然后ok就行了.


运行成功.

多线程实现单词统计工具

实验简述

实验说明
多线程实现单词统计工具。
解决方案
区分单词原则:凡是一个非字母或数字的字符跟在字母或数字的后面,那么这个字母或数字就是单词的结尾。
允许线程使用互斥锁来修改临界资源,确保线程间的同步与协作。如果两个线程需要安全地共享一个公共计数器,需要把公共计数器加锁。线程需要访问称为互斥锁的变量,它可以使线程间很好地合作,避免对于资源的访问冲突。
程序框架

源代码

创建第三个project–words_counter,先在根目录下新建两个txt文件,一共3+6=9个单词

#include <stdio.h>
#include<pthread.h>
#include<ctype.h>pthread_mutex_t counter_clock = PTHREAD_MUTEX_INITIALIZER;
static int total_words = 0;void *count_words(void*);int main(int ac,char *av[ ])
{if(ac !=3){printf("Usage:%s file1 filc2\n",av[0]);exit(1);}int error_1=0,error_2=0;pthread_t conuter_1,conuter_2;/*分别以av[1]和av[2]作为参数,创建两个线程t1和t2*/error_1 = pthread_create(&conuter_1,NULL,count_words,av[1]);error_2 = pthread_create(&conuter_2,NULL,count_words,av[2]);if(error_1 || error_2){printf("pthread_create is not created...\n");return -1;}/*让线程t1和t2进入等待态*/error_1 = pthread_join(conuter_1,NULL);error_2 = pthread_join(conuter_2,NULL);if(error_1 || error_2){printf("pthread_join is failed...\n");return -1;}/*输出统计出来的单词总数*/printf("Number of words is %d\n",total_words);return 0;
}void *count_words(void *f)
{char *filename = (char *)f;FILE *fp;int c,prevc='\0';if( (fp=fopen(filename,"r")) != NULL) //文件不为空{while((c=getc(fp)) != EOF)  //输入{if(!isalnum(c) && isalnum(prevc))   //所传的字符一是字母和数字 && 一个是其他字符{pthread_mutex_lock(&counter_clock); //为了线程安全上锁total_words++;pthread_mutex_unlock(&counter_clock);   //解锁}prevc = c;}fclose(fp);}else{perror(filename);   //输出错误信息}return NULL;
}

代码见上,然后就是给main函数传参数,菜单栏ProjectSet programs’ arguments…,左侧Global compiler settings,选中Debug,在下边的Program arguments中添加要参入的参数./text1.txt ./text2.txt.然后ok.

然后运行查看结果:

成功了,=w=

Linux学习之C语言的进程与线程编程相关推荐

  1. linux 线程id 进程id,在Linux上显示正在运行的进程的线程ID

    在Linux上显示正在运行的进程的线程ID 在上Linux," ps -T"可以显示正在运行的进程的线程信息: # ps -T 2739 PID SPID TTY STAT TIM ...

  2. Linux中的各种栈:进程栈 线程栈 内核栈 中断栈

    Linux中的各种栈:进程栈 线程栈 内核栈 中断栈 栈的作用 1. 函数调用 2. 多任务支持 Linux 中有几种栈?各种栈的内存位置? 1. 进程栈 2. 线程栈 3. 进程内核栈 4. 中断栈 ...

  3. Python学习笔记__10.4章 进程VS线程

    # 这是学习廖雪峰老师python教程的学习笔记 1.概览 我们介绍了多进程和多线程,这是实现多任务最常用的两种方式.现在,我们来讨论一下这两种方式的优缺点 要实现多任务,通常我们会设计Master- ...

  4. Linux 中的各种栈:进程栈 线程栈 内核栈 中断栈

    栈是什么?栈有什么作用? 首先,栈 (stack) 是一种串列形式的 数据结构.这种数据结构的特点是 后入先出 (LIFO, Last In First Out),数据只能在串列的一端 (称为:栈顶 ...

  5. 一文读懂 | Linux 中的各种栈:进程栈 线程栈 内核栈 中断栈

    点击蓝字 关注我们 因公众号更改推送规则,请点"在看"并加"星标"第一时间获取精彩技术分享 来源于网络,侵删 栈是什么?栈有什么作用? 首先,栈 (stack) ...

  6. 计算机操作系统学习笔记 第二章、进程与线程

    文章目录 1 进程和线程 1.1 进程的概念和特征 1.1.1 进程的概念 1.1.2 进程的特征 1.2 进程的状态与转换 1.3 进程的组织 1.4 进程控制 1.5 进程通信 1.5.1 共享存 ...

  7. linux命令(一)查看进程的线程数top,ps

    1.ps -T -p <pid> ps -T -p 116115|wc -l 2. top -H -p 116115 查看进程的线程使用率 3.pstree -p 116115|wc -l ...

  8. Linux学习笔记 --网络配置及进程管理

    目录 网络配置 ☆☆☆☆指定服务器 IP 设置主机名和 hosts 映射 主机名解析过程分析(Hosts.DNS) ☆☆☆进程管理 ps 父子进程 终止进程 kill.killall ☆☆☆服务管理 ...

  9. linux 命名管道 c语言 不同进程,进程间通信 - 命名管道实现

    命名管道概述 命名管道是通过网络来完成进程之间的通信的,命名管道依赖于底层网络接口, 其中包括有 DNS 服务,TCP/IP 协议等等机制,但是其屏蔽了底层的网络协议细节, 对于匿名管道而言,其只能实 ...

最新文章

  1. win10自带Ubuntu
  2. php中的冒号应用,php中双冒号的应用
  3. 糖药病数据集分类_使用optuna和mlflow进行心脏病分类器调整
  4. excel 公式 平移 引用单元格_不学会这3个Excel隔列求和函数公式,你只能一个个单元格去相加...
  5. 40-400-044-运维-优化-MySQL order by 优化
  6. OLED12864(SSD1306)驱动代码
  7. 韦根协议——STM32收发
  8. app小程序手机端Python爬虫实战10xpath定位方式
  9. resultFul请求案例
  10. Android安卓拖拉机版Docker
  11. 锐龙r5 5600h核显什么水平 r5 5600h属于什么级别
  12. 华为内部存储转sd卡_怎样把华为手机内存的东西传、转到SD卡中呢
  13. pytorch MSELoss参数详解
  14. 51nod3241 小明和他的同学们
  15. DEDEcms终极SEO优化教程
  16. UWB:室内定位首选
  17. 中国招聘网站之“忧”
  18. 『工程项目实践』银行卡识别(CTPN+CRNN)
  19. java蜘蛛纸牌教学视频_南通java学习班费用
  20. 敏捷转型行动笔记:价值流分析

热门文章

  1. UI组件-UITabbarController
  2. 解决加载静态文件无法被浏览器缓存问题
  3. jqGrid 中的editrules来自定义colModel验证规则
  4. 最大计算机病毒诈骗怎么发生,又是怎么被制止的
  5. windows下搭建Apache+Mysql+PHP开发环境
  6. PowerMock介绍
  7. JAVA socket编程实例
  8. 制造行业质量追溯难,看看这些一流制造企业,都怎么做质量管理?
  9. “不翻身,就要翻船”!帆软独家:制造业数字化转型解决方案
  10. feiq肆无忌惮欺压同行