Advanced Programming in UNIX Environment Episode 95

本文介绍了一种自定义锁的实现方式,并详细探讨了POSIX信号量的使用方法。通过创建自定义锁结构,文章展示了如何利用信号量实现互斥操作。此外,还讨论了客户端-服务器模型中不同类型的进程间通信(IPC),包括管道、消息队列及套接字等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Assuming we were to create our own lock that could be locked by one thread and unlocked by another, our lock structure might look like

struct slock {
	sem_t *semp;
	char name[_POSIX_NAME_MAX];
};
#include "slock.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

struct slock * s_alloc()
{
    struct slock *sp;
    static int count;

    if((sp=malloc(sizeof(struct slock)))==NULL)
        return NULL;
        
    do
    {
        snprintf(sp->name, sizeof(sp->name), "/%ld.%d",(long)getpid(), count++);
        sp->semp=sem_open(sp->name, O_CREAT|O_EXCL, SIRWXU, 1);
    }while((sp->semp==SEM_FAILED)&&(errno==EEXIST))
    if(sp->semp==SEM_FAILED)
    {
        free(sp);
        return NULL;
    }

    sem_unlink(sp->name);
    return 0;
}

void s_free(struct slock *sp)
{
    sem_close(sp->semp);
    free(sp);
}

int s_lock(struct slock *sp)
{
    return sem_wait(sp->semp);
}

int s_trylock(struct slock *sp)
{
    return sem_trywait(sp->semp);
}

int s_unlock(struct slock *sp)
{
    return sem_post(sp->semp);
}

Mutual exclusion using a POSIX semaphore

Client–Server Proper ties

Let’s detail some of the properties of clients and servers that are affected by the various types of IPC used between them. The simplest type of relationship is to have the client fork and exec the desired server. Two half-duplex pipes can be created before the fork to allow data to be transferred in both directions. Figure 15.16 is an example of this arrangement. The server that is executed can be a set-user-ID program, giving it special privileges. Also, the server can determine the real identity of the client by looking at its real user ID. (Recall from Section 8.10 that the real user ID and real group ID don’t change across an exec.)

With this arrangement, we can build an open server. (We show an implementation of this client–server mechanism in Section 17.5.) It opens files for the client instead of the client calling the open function. This way, additional permission checking can be added, above and beyond the normal UNIX system user/group/other permissions. We assume that the server is a set-user-ID program, giving it additional permissions (root permission, perhaps). The server uses the real user ID of the client to determine whether to give it access to the requested file. This way, we can build a server that allows certain users permissions that they don’t normally have.

Multiple possibilities exist with message queues.

1.A single queue can be used between the server and all the clients, using the type field of each message to indicate the message recipient.
2.Alternatively, an individual message queue can be used for each client.

Network IPC: Sockets

Introduction

In this chapter, we look at the mechanisms that allow processes running on different computers (connected to a common network) to communicate with one another—network IPC.

In this chapter, we describe the socket network IPC interface, which can be used by processes to communicate with other processes, regardless of where they are running — on the same machine or on different machines. Indeed, this was one of the design goals of the socket interface. The same interfaces can be used for both intermachine communication and intramachine communication. Although the socket interface can be used to communicate using many different network protocols, we will restrict our discussion to the TCP/IP protocol suite in this chapter, since it is the de facto standard for communicating over the Internet.

The socket API as specified by POSIX.1 is based on the 4.4BSD socket interface. Although minor changes have been made over the years, the current socket interface closely resembles the interface when it was originally introduced in 4.2BSD in the early 1980s.

Socket Descriptors

A socket is an abstraction of a communication endpoint. Just as they would use file descriptors to access files, applications use socket descriptors to access sockets. Socket descriptors are implemented as file descriptors in the UNIX System. Indeed, many of the functions that deal with file descriptors, such as read and write, will work with a socket descriptor.

To create a socket, we call the socket function.

#include <sys/socket.h>
int socket(int domain, int type, int protocol);

Most systems define the AF_LOCAL domain also, which is an alias for AF_UNIX. The AF_UNSPEC domain is a wildcard that represents ‘‘any’’ domain. Historically, some platforms provide support for additional network protocols, such as AF_IPX for the NetWare protocol family, but domain constants for these protocols are not defined by the POSIX.1 standard.

A datagram, therefore, provides a connectionless service. A byte stream (SOCK_STREAM), in contrast, requires that, before you can exchange data, you set up a logical connection between your socket and the socket belonging to the peer with which you wish to communicate.

A datagram is a self-contained message. Sending a datagram is analogous to mailing someone a letter. You can mail many letters, but you can’t guarantee the order of delivery, and some might get lost along the way. Each letter contains the address of the recipient, making the letter independent from all the others. Each letter can even go to different recipients.

A SOCK_STREAM socket provides a byte-stream service; applications are unaware of message boundaries. This means that when we read data from a SOCK_STREAM socket, it might not return the same number of bytes written by the sender. We will eventually get everything sent to us, but it might take several function calls.

A SOCK_SEQPACKET socket is just like a SOCK_STREAM socket except that we get a message-based service instead of a byte-stream service. This means that the amount of data received from a SOCK_SEQPACKET socket is the same amount as was written. The Stream Control Transmission Protocol (SCTP) provides a sequential packet service in the Internet domain.

A SOCK_RAW socket provides a datagram interface directly to the underlying network layer (which means IP in the Internet domain). Applications are responsible for building their own protocol headers when using this interface, because the transport protocols (TCP and UDP, for example) are bypassed. Superuser privileges are required to create a raw socket to prevent malicious applications from creating packets that might bypass established security mechanisms.

### Python 中循环 Episode 的代码片段 在 Python 编程中,`for` 循环常用于遍历一系列的迭代对象,比如列表、元组或范围。当涉及到模拟多个 episode(例如,在强化学习环境中),可以使用 `range()` 函数来指定要执行的次数。 下面是一个简单的例子,展示了如何设置一个 for 循环来进行多次 episode 运行: ```python num_episodes = 10 # 定义想要运行的episode数量 for episode in range(num_episodes): # 使用range()函数创建从0到num_episodes-1的序列 print(f"Starting episode {episode + 1}") # 输出当前是第几个episode # 此处放置每轮episode的具体逻辑操作... print(f"Finished episode {episode + 1}\n") # 结束本轮后打印完成信息 ``` 在这个例子中,变量 `num_episodes` 设置了总的 episode 数目;而 `range(num_episodes)` 则会生成一个由整数组成的不可变序列,这些整数是从零开始直到 `num_episodes - 1` 。每次迭代时,都会更新 `episode` 变量,并可以在循环体内访问其值以跟踪进度或者作为参数传递给其他功能模块[^1]。 如果希望调整上述代码中的某些行为,可以根据具体需求更改 `num_episodes` 的数值大小或是向循环内部添加更多处理步骤。此外,还可以引入条件判断语句或其他控制结构使程序更加灵活多变。 #### 修改循环Episode的行为 假设现在有一个特定的任务需要每隔一定数量的 episodes 执行一次特殊动作,则可以通过增加额外的计数器以及相应的条件测试来实现这一点: ```python special_action_interval = 5 # 设定每隔多少个episodes触发一次特别行动 counter = 0 # 初始化辅助计数器 for episode in range(num_episodes): counter += 1 # 增加计数器 if counter % special_action_interval == 0: print(f"Performing special action at the end of episode {episode}") # 继续常规的episode流程... print("All episodes have been completed.") ``` 这里增加了两个新的元素:一个是用来记录已经过了多少个连续 episodes 的计数器 `counter`; 另外则是每当到达预设间隔(`special_action_interval`)就采取措施的一段逻辑分支。这样就可以轻松地扩展基础框架的功能而不改变原有核心部分的工作方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值