进程锁介绍
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#define M 3
int tickets = 20;
pthread_mutex_t lock; //互斥锁
// 线程函数,用于售票
void *fun(void *args) {
while (1) {
pthread_mutex_lock(&lock); // 获取互斥锁
if (tickets > 0) {
sleep(1);
tickets--;
printf("the remaining tickets are %d\n", tickets);
} else {
pthread_mutex_unlock(&lock); // 释放互斥锁
break;
}
pthread_mutex_unlock(&lock); // 释放互斥锁
}
return NULL;
}
int main() {
pthread_t id[M];
int i;
pthread_mutex_init(&lock, NULL); // 初始化互斥锁
for (i = 0; i < M; i++) {
pthread_create(&id[i], NULL, fun, NULL);
} //创建线程
for (i = 0; i < M; i++) {
pthread_join(id[i], NULL);
} //等待线程结束
pthread_mutex_destroy(&lock); // 销毁互斥锁
return 0;
}
pthread_mutex_init(&lock, NULL); // 初始化互斥锁
pthread_mutex_destroy(&lock); // 销毁互斥锁
pthread_mutex_unlock(&lock); // 释放互斥锁
pthread_mutex_lock(&lock); // 获取互斥锁
进程之间的通信
- 普通方式
内存映射(mmap 技术) 映射到同一物理空间
main 函数列表 环境列表 (只对与夫进程传递给子进程)- 信号 kill -sig pid 给进程发送信号
给进程发送一个sig 信号- 上古方式 pipe 管道
4.IPC 进程间通信
共享内存 sharemomery
消息列列 messagequeue
信号量集 semaphore set
5 网络通信
socket
socket` 是一个通信端点,它为两个网络实体之间提供了一个通信信道 | socket 编程是实现网络通信的一种常用方法。socket 允许程序通过网络进行数据的发送和接收, |
---|---|
创建 socket | int sockfd = socket(AF_INET, SOCK_STREAM, 0); |
AF_INET 表示使用 IPv4 地址。 | SOCK_STREAM 表示使用面向连接的、可靠的字节流通信(如 TCP)。 |
一旦创建了 socket,通常你会想要将其绑定到一个特定的网络接口和端口上 | |
struct sockaddr_in server_addr; | |
memset(&server_addr, 0, sizeof(server_addr)); | |
server_addr.sin_family = AF_INET; | |
server_addr.sin_port = htons(port); | |
server_addr.sin_addr.s_addr = INADDR_ANY; | |
bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); | |
监听连对于服务器端的 socket,你需要监听传入的连接请求 | listen(sockfd, SOMAXCONN); |
接受连接*:当服务器监听到连接请求时,它可以接受连接,从而创建一个新的 socket 用于与客户端通信。 | |
struct sockaddr_in client_addr; | |
socklen_t client_len = sizeof(client_addr); | |
int clientfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len); | |
发送和接收数据:一旦建立了连接,就可以使用 send 和 recv (或 write 和 read )函数来发送和接收数据。 | send(clientfd, message, strlen(message), 0); 或者 write(clientfd, message, strlen(message)); 或者 ssize_t received = recv(clientfd, buffer, sizeof(buffer) - 1, 0); 或者 ssize_t received = read(clientfd, buffer, sizeof(buffer) - 1); |
数据传输完成后,应该关闭 socket 连接。 | close(sockfd); |
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
volatile sig_atomic_t running = 1; // 用于标识进程是否运行的变量
void sig_handler(int signum) { // 信号处理函数
running = 0;
}
int main(){
pid_t id = fork(); // 创建子进程
if(id == -1){
perror("fork");
return -1; // 处理出错情况
}
if(id > 0){ // 在父进程中执行的代码
return 0; // 父进程结束
}
printf("%d", getpid()); // 打印子进程的进程号
setsid(); // 设置新的会话领导
chdir("/"); // 更改进程的工作目录为根目录
umask(0); // 设置文件创建的掩码
int i;
for (i=0; i<3; i++){ // 循环次数为3
close(i); // 关闭标准输入、输出、错误流
signal(SIGINT, sig_handler); // 如果进程收到SIGINT信号,则调用sig_handler函数
while(running){ // 当进程运行时执行循环
int fd = open("test.txt", O_RDWR|O_CREAT, 0666); // 打开或创建文件
if(fd == -1){
perror("open"); // 处理打开文件出错情况
return -1; // 返回错误
}
char *p = "这是一个守护进程";
write(fd, p, strlen(p)); // 写入数据到文件
close(fd); // 关闭文件
sleep(3); // 休眠3秒
}
}
return 0; // 子进程结束
}
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
static void sig_usr(int);
int main(void)
{
// 设置信号处理函数,捕获SIGUSR1信号 sig_usr是名字,函数名随意
if (signal(SIGUSR1, sig_usr) == SIG_ERR) {
printf("无法捕获SIGUSR1");
exit(-1);
}
// 设置信号处理函数,捕获SIGUSR2信号
if (signal(SIGUSR2, sig_usr) == SIG_ERR) {
printf("无法捕获SIGUSR2");
exit(-1);
}
// 循环等待信号
for ( ; ; )
pause();
}
// 信号处理函数
static void sig_usr(int signo)
{
// 判断接收到的信号类型并作出相应处理
if (signo == SIGUSR1) {
printf("接收到SIGUSR1\n");
} else if (signo == SIGUSR2) {
printf("接收到SIGUSR2");
} else {
printf("接收到信号 %d\n", signo);
}
}
无论可靠还是不可靠信号都可以
sigqueue
/sigacton
函数发送和安装信号来源 硬件异常 除了 0 无效内存等
硬件异常通常被驱动检测到并通知内核
系统内核对这些信号进行进程递送相应的信号
软件异常则通过sigqueue
/sigacton
函数产生信号信号处理
可以 忽略 终止进程
终止进程产生core文件
捕获并处理 当信号发生时 内核会调用一个事先注册好的用户信号处理函数