简明linux系统编程--互斥锁--TCP--UDP初识

news2024/9/21 9:20:20

目录

1.互斥锁

2.信号

2.1介绍

2.2信号的内核机制

3.linux网络编程概述

3.1一览七层协议

3.2一览数据传输过程

 3.3四层网络模型

3.4服务端和客户端的数据交互

4.TCP服务端编程

5.TCP客户端编程

6.UDP服务端编程

7.UDP客户端编程


1.互斥锁

互斥锁也是和信号量一样,用于进行任务之间的通信

我们的这个互斥锁,分为上锁和解锁,我们的某一个进程占用这个资源的时候,就会把这个共享区域上锁,表示这个空间资源已经被使用,其他的想要使用这个资源的进程就会被挂起,直到我们的这个正在使用资源的进程使用完毕,其他的被挂起的进程才可以使用这个资源,这个资源就会被从原来的上锁状态到现在的解锁状态,被其他的进程使用;

实际上这个互斥锁主要是是被线程使用,互斥锁不可能会同时被两个线程拥有,我们把这个称之为排他性;

已经处于加锁的状态的互斥锁,被其他的线程访问,这个线程就会被加锁,只有这个互斥锁解锁的时候,其他的线程才可以使用;

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>

//两个线程函数的声明
void *thread1_function(void *arg);
void *thread2_function(void *arg);

pthread_mutex_t mutex;//创建互斥锁


//主函数进行互斥锁的创建和初始化,以及线程的创建
int main(void)
{
	pthread_t pthread1, pthread2;
	int ret;

	//初始化互斥锁
	//第二个就是默认的配置选项
	pthread_mutex_init(&mutex, NULL);

	ret = pthread_create(&pthread1, NULL, thread1_function, NULL);
	if(ret != 0)
	{
		perror("pthread_create");
		exit(1);
	}

	ret = pthread_create(&pthread2, NULL, thread2_function, NULL);
	if(ret != 0)
	{
		perror("pthread_create");
		exit(1);
	}

	//线程的连接,第一个参数就是连接线程的线程号
	pthread_join(pthread1, NULL);
	pthread_join(pthread2, NULL);
	printf("The thread is over, process is over too.\n");

	return 0;
}

//线程1的操作:上锁
void *thread1_function(void *arg)
{
	int i;

	while(1)
	{
		//这个函数就是对于这个进行初始化的互斥锁进行上锁
		pthread_mutex_lock(&mutex);
		for(i = 0; i < 10; i++)
		{
			printf("Hello world\n");
			sleep(1);
		}
		//循环休眠之后进行解锁的操作
		pthread_mutex_unlock(&mutex);
		sleep(1);
	}
	return NULL;
}


//线程2的操作:先是阻塞挂起,然后加锁(线程1已经占用完成)
void *thread2_function(void *arg)
{
	int i;
	//确保线程1先运行,开始的时候让这个线程1先运行
	sleep(1);

	while(1)
	{
		//尝试加锁,但是会处于阻塞状态
		pthread_mutex_lock(&mutex);
		for(i = 0; i < 10; i++)
		{
			//这个就是线程1使用完之后,才会打印这个good morning
			printf("Good moring\n");
			sleep(1);
		}
		pthread_mutex_unlock(&mutex);
		sleep(1);
	}
	return NULL;
}

2.信号

2.1介绍

数据传输方式:管道,共享内存,消息队列;

信号主要用途:传递控制命令,主要是进程和应用程序之间,或者是应用程序之间;

2.2信号的内核机制

其实这个信号我们经常使用,只不过这次是说出了他的真正的名字,我们之前使用的这个ctrl+c就是退出这个程序,ctrl+z就是强制退出,这些都是我们的信号;

我们的这个信号有的是我们自己可以控制的,有的就是属于内核层面上面的,是固定的,即使我们进行自定义,他也不会按照我们的定义去修改;

下面的这个就是我们传递命令行参数,通过这个kill函数控制第二个程序里面运行的线程,我们的这个kill函数里面的传递的这个内容就是相当于信号,控制这个function里面的线程的执行;

int main(int argc, char *argv[])
{
	kill(atoi(argv[1]), SIGQUIT);

	return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

void function(int signo);

int main(void)
{
	int i = 0;

	printf("pid = %d\n", getpid());

	signal(SIGINT, function);
	signal(SIGKILL, function);

	while(1)
	{
		printf("Count to %d\n", ++i);
		sleep(1);
	}

	return 0;
}

void function(int signo)
{
	//把ctrl+c的这个处理机制重写了
	if(signo == SIGINT)
	{
		printf("You have just triggered a ctrl+c operation.\n");
		exit(1);
	}

	else if(signo == SIGQUIT)
		printf("Trig a SIGQUIT signal.\n");
}

有些时候,我们传递这个信号的时候,这个控制程序并不会按照我们的这个设计输出打印结果,例如这个,我们虽然指定了这个信号的打印结果,但是如果这个信号是内核层面的,我们就无法进行修改,这个时候打印的时候就不会按照我们的设计进行打印,而是按照系统的内核设计;

3.linux网络编程概述

3.1一览七层协议

3.2一览数据传输过程

发送就是封装的过程,接收数据就是解封的过程,类似于我们的这个快递的传输过程,发送这个快递的时候需要不断的进行打包,当我们取到这个快递的时候就需要不断地进行包装的拆解;

这个里面的主机A就相当于这个快递发送端,这个主机B就相当于这个我们对于这个数据进行拆解的过程;

 3.3四层网络模型

下面的这个四层模型就是上面的这个模型简化之后得到的:

TCP协议面向连接,建立连接之后,这个内容才会被发送,稳定传输数据;

UDP不是面向连接的,不稳定,不可靠,可能发送端和接收端没有建立连接,这个数据就会被发送出去了;

TCP因为确认连接的过程繁琐,传输的效率很低,UDP的传输效率相对较高,我们需要根据我们的具体需求进行选择;

当我们发送的数据比较大的时候,就是使用的UDP协议,例如看视频,顶多出现卡顿现象;

如果我们需要确保接收端收到数据,我们需要使用更加安全的TCP;

3.4服务端和客户端的数据交互

4.TCP服务端编程

#include <stdio.h>

//下面的两个头文件是使用socket函数需要包含的
#include <sys/types.h>
#include <sys/socket.h>

//htons函数需要包含下面的这个头文件
#include <arpa/inet.h>

#include <netinet/in.h>
#include <unistd.h>

#define PORT_ID	8800
#define SIZE	100

int main(void)
{
	int sockfd, client_sockfd;
	//这个结构体的
	struct sockaddr_in my_addr, client_addr;
	int addr_len;
	char welcome[SIZE] = "Welcome to connect to the sever!";

	//1.socket()创建用于通信的节点,返回节点的描述符-----------------------------------------------------
	//第一个参数配置通信域
	//AF_INET表示我们使用的是IPV4标准
	//SOCK_STREAM表示的就是TCP协议的套接字
	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	//2.bind()---------------------------------------------------------------------------------------
	my_addr.sin_family = AF_INET;

	//设置端口号,应用层上面的,这个时候,
    //因为计算机和网络的大小端可能不统一,
	//我们使用这个htons函数把主机和网络的字节序进行转换统一(全部转换为大端模式)
	my_addr.sin_port = htons(PORT_ID);

	//INADDR_ANY参数表示的是监视计算机上面的所有网卡的数据
	my_addr.sin_addr.s_addr = INADDR_ANY;

	//把一个名字绑定到我们的套接字上面
	bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));

	//3.listen():监听套接字的连接---------------------------------------------------------------------
	//sockfd表示需要监听的套接字描述符

	listen(sockfd, 10);

	addr_len = sizeof(struct sockaddr);

	while(1)
	{
		printf("Server is waiting for client to connect:\n");
		//4.accept():等待客户端的请求----------------------------------------------------------------------------------
		//第二个参数:访问过来的客户端的地址
        //这个就是输出型参数(运行完之后客户端的地址就被放到这个位置,不需要我们手动填充)
		//accept返回值就是新的套接字描述符,新的客户端描述符,客户端有请求,就会返回新的套接字描述符
		client_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &addr_len);
		//inet_ntoa函数作用就是把我们的internet主机地址从二进制转换为点分的10进制,例如这个192.168.等等之类的
		printf("Client IP address = %s\n", inet_ntoa(client_addr.sin_addr));


		//5.send()服务器发送消息到客户端--------------------------------------------------------------------------------
		//第二个参数就是发送的内容char welcome[SIZE] = "Welcome to connect to the sever!";
		send(client_sockfd, welcome, SIZE, 0);
		printf("Disconnect the client request.\n");


		//6.close()---关闭客户端的连接---------------------------------------------------------------------------------
		close(client_sockfd);
	}

	close(sockfd);

	return 0;
}

5.TCP客户端编程

#define PORT_ID	8800
#define SIZE	100

//./client IP  : ./client 192.168.0.10
int main(int argc, char *argv[])
{
	int sockfd;
	//这个结构体里面的是服务器端的这个地址
	struct sockaddr_in server_addr;
	char buf[SIZE];


	//当我们输入的参数后面没有这个IP地址的时候,我们需要给出这个提示信息,提示用户给出来IP地址
	//agrc表示的就是参数的个数
	if(argc < 2)
	{
		printf("Usage: ./client [server IP address]\n");
		exit(1);
	}

	//1.socket()
	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	//2.connect()
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(PORT_ID);
	//argv[1]这个表示的就是命令行参数里面和IP地址相关的这个参数
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));


	//recv函数表示服务器端对于客户端发来的数据的接受,buf这个表示接受的信息内容放到buf里面去
	recv(sockfd, buf, SIZE, 0);
	printf("Client receive from server: %s\n", buf);

	close(sockfd);

	return 0;
}

6.UDP服务端编程

和上面的这个TCP想比较,这个UDP就简单了很多:

UDP没有accept函数获得客户端的信息,我们无法知道谁发送给我们的信息;

我们想要获得这个谁发送的,我们使用这个recvfrom知道谁发送到我们客户端的;

#define PORT_ID	8800
#define SIZE	100

int main(void)
{
	int sockfd;
	struct sockaddr_in my_addr, client_addr;
	int addr_len;
	char buf[SIZE];

	//1.socket()
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);

	//2.bind()
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(PORT_ID);
	my_addr.sin_addr.s_addr = INADDR_ANY;
	bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));

	addr_len = sizeof(struct sockaddr);

	while(1)
	{
		printf("Server is waiting for client to connect:\n");
		//4.recvfrom()函数可以让我们知道这个接收到的数据的来源
		recvfrom(sockfd, buf, SIZE, 0, (struct sockaddr *)&client_addr, &addr_len);
		printf("Server receive from client: %s\n", buf);
	}

	close(sockfd);

	return 0;
}

7.UDP客户端编程

#define PORT_ID	8800
#define SIZE	100

//./client IP  : ./client 192.168.0.10
int main(int argc, char *argv[])
{
	int sockfd;
	struct sockaddr_in server_addr;
	char buf[SIZE];
	int i;

	if(argc < 2)
	{
		printf("Usage: ./client [server IP address]\n");
		exit(1);
	}

	//1.socket()
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);

	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(PORT_ID);
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);

	for(i = 0; i < 10; i++)
	{
		sprintf(buf, "%d\n", i);
		sendto(sockfd, buf, SIZE, 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
		printf("Client sends to server %s: %s\n", argv[1], buf);
		sleep(1);
	}

	close(sockfd);

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2151935.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我与Linux的爱恋:命令行参数|环境变量

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;Linux的学习 文章目录 一.命令行参数二.环境变量1.环境变量的基本概念2.查看环境变量的方法3.环境变量相关命令 一.命令行参数 【示例1】main函数也是函数&#xff0c;main函数可以带参…

Paragon NTFS for Mac和Tuxera NTFS for Mac,那么两种工具有什么区别呢?

我们在使用Mac系统读取U盘的过程中往往会遇到一个问题&#xff0c;那就是U盘插进电脑无法显示&#xff0c;或者只能读取不能编辑。出现这种情况的原因就一般是格式错误。 很多小伙伴在解决这种问题的时候会选择使用U盘读写工具&#xff0c;那么哪一种读写工具比较好呢&#xf…

Windows下利用MSYS2和VS的nmake编译nginx源码

目录 一、使用说明 二、安装软件 2.1 下载依赖库 2.3 下载并安装 StrawberryPerl 2.4 下载并安装 MSYS 2 2.5 nginx源代码下载 三、编译配置 3.1 设置NGX_MSVC_VER 3.2 配置 Makefile 3.3 编译代码 3.4 整理Nginx发布环境 四、错误处理 一、使用说明 本文章主要记…

Hash入门

unordered_set void test_unordered_set() {unordered_set<int> us;us.insert(4);us.insert(2);us.insert(1);us.insert(5);us.insert(6);us.insert(2);us.insert(2);//去重unordered_set<int>::iterator it us.begin();while (it ! us.end()){cout << *it…

​OpenAI最强模型o1系列:开启人工智能推理新时代

前不久OpenAI发布全新模型——o1模型&#xff0c;也就是业界说的“草莓模型”&#xff0c;包含三款型号&#xff1a;OpenAI o1、OpenAI o1-preview和OpenAI o1-mini。 其中&#xff0c;OpenAI o1-mini和 o1-preview已经对用户开放使用&#xff1a; OpenAI o1&#xff1a;高级推…

mysql笔记—sql性能分析

1.查看数据库各个语句的执行频次 show global/session status like ‘com__’ 2.慢查询 默认没有开启&#xff0c;需要手动开启&#xff08;在/etc/my.cnf中开启&#xff09; 开启后在localhost-slow.log中可以查询到慢查询的语句的相关信息&#xff1a; 3.explain 用法&…

<<编码>> 第 16 章 存储器组织(1)--比特锁存器 示例电路

1 比特锁存器 info::操作说明 鼠标单击逻辑输入切换 0|1 状态 就是前面的电平触发的 D 型锁存器. 写入(Write) 就是时钟信号 primary::在线交互操作链接 https://cc.xiaogd.net/?startCircuitLinkhttps://book.xiaogd.net/code-hlchs-examples/assets/circuit/code-hlchs-ch16…

2025年最新大数据毕业设计选题-Hadoop综合项目

选题思路 回忆学过的知识(Python、Java、Hadoop、Hive、Sqoop、Spark、算法等等。。。) 结合学过的知识确定大的方向 a. 确定技术方向&#xff0c;比如基于Hadoop、基于Hive、基于Spark 等等。。。 b. 确定业务方向&#xff0c;比如民宿分析、电商行为分析、天气分析等等。。。…

OpenCV特征检测(6)对初步检测到的角点位置进行亚像素级别的精炼函数cornerSubPix()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 细化角点的位置。 该函数迭代以找到角点或径向鞍点的亚像素级准确位置&#xff0c;如 93中所述&#xff0c;并如下图所示。 亚像素级准确的角点…

TryHackMe 第2天 | Pre Security (上)

该学习路径讲解了网络安全入门的必备技术知识&#xff0c;比如计算机网络、网络协议、Linux命令、Windows设置等内容。本篇博客将记录第一项&#xff1a;计算机网络。 Network Fundamentals What is networking? 网络就是相互连接的事物&#xff0c;我们的人际关系也可以抽…

Liveweb视频汇聚平台支持GB28181转RTMP、HLS、RTSP、FLV格式播放方案

GB28181协议凭借其在安防流媒体行业独有的大统一地位&#xff0c;目前已经在各种安防项目上使用。雪亮工程、幼儿园监控、智慧工地、物流监控等等项目上目前都需要接入安防摄像头或平台进行直播、回放。而GB28181协议作为国家推荐标准&#xff0c;目前基本所有厂家的安防摄像头…

[Unity Demo]从零开始制作空洞骑士Hollow Knight第六集:制作小骑士完整的跳跃落地行为

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、制作一个完整的小骑士跳跃落地行为 1.制作动画以及UNITY编辑器编辑2.使用代码实现完整的跳跃落地行为控制3.更多要考虑到的点总结 前言 大家好久不见&…

【CSS Tricks】如何做一个粒子效果的logo

效果展示 代码展示 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>粒子效果Logo</title>…

VUE面试题(单页应用及其首屏加载速度慢的问题)

目录 一、单页应用 1.概念 2.单页面应用的优缺点 二、多页面应用&#xff1a; 1.概念 2.区别 三、SPA的实现 1.原理 2.方式&#xff1a; 3.Hash与History模式有什么区别 四、首屏加载速度慢如何优化 1.什么是首屏加载&#xff1f; 2.首屏加载慢的原因 3.如何解决…

OpenCV特征检测(2)边缘检测函数Canny()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 使用 Canny 算法 48在图像中查找边缘。 该函数使用 Canny 算法在输入图像中查找边缘&#xff0c;并在输出地图 edges 中标记它们。在 threshold1…

【0332】Postgres内核 start background worker(s)

0. 相关联文件 postmaster.cilist.h1. 启动 background worker(s) Postgres内核在 PostmasterMain() 函数中初始化 postmaster 守护进程时候,通过 maybe_start_bgworkers() 函数开始尝试启动 background worker(s)。 若时机恰当,则启动 background worker(s)。 作为一种附带…

基于云的补丁管理

什么是云补丁 云补丁或基于云的补丁管理是指扫描和检测缺失补丁、测试补丁并将它们部署到所需系统的过程&#xff0c;所有这些都通过基于云的控制台或软件完成。虽然补丁管理工作流程通常保持不变&#xff0c;但基于云的补丁管理的主要区别在于&#xff0c;整个过程仅通过基于…

iOS 18 适配 Xcode 16 问题

在适配 iOS 18 xcode 16时遇到的问题&#xff0c;记录一下。 1. 使用xcode 16 iOS 18 运行App时遇到&#xff0c;APP 的icon 出现空白现象。 原先APP icon 设置方案。 暂时解决方案&#xff1a; 2、

Python 低层多线程接口_thread的用法

_thread是python标准库中的一个低层多线程API&#xff0c;可以在进程中启动线程来处理任务&#xff0c;并且提供了简单的锁机制来控制共享资源的同步访问。本文就_thread模块的用法和特性做个简单的演示。 文章目录 一、进程和线程的区别二、_thread模块的用法2.1 派生线程2.2…

Percona发布开源DBaaS平台;阿里云RDS发布全球多活数据库(GAD);Redshift支持自然语言生成SQL

重要更新 1. 云栖大会于本周四/五在杭州举行&#xff0c;周五上午云栖主论坛阿里云数据库负责人李飞飞将发表《从数据到智能&#xff1a;DataAI驱动的云原生数据库》演讲&#xff0c;另外&#xff0c;还有多场次的数据库专场&#xff0c;感兴趣的可以现场或在线观看&#xff1a…