如何在Android上设置应用对WiFi和移动网络的上网权限控制
iptables是Linux的一个命令行工具,通过设置一些规则可以直接把指定uid或网址的数据包从ip层过滤掉,从而实现网络防火墙的功能,这部分已经比较成熟,android或厂商只是对iptables命令进行了封装,让android app可以通过iptables命令进行防火墙设置,iptables有很多复杂的功能,我们主要看看怎么设置白名单只让指定的uid app可以联网和设置黑名单让指定的uid app不能联网,我们通过代码流程来分析,代码是mtk android8.1。
root后通过adb shell iptables -L可以查看当前的规则列表,Chain INPUT,OUTPUT就是控制数据包的输入输出,没做任何设置前应该张下面这个样子,Chain OUTPUT的数据包通过Chain fw_OUTPUT控制, Chain fw_OUTPUT的规则是空的,所以当前对网络不做限制。
$ adb shell iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
bw_INPUT all -- anywhere anywhere
fw_INPUT all -- anywhere anywhere
Chain FORWARD (policy ACCEPT)
target prot opt source destination
oem_fwd all -- anywhere anywhere
fw_FORWARD all -- anywhere anywhere
bw_FORWARD all -- anywhere anywhere
natctrl_FORWARD all -- anywhere anywhere
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
oem_out all -- anywhere anywhere
firewall all -- anywhere anywhere
fw_OUTPUT all -- anywhere anywhere
st_OUTPUT all -- anywhere anywhere
bw_OUTPUT all -- anywhere anywhere
Chain fw_FORWARD (1 references)
target prot opt source destination
Chain fw_INPUT (1 references)
target prot opt source destination
Chain fw_OUTPUT (1 references)
target prot opt source destination
//android app层
INetworkManagementService networkService = INetworkManagementService.Stub.asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
public void WhiteListMode(INetworkManagementService networkService) {
FireWallUtils.setFirewallEnabled(networkService, true);//白名单模式
FireWallUtils.setFirewallUidRule(networkService, 0, 1016, 0); //重置vpn uid rule
FireWallUtils.setFirewallUidRule(networkService, 0, 1016, 1); //设置vpn uid为白名单
FireWallUtils.setFirewallUidRule(networkService, 0, 0, 0); //重置root uid rule
FireWallUtils.setFirewallUidRule(networkService, 0, 0, 1); //设置root uid为白名单
// List<String> whitelistApp = new ArrayList<>();
// whitelistApp.add("com.nuts.extremspeedup");
// PackageManager pm = mContext.getPackageManager();
// for (String pkgName : whitelistApp) {
// int uid = FireWallUtils.getUidFromPackageName(pm, pkgName);
// if (uid > 0) {
// FireWallUtils.setFirewallUidRule(networkService, 0, uid, 0); //重置 uid rule
// FireWallUtils.setFirewallUidRule(networkService, 0, uid, 1); //设置白名单
// }
// }
}
public void BlackListMode(INetworkManagementService networkService) {
FireWallUtils.setFirewallEnabled(networkService, false); //黑名单模式
List<String> whitelistApp = new ArrayList<>();
whitelistApp.add("com.iflytek.inputmethod");//com.iflytek.inputmethod
PackageManager pm = getPackageManager();
for (String pkgName : whitelistApp) {
int uid = FireWallUtils.getUidFromPackageName(pm, pkgName); //获取app的uid
if (uid > 0) {
FireWallUtils.setFirewallUidRule(networkService, 0, uid, 0); //重置 uid rule
FireWallUtils.setFirewallUidRule(networkService, 0, uid, 2); //设置uid为黑名单
}
}
}
public void DisableMobileMode(INetworkManagementService networkService) {
FireWallUtils.setFirewallEnabled(networkService, false); //黑名单模式
List<String> whitelistApp = new ArrayList<>();
whitelistApp.add("com.iflytek.inputmethod");//com.iflytek.inputmethod
PackageManager pm = getPackageManager();
for (String pkgName : whitelistApp) {
int uid = FireWallUtils.getUidFromPackageName(pm, pkgName);
if (uid > 0) {
FireWallUtils.setFirewallUidChainRule(networkService, uid, 0, false); //(networkType == 1) ? WIFI : MOBILE; , 禁止此uid连mobile
}
}
}
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.INetworkManagementService;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class FireWallUtils {
private static final String TAG = "FireWallUtils";
public static void setFirewallEnabled(INetworkManagementService networkService, boolean enable) {
try {
networkService.setFirewallEnabled(enable);
} catch (RemoteException e) {
Log.e(TAG, "setFirewallEnabled RemoteException e:" + Log.getStackTraceString(e));
} catch (Exception e) {
Log.e(TAG, "setFirewallEnabled Exception e:" + Log.getStackTraceString(e));
}
}
public static void setFirewallUidRule(INetworkManagementService networkService, int chain, int uid, int rule) {
try {
networkService.setFirewallUidRule(chain, uid, rule);
} catch (RemoteException e) {
Log.e(TAG, "setFirewallUidRule RemoteException e:" + Log.getStackTraceString(e));
} catch (Exception e) {
Log.e(TAG, "setFirewallUidRule Exception e:" + Log.getStackTraceString(e));
}
}
public static int getUidFromPackageName(PackageManager pm, String pkgName) {
try {
ApplicationInfo appInfo = pm.getApplicationInfo(pkgName, PackageManager.MATCH_ALL);
if (appInfo != null) {
return appInfo.uid;
}
} catch (PackageManager.NameNotFoundException e) {
//e.printStackTrace();
}
return -1;
}
}
public static void setFirewallUidChainRule(INetworkManagementService networkService, int uid, int networkType, boolean allow) {
try {
networkService.setFirewallUidChainRule(uid, networkType, allow);
} catch (RemoteException e) {
Log.d(TAG, "setFirewallEnabled RemoteException e:" + Log.getStackTraceString(e));
} catch (Exception e) {
Log.d(TAG, "setFirewallEnabled Exception e:" + Log.getStackTraceString(e));
}
}
public static void clearFirewallChain(INetworkManagementService networkService, String chain) {
try {
networkService.clearFirewallChain(chain);
} catch (RemoteException e) {
Log.d(TAG, "setFirewallEnabled RemoteException e:" + Log.getStackTraceString(e));
} catch (Exception e) {
Log.d(TAG, "setFirewallEnabled Exception e:" + Log.getStackTraceString(e));
}
}
frameworks/base/core/java/android/os/INetworkManagementService.aidl
void setFirewallEnabled(boolean enabled);
boolean isFirewallEnabled();
void setFirewallInterfaceRule(String iface, boolean allow);
void setFirewallUidRule(int chain, int uid, int rule);
void setFirewallUidRules(int chain, in int[] uids, in int[] rules);
void setFirewallChainEnabled(int chain, boolean enable);
/**
* agold
* Cnfigure firewall rule by uid and chain
* @hide
*/
void setFirewallUidChainRule(int uid, int networkType, boolean allow);
/** @} */
/**
* agold
* Delete all rules in chain or all chains
* @hide
*/
void clearFirewallChain(String chain);
frameworks/base/services/core/java/com/android/server/NetworkManagementService.java
@Override
public void setFirewallEnabled(boolean enabled) {
enforceSystemUid();
try {
mConnector.execute("firewall", "enable", enabled ? "whitelist" : "blacklist");
mFirewallEnabled = enabled;
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
@Override
public void setFirewallUidRule(int chain, int uid, int rule) {
enforceSystemUid();
synchronized (mQuotaLock) {
setFirewallUidRuleLocked(chain, uid, rule);
}
}
private void setFirewallUidRuleLocked(int chain, int uid, int rule) {
if (updateFirewallUidRuleLocked(chain, uid, rule)) {
try {
mConnector.execute("firewall", "set_uid_rule", getFirewallChainName(chain), uid,
getFirewallRuleName(chain, rule));
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
}
/**
* agold
* @Configure firewall rule by uid and chain
* @hide
*/
public void setFirewallUidChainRule(int uid, int networkType, boolean allow) {
android.util.Log.i("linyuan_NMS", "setFirewallUidChainRule uid = " + uid + ", networkType = " + networkType + ", allow = " + allow);
//enforceSystemUid();
final String MOBILE = "mobile";
final String WIFI = "wifi";
final String rule = allow ? "allow" : "deny";
final String chain = (networkType == 1) ? WIFI : MOBILE;
try {
mConnector.execute("firewall", "set_uid_fw_rule", uid, chain, rule);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
/**
* agold
* @Configure firewall rule by uid and chain
* @hide
*/
public void clearFirewallChain(String chain) {
//enforceSystemUid();
try {
mConnector.execute("firewall", "clear_fw_chain", chain);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
system/netd/server/CommandListener.cpp
int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc,
char **argv) {
if (argc < 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing command", false);
return 0;
}
if (!strcmp(argv[1], "enable")) {
if (argc != 3) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: firewall enable <whitelist|blacklist>", false);
return 0;
}
FirewallType firewallType = parseFirewallType(argv[2]);
int res = gCtls->firewallCtrl.enableFirewall(firewallType);
return sendGenericOkFail(cli, res);
}
if (!strcmp(argv[1], "disable")) {
int res = gCtls->firewallCtrl.disableFirewall();
return sendGenericOkFail(cli, res);
}
if (!strcmp(argv[1], "is_enabled")) {
int res = gCtls->firewallCtrl.isFirewallEnabled();
return sendGenericOkFail(cli, res);
}
if (!strcmp(argv[1], "set_uid_rule")) {
if (argc != 5) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: firewall set_uid_rule <dozable|standby|none> <1000> <allow|deny>",
false);
return 0;
}
ChildChain childChain = parseChildChain(argv[2]);
if (childChain == INVALID_CHAIN) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Invalid chain name. Valid names are: <dozable|standby|none>",
false);
return 0;
}
int uid = atoi(argv[3]);
FirewallRule rule = parseRule(argv[4]);
int res = gCtls->firewallCtrl.setUidRule(childChain, uid, rule);
return sendGenericOkFail(cli, res);
}
//agold start
if (!strcmp(argv[1], "set_uid_fw_rule")) {
ALOGD("set_uid_fw_rule");
if (argc != 5) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: firewall set_uid_fw_rule <uid> <mobile|wifi> <allow|deny>",
false);
return 0;
}
int uid = atoi(argv[2]);
FirewallChinaRule chain = parseChain(argv[3]);
FirewallRule rule = parseRule(argv[4]);
int res = gCtls->firewallCtrl.setUidFwRule(uid, chain, rule);
return sendGenericOkFail(cli, res);
}
if (!strcmp(argv[1], "clear_fw_chain")) {
if (argc != 3) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: firewall clear_fw_chain <chain>",
false);
return 0;
}
FirewallChinaRule chain = parseChain(argv[2]);
int res = gCtls->firewallCtrl.clearFwChain(chain);
return sendGenericOkFail(cli, res);
}
//agold end
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown command", false);
return 0;
}
system/netd/server/FirewallController.cpp
int FirewallController::enableFirewall(FirewallType ftype) {
int res = 0;
if (mFirewallType != ftype) {
// flush any existing rules
disableFirewall();
if (ftype == WHITELIST) {
// create default rule to drop all traffic
std::string command =
"*filter\n"
"-A fw_INPUT -j DROP\n"
"-A fw_OUTPUT -j REJECT\n"
"-A fw_FORWARD -j REJECT\n"
"COMMIT\n";
res = execIptablesRestore(V4V6, command.c_str());
}
// Set this after calling disableFirewall(), since it defaults to WHITELIST there
mFirewallType = ftype;
}
return res;
}
int FirewallController::disableFirewall(void) {
mFirewallType = WHITELIST;
mIfaceRules.clear();
// flush any existing rules
std::string command =
"*filter\n"
":fw_INPUT -\n"
":fw_OUTPUT -\n"
":fw_FORWARD -\n"
"COMMIT\n";
return execIptablesRestore(V4V6, command.c_str());
}
int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) {
const char* op;
const char* target;
FirewallType firewallType = getFirewallType(chain);
if (firewallType == WHITELIST) {
target = "RETURN";
// When adding, insert RETURN rules at the front, before the catch-all DROP at the end.
op = (rule == ALLOW)? "-I" : "-D";
} else { // BLACKLIST mode
target = "DROP";
// When adding, append DROP rules at the end, after the RETURN rule that matches TCP RSTs.
op = (rule == DENY)? "-A" : "-D";
}
std::vector<std::string> chainNames;
switch(chain) {
case DOZABLE:
chainNames = { LOCAL_DOZABLE };
break;
case STANDBY:
chainNames = { LOCAL_STANDBY };
break;
case POWERSAVE:
chainNames = { LOCAL_POWERSAVE };
break;
case NONE:
chainNames = { LOCAL_INPUT, LOCAL_OUTPUT };
break;
default:
ALOGW("Unknown child chain: %d", chain);
return -1;
}
std::string command = "*filter\n";
for (std::string chainName : chainNames) {
StringAppendF(&command, "%s %s -m owner --uid-owner %d -j %s\n",
op, chainName.c_str(), uid, target);
}
StringAppendF(&command, "COMMIT\n");
return execIptablesRestore(V4V6, command);
}
//agold start
const char* FirewallController::FIREWALL = "firewall";
const char* FirewallController::FIREWALL_MOBILE = "mobile";
const char* FirewallController::FIREWALL_WIFI = "wifi";
//agold end
int FirewallController::setupIptablesHooks(void) {
int res = 0;
//agold start
std::string command = "*filter\n";
StringAppendF(&command, "-F firewall \n");
StringAppendF(&command, "-A firewall -o ppp+ -j mobile\n");
StringAppendF(&command, "-A firewall -o ccmni+ -j mobile\n");
StringAppendF(&command, "-A firewall -o ccemni+ -j mobile\n");
StringAppendF(&command, "-A firewall -o usb+ -j mobile\n");
StringAppendF(&command, "-A firewall -o cc2mni+ -j mobile\n");
StringAppendF(&command, "-A firewall -o wlan+ -j wifi\n");
StringAppendF(&command, "COMMIT\n");
res |= execIptablesRestore(V4V6, command.c_str());
//agold end
res |= createChain(LOCAL_DOZABLE, getFirewallType(DOZABLE));
res |= createChain(LOCAL_STANDBY, getFirewallType(STANDBY));
res |= createChain(LOCAL_POWERSAVE, getFirewallType(POWERSAVE));
return res;
}
//agold start
int FirewallController::setUidFwRule(int uid, FirewallChinaRule chain, FirewallRule rule) {
ALOGD("setUidFwRule");
std::string command = "*filter\n";
char uidStr[16];
const char* op;
const char* fwChain;
sprintf(uidStr, "%d", uid);
if (rule == DENY) {
op = "-I";
} else {
op = "-D";
}
if(chain == MOBILE) {
fwChain = "mobile";
}else{
fwChain = "wifi";
}
/*
if(chain == MOBILE) {
if(rule == ALLOW)
blacklistUsers.insert(uid);
else
blacklistUsers.erase(uid);
}
*/
ALOGD("setUidFwRule op = %s, chain = %s, uid = %s", op, fwChain, uidStr);
StringAppendF(&command, "%s %s -m owner --uid-owner %s -j REJECT\n", op, fwChain, uidStr);
StringAppendF(&command, "COMMIT\n");
return execIptablesRestore(V4V6, command.c_str());
}
int FirewallController::clearFwChain(FirewallChinaRule chain) {
std::string command = "*filter\n";
const char* fwChain;
if(chain == MOBILE) {
fwChain = "mobile";
}else{
fwChain = "wifi";
}
StringAppendF(&command, "-F %s \n", fwChain);
StringAppendF(&command, "COMMIT\n");
return execIptablesRestore(V4V6, command.c_str());
}
//agold end
运行到最后就是调用iptables命令去添加或删除一些规则,如果没有接口可以参考上面的接口增加,熟悉下iptables的规则就知道怎么添加了。
WhiteListMode函数其实就调的下面的iptables命令
adb shell iptables -A fw_INPUT -j DROP ; // -A表示要添加规则到fw_INPUT链, -j DROP表示添加丢弃规则,丢弃所有输入包
adb shell iptables -A fw_OUTPUT -j REJECT // -A表示要添加规则到fw_OUTPU链, -j REJECT表示添加拒绝规则,拒绝所有输出包
adb shell iptables -A fw_FORWARD -j REJECT
adb shell iptables -I fw_OUTPUT -m owner --uid-owner 1016 -j ACCEPT // -I表示插入一条规则到fw_OUTPUT链,是一条白名单规则,允许uid 1016的数据包通过
adb shell iptables -I fw_INPUT -m owner --uid-owner 1016 -j ACCEPT
调用后iptables张下面这个样子,Chain OUTPUT的数据包通过Chain fw_OUTPUT控制, Chain fw_OUTPUT的规则是reject所有数据包,只允许uid 1016 vpn。
$ adb shell iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
bw_INPUT all -- anywhere anywhere
fw_INPUT all -- anywhere anywhere
Chain FORWARD (policy ACCEPT)
target prot opt source destination
oem_fwd all -- anywhere anywhere
fw_FORWARD all -- anywhere anywhere
bw_FORWARD all -- anywhere anywhere
natctrl_FORWARD all -- anywhere anywhere
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
oem_out all -- anywhere anywhere
firewall all -- anywhere anywhere
fw_OUTPUT all -- anywhere anywhere
st_OUTPUT all -- anywhere anywhere
bw_OUTPUT all -- anywhere anywhere
Chain fw_FORWARD (1 references)
target prot opt source destination
REJECT all -- anywhere anywhere reject-with icmp-port-unreachable
Chain fw_INPUT (1 references)
target prot opt source destination
RETURN all -- anywhere anywhere owner UID match vpn
DROP all -- anywhere anywhere
Chain fw_OUTPUT (1 references)
target prot opt source destination
RETURN all -- anywhere anywhere owner UID match vpn
REJECT all -- anywhere anywhere reject-with icmp-port-unreachable
BlackListMode函数其实就调的下面的iptables命令
adb shell iptables -F fw_INPUT
adb shell iptables -F fw_OUTPUT //清除fw_OUTPUT链上所有规则,允许所有数据包通过
adb shell iptables -D fw_INPUT -m owner --uid-owner 10086 -j DROP // -D表示删除一条规则
adb shell iptables -I fw_OUTPUT -m owner --uid-owner 10086 -j DROP // -I表示插入一条规则到fw_OUTPUT链,是一条黑名单规则,只禁止uid 10086的数据包通过
adb shell iptables -I fw_INPUT -m owner --uid-owner 10086 -j DROP
调用后iptables张下面这个样子,Chain OUTPUT的数据包通过Chain fw_OUTPUT控制, Chain fw_OUTPUT的规则是只丢弃
uid 为u11_a83(10086)的数据包,就是前面设置的app的uid 10086
~$adb shell iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
bw_INPUT all -- anywhere anywhere
fw_INPUT all -- anywhere anywhere
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
oem_out all -- anywhere anywhere
firewall all -- anywhere anywhere
fw_OUTPUT all -- anywhere anywhere
st_OUTPUT all -- anywhere anywhere
bw_OUTPUT all -- anywhere anywhere
Chain fw_INPUT (1 references)
target prot opt source destination
DROP all -- anywhere anywhere owner UID match u11_a83
Chain fw_OUTPUT (1 references)
target prot opt source destination
DROP all -- anywhere anywhere owner UID match u11_a83
如果需要单独控制wifi或移动数据,看DisableMobileMode里面setFirewallUidChainRule接口,这部分是厂商加的,如果没有可以按上面的内容进行添加,参考agold关键字,对照adb shell iptables -L的输出调试问题就不大了。
调用后iptables张下面这个样子,Chain OUTPUT的数据包通过Chain fw_OUTPUT 和Chain firewall控制, Chain fw_OUTPUT的规则为空就是不限制,Chain firewall通过mobile和wifi链控制wifi/mobile, Chain wifi为空,Chain mobile为reject owner UID match u0_a79
~$ adb shell iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
bw_INPUT all -- anywhere anywhere
fw_INPUT all -- anywhere anywhere
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
oem_out all -- anywhere anywhere
firewall all -- anywhere anywhere
fw_OUTPUT all -- anywhere anywhere
st_OUTPUT all -- anywhere anywhere
bw_OUTPUT all -- anywhere anywhere
Chain firewall (1 references)
target prot opt source destination
mobile all -- anywhere anywhere
mobile all -- anywhere anywhere
mobile all -- anywhere anywhere
mobile all -- anywhere anywhere
mobile all -- anywhere anywhere
wifi all -- anywhere anywhere
Chain fw_INPUT (1 references)
target prot opt source destination
Chain fw_OUTPUT (1 references)
target prot opt source destination
Chain mobile (5 references)
target prot opt source destination
REJECT all -- anywhere anywhere owner UID match u0_a79 reject-with icmp-port-unreachable
Chain wifi (1 references)
target prot opt source destination
iptables命令可以参考 https://blog.****.net/l1028386804/article/details/47356011
https://blog.****.net/reyleon/article/details/12976341
推荐阅读
-
windows下进程间通信的(13种方法)-摘 要 本文讨论了进程间通信与应用程序间通信的含义及相应的实现技术,并对这些技术的原理、特性等进行了深入的分析和比较。 ---- 关键词 信号 管道 消息队列 共享存储段 信号灯 远程过程调用 Socket套接字 MQSeries 1 引言 ---- 进程间通信的主要目的是实现同一计算机系统内部的相互协作的进程之间的数据共享与信息交换,由于这些进程处于同一软件和硬件环境下,利用操作系统提供的的编程接口,用户可以方便地在程序中实现这种通信;应用程序间通信的主要目的是实现不同计算机系统中的相互协作的应用程序之间的数据共享与信息交换,由于应用程序分别运行在不同计算机系统中,它们之间要通过网络之间的协议才能实现数据共享与信息交换。进程间通信和应用程序间通信及相应的实现技术有许多相同之处,也各有自己的特色。即使是同一类型的通信也有多种的实现方法,以适应不同情况的需要。 ---- 为了充分认识和掌握这两种通信及相应的实现技术,本文将就以下几个方面对这两种通信进行深入的讨论:问题的由来、解决问题的策略和方法、每种方法的工作原理和实现、每种实现方法的特点和适用的范围等。 2 进程间的通信及其实现技术 ---- 用户提交给计算机的任务最终都是通过一个个的进程来完成的。在一组并发进程中的任何两个进程之间,如果都不存在公共变量,则称该组进程为不相交的。在不相交的进程组中,每个进程都独立于其它进程,它的运行环境与顺序程序一样,而且它的运行环境也不为别的进程所改变。运行的结果是确定的,不会发生与时间相关的错误。 ---- 但是,在实际中,并发进程的各个进程之间并不是完全互相独立的,它们之间往往存在着相互制约的关系。进程之间的相互制约关系表现为两种方式: ---- (1) 间接相互制约:共享CPU ---- (2) 直接相互制约:竞争和协作 ---- 竞争——进程对共享资源的竞争。为保证进程互斥地访问共享资源,各进程必须互斥地进入各自的临界段。 ---- 协作——进程之间交换数据。为完成一个共同任务而同时运行的一组进程称为同组进程,它们之间必须交换数据,以达到协作完成任务的目的,交换数据可以通知对方可以做某事或者委托对方做某事。 ---- 共享CPU问题由操作系统的进程调度来实现,进程间的竞争和协作由进程间的通信来完成。进程间的通信一般由操作系统提供编程接口,由程序员在程序中实现。UNIX在这个方面可以说最具特色,它提供了一整套进程间的数据共享与信息交换的处理方法——进程通信机制(IPC)。因此,我们就以UNIX为例来分析进程间通信的各种实现技术。 ---- 在UNIX中,文件(File)、信号(Signal)、无名管道(Unnamed Pipes)、有名管道(FIFOs)是传统IPC功能;新的IPC功能包括消息队列(Message queues)、共享存储段(Shared memory segment)和信号灯(Semapores)。 ---- (1) 信号 ---- 信号机制是UNIX为进程中断处理而设置的。它只是一组预定义的值,因此不能用于信息交换,仅用于进程中断控制。例如在发生浮点错、非法内存访问、执行无效指令、某些按键(如ctrl-c、del等)等都会产生一个信号,操作系统就会调用有关的系统调用或用户定义的处理过程来处理。 ---- 信号处理的系统调用是signal,调用形式是: ---- signal(signalno,action) ---- 其中,signalno是规定信号编号的值,action指明当特定的信号发生时所执行的动作。 ---- (2) 无名管道和有名管道 ---- 无名管道实际上是内存中的一个临时存储区,它由系统安全控制,并且独立于创建它的进程的内存区。管道对数据采用先进先出方式管理,并严格按顺序操作,例如不能对管道进行搜索,管道中的信息只能读一次。 ---- 无名管道只能用于两个相互协作的进程之间的通信,并且访问无名管道的进程必须有共同的祖先。 ---- 系统提供了许多标准管道库函数,如: pipe——打开一个可以读写的管道; close——关闭相应的管道; read——从管道中读取字符; write——向管道中写入字符; ---- 有名管道的操作和无名管道类似,不同的地方在于使用有名管道的进程不需要具有共同的祖先,其它进程,只要知道该管道的名字,就可以访问它。管道非常适合进程之间快速交换信息。 ---- (3) 消息队列(MQ) ---- 消息队列是内存中独立于生成它的进程的一段存储区,一旦创建消息队列,任何进程,只要具有正确的的访问权限,都可以访问消息队列,消息队列非常适合于在进程间交换短信息。 ---- 消息队列的每条消息由类型编号来分类,这样接收进程可以选择读取特定的消息类型——这一点与管道不同。消息队列在创建后将一直存在,直到使用msgctl系统调用或iqcrm -q命令删除它为止。 ---- 系统提供了许多有关创建、使用和管理消息队列的系统调用,如: ---- int msgget(key,flag)——创建一个具有flag权限的MQ及其相应的结构,并返回一个唯一的正整数msqid(MQ的标识符); ---- int msgsnd(msqid,msgp,msgsz,msgtyp,flag)——向队列中发送信息; ---- int msgrcv(msqid,cmd,buf)——从队列中接收信息; ---- int msgctl(msqid,cmd,buf)——对MQ的控制操作; ---- (4) 共享存储段(SM) ---- 共享存储段是主存的一部分,它由一个或多个独立的进程共享。各进程的数据段与共享存储段相关联,对每个进程来说,共享存储段有不同的虚拟地址。系统提供的有关SM的系统调用有: ---- int shmget(key,size,flag)——创建大小为size的SM段,其相应的数据结构名为key,并返回共享内存区的标识符shmid; ---- char shmat(shmid,address,flag)——将当前进程数据段的地址赋给shmget所返回的名为shmid的SM段; ---- int shmdr(address)——从进程地址空间删除SM段; ---- int shmctl (shmid,cmd,buf)——对SM的控制操作; ---- SM的大小只受主存限制,SM段的访问及进程间的信息交换可以通过同步读写来完成。同步通常由信号灯来实现。SM非常适合进程之间大量数据的共享。 ---- (5) 信号灯 ---- 在UNIX中,信号灯是一组进程共享的数据结构,当几个进程竞争同一资源时(文件、共享内存或消息队列等),它们的操作便由信号灯来同步,以防止互相干扰。 ---- 信号灯保证了某一时刻只有一个进程访问某一临界资源,所有请求该资源的其它进程都将被挂起,一旦该资源得到释放,系统才允许其它进程访问该资源。信号灯通常配对使用,以便实现资源的加锁和解锁。 ---- 进程间通信的实现技术的特点是:操作系统提供实现机制和编程接口,由用户在程序中实现,保证进程间可以进行快速的信息交换和大量数据的共享。但是,上述方式主要适合在同一台计算机系统内部的进程之间的通信。 3 应用程序间的通信及其实现技术 ---- 同进程之间的相互制约一样,不同的应用程序之间也存在竞争和协作的关系。UNIX操作系统也提供一些可用于应用程序之间实现数据共享与信息交换的编程接口,程序员可以通过自己编程来实现。如远程过程调用和基于TCP/IP协议的套接字(Socket)编程。但是,相对普通程序员来说,它们涉及的技术比较深,编程也比较复杂,实现起来困难较大。 ---- 于是,一种新的技术应运而生——通过将有关通信的细节完全掩盖在某个独立软件内部,即底层的通讯工作和相应的维护管理工作由该软件内部来实现,用户只需要将通信任务提交给该软件去完成,而不必理会它的具体工作过程——这就是所谓的中间件技术。 ---- 我们在这里分别讨论这三种常用的应用程序间通信的实现技术——远程过程调用、会话编程技术和MQSeries消息队列技术。其中远程过程调用和会话编程属于比较低级的方式,程序员参与的程度较深,而MQSeries消息队列则属于比较高级的方式,即中间件方式,程序员参与的程度较浅。 ---- 4.1 远程过程调用(RPC)
-
如何在Android上设置应用对WiFi和移动网络的上网权限控制